关于冒泡排序和二分查找
关于冒泡排序和二分查找的一些个人看法
1. 冒泡排序
冒牌排序为什么叫冒泡排序呢?是因为每次都会有一个较小的数不断往数组前方走(小到大排序),如同一个泡泡上升的过程,因此我们称之为冒泡排序。先贴一下代码:
void Bubble_sort()
{
for(int i = 1;i < n;i ++ )
{
for(int j = 1;j < n;j ++ )
{
if(a[j] > a[j + 1])
{
swap(a[j],a[j + 1]);
}
}
}
}
我们每次都选取数列中的第一个元素\(a[j]\)往后比较,若\(a[j+1]\)大于\(a[j]\),则交换两个数,每进行一次这样的过程,都会使得数组中的一个较大的数处于数组后方。一共进行\(n\)次这样的过程,所以会使\(n\)个数处于有序的状态,于是就可以在\(O(n^2)\)的时间内排好这一列数。由于在\(a[j]\)与\(a[j+1]\)相同时,不发生交换,所以冒泡排序还是一种稳定排序算法。
2. 二分查找(\(a\)数组为单调递增数列)
二分查找是确定一个元素在数列中的位置的算法。什么样子的数组能使用二分查找呢?不如来看看二分查找的代码:
int equal_dict(int x)
{
while(l < r)
{
int mid = (l + r) >> 1;
if(a[mid] >= x) r = mid;
else l = mid + 1;
}
return a[l];
}
先说结论:具有单调性的数组才可以进行二分查找。每次我们都有一个\(l\)和\(r\),来表示我们要查找的范围,\(mid\)为\(l\),\(r\)的中间值。如果以mid为下标的数大于了目标值\(x\),则下标为\(mid\)的数以后都是大于\(x\)的,所以\(x\)不可能位于\(mid\)右侧,所以我们就选择\(mid\)的左侧,这个区间一定是\(x\)所在的区间。小于同理。如果数列不是有序的,则不能进行缩小区间的操作,无法找到\(x\)的位置。
二分算法充分利用了元素间的次序关系,采用分治策略。由于每次区间长度折半,所以时间复杂度为\(O(log_{2}{n})\)。发明二分算法的大佬Knuth说,思路很简单,细节是魔鬼。二分的细节很是重要,据说只有10%的程序员能写对二分。所以二分要掌握自己的一套写法,练熟练会即可。
写在后面
排序算法有很多种,大致可以分为以下三类:
- 冒泡排序、插入排序、选择排序
- 堆排序、归并排序、快速排序
- 计数排序、基数排序、桶排序
前两类都是基于比较的排序算法,第一类排序时间复杂度为\(O(n^2)\),第二类排序时间复杂度为\(O(nlog_n)\),信息论以证明,基于比较的排序算法的时间复杂度下界为\(O(nlog_n)\),第二类算法已经是最优的基于比较的排序算法了
第三类算法换了一种思路,它们不直接比较大小,而是对被排序的数值采取按位划分,分类映射等处理方式,其时间复杂度不仅与\(n\)有关,还与数值的大小范围\(m\)有关。
流萤断续光,一明一灭一尺间