对于区间[a,b]上连续不断且f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点。
适用于提高大列表查询速度,对于包含n个元素的列表,时间复杂度为:O(log2n)。
列表必须有序,并且当列表中存在多个相同元素时并非返回列表中第一次出现元素,不同实现算法获取到元素索引也不一定相同。
代码实现1
/// <summary>
/// 递归实现元素查找
/// <para>集合长度不变,左右查找边界移动。</para>
/// <para>已经做集合有序性检查,必须升序,否则返回-1</para>
/// <para>集合中存在查找元素时返回索引,否则返回-1。</para>
/// </summary>
/// <param name="list">查找集合</param>
/// <param name="t">待查找元素</param>
/// <param name="left">查找左边界</param>
/// <param name="right">查找右边界</param>
/// <returns></returns>
static int FindIndexByBisection(List<int> list, int t, int left, int right)
{
/*保证集合有序*/
if (list[left] > list[right])
return -1;
/*集合中存在的元素一定在最小值与最大值之间*/
if (t < list[left] || t > list[right])
return -1;
/*左右边界已经首次靠近,但是两边界所在元素均不等于待查找元素,直接返回*/
if (right - left == 1 && list[left] != t && list[right] != t)
return -1;
int midIndex = (left + right) / 2;
int midValue = list[midIndex];
if (t < midValue)
return FindIndexByBisection(list, t, left, midIndex);
else if (t > midValue)
return FindIndexByBisection(list, t, midIndex, right);
else
return midIndex;
}
代码实现2
/// <summary>
/// 递归实现元素查找
/// <para>集合长度变化,左右查找边界固定。</para>
/// <para>已经做集合有序性检查,必须升序,否则返回-1。</para>
/// <para>集合中存在查找元素时返回索引,否则返回-1。</para>
/// </summary>
/// <param name="list">查找集合</param>
/// <param name="t">待查找元素</param>
/// <param name="index">待查找元素索引</param>
static void FindIndexByBisection(List<int> list, int t, ref int index)
{
/*初始值处理,防止调用方法中设置了错误默认值*/
int count = list.Count;
int midIndex = count / 2;
int midValue = list[midIndex];
/*找到元素立即返回*/
if (t == midValue)
{
index += 1;
return;
}
/*未找到元素,且集合长度变为1返回-1*/
if (list.Count == 1)
{
index = -1;
return;
}
/*未找到元素,集合长度大于1,但集合无序返回-1*/
if (list[count - 2] > list[count - 1])
{
index = -1;
return;
}
List<int> midList = list.Take(midIndex).ToList();
if (t > midValue)
{
index += midIndex;//元素比集合中间位置元素大时将中间元素索引累加上去
midList = list.Skip(midIndex).ToList();
}
FindIndexByBisection(midList, t, ref index);
}
表示最糟糕情况下程序的运行时间。
O(log n) 对数时间,二分查找。
O(n) 线性时间,简单查找。
O(n * log n) 快速排序算法。
O(n*n) 选择排序算法。
O(n!) 旅行商问题。
算法的速度指的并非时间,而是操作数的增速。
谈论算法的速度时,我们说的是随着输入的增加,其运行时间将以什么样的速度增加。
大O表示法忽略常量是由于不同算法间效率相差几个数量级,而对于效率相近的算法常量有时候事关重大。
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
时间复杂度O(n*n)。
代码实现1
/// <summary>
/// 选择排序
/// <para>时间复杂度o(n^2)</para>
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
static List<int> SelectionSort(List<int> list)
{
var result = new List<int>();
for (var i = 0; i < list.Count; i++)
{
var minValue = list[i];
for (var j = i; j < list.Count; j++)
{
if (list[j] < minValue)
minValue = list[j];
}
result.Add(minValue);
}
return result;
}
如果使用循环,代码的性能可能更高;如果使用递归,程序可能更容易理解。如何选择要看什么对你来说更重要。
指什么条件下结束函数自我调用,避免无限循环;
指函数调用自己。
分而治之
策略
找出基线条件,条件尽可能简单。
不断将问题分解,直到符合基线条件。
快速排序原理
从数组中随机地选择一个元素作为基准值;
找出比基准值小以及比基准值大的元素,将数组分区;
小于基准值的元素构成的子数组;
基准值;
大于基准值的元素构成的子数组。
对两个子数组进行快速排序。
时间复杂度O(n log n)。
代码实现1
/// <summary>
/// 快速排序
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
static List<int> QuickSort(List<int> list)
{
/*基准条件:当列表为空或者只存在一个元素时一定有序,返回列表*/
if (list?.Count <= 1)
return list;
var midIndex = list.Count / 2;
var midValue = list[midIndex];//基准值
var lessList = new List<int>();//左列表
var greaterList = new List<int>();//右列表
for (int i = 1; i < list.Count; i++)
{
if (list[i] <= midValue)
lessList.Add(list[i]);
else
greaterList.Add(list[i]);
}
lessList = QuickSort(lessList);
greaterList = QuickSort(greaterList);
var result = new List<int>();
result.AddRange(lessList);
result.Add(midValue);
result.AddRange(greaterList);
return result;
}
总是将相同的输入映射到相同的索引;
将不同的输入映射到不同的索引;
知道数组大小,只返回有效索引。
散列表包含的元素数/位置总数。
给两个键分配的位置相同。
数组中冲突位置存放一个链表,而将冲突键存到链表中。可以避免冲突性能较低。
保证较低的填装因子(<=1)。
O(1)。
原文:https://www.cnblogs.com/fuxuyang/p/11144333.html