经典排序、搜索算法
GITHUB
https://github.com/nebulium/Sorting
参考书
- 《学习JavaScript数据结构与算法》
冒泡排序
- 两层循环,外层循环做计数,内层循环每次完成一个循环的时候确保未排序部分最大的值“冒泡”到数组最末端,下面是i=0的情况:
- 显然,每次j=length-1的时候,未排序部分的最大值已经到达右端了(因为比较的是o[j+1]和o[j]),即已经冒泡完毕。所以没有必要每次在内层循环都对这些已经排序完毕的进行比较。因此有改进的冒泡排序。
- 改进的冒泡排序:修改内层循环中j的上限:j < length-1-i;
- 算法复杂度:O(n2)
选择排序:
- 原址比较排序算法
- 算法复杂度:O(n2)
- 同样两层循环,外层循环做计数,且每次预设第i个值为最小值(minIndex)。内层循环每次完成一个循环都确保未排序部分的最小的值被选中(minIndex),继而与预设的索引指向的值交换。下面是i=0的情况:
插入排序:
- 每次比较一个数组项。先默认第一项已经排序完毕,考虑第二项应该放在第一项的左边还是右边;第二项插入完毕后,认为第一项和第二项都已经排序完毕,考虑第三项应该插入第一项的左边还是第一二项的中间亦或者第二项的右边…依次类推,直到所有项排序完毕。注意需要进行数组项的移位:
a. 从i=1开始考虑(因为i=0的情况显然已经“排序完毕”了)。
b. 将当前值“取出”给temp。依次与前面的已经排序号的数组项进行比较。
c. 如果比前一个值(即已经升序排序完毕的数组项的最后一个值)大,则不需要进一步排序,因为包含当前值的数组项已经升序排序好了。
d. 如果比前一个值小,则需要将前一个值移位值当前值,并继续与更前面的一个值比较。直到找到合适的位置(升序排列的合适的位置),将取出的值 (temp)插入到那个位置。
- 算法复杂度:同样有两重循环,在排序小型数组的时候,性能比冒泡排序和选择排序都好。
归并排序:
- 递归算法,算法复杂度为O(nlogn)
- 使用分治的方法。将数组分解,直至分解为只含有一个值的数组,再调用merge函数,对其进行合并以及排序。
快速排序
- 同样用分治的方法,将数组分解。但是由于对数组的分解并不是比较均匀的,分解的函数甚至有重叠的情况。所以如果想要直观的理解比较困难。此处列出算法。建议理解代码。个人看来,这个排序算法的实现用到的递归比归并排序的好理解:
a. 首先需要进行划分操作:找到一个主元、并定义两个作为左右指针的变量,一个取索引0,一个取索引length-1.
b. 左指针从左向右找到一个比主元大的值,右指针从右向左找到一个比主元小的值,直到二者发生交错(left>right)。也就是说,基本上按照升序排列,要求主元左边的值都大于右边的。
c. 找到对应的两个值之后交换。
d. 现在得到两个新的数组分区,即从0索引到移动后的左指针的位置(实际上是-1处),以及左指针到数组最右端(length-1)处。对这两段分区再一次调用划分操作的函数,进行递归处理。
e. 直到数组排序完成(left大于index-1,right小于index,即数组无法继续进行划分)
- 算法复杂度:O(nlogn),但性能比前面的都要好。
搜索算法
- 顺序搜索:顺序进行搜索,效率低。
- 二分搜索
a. 二分搜索的前提,就是先调用排序算法、将数组从小到大排序。
b. 然后取其中间的数值,与要搜索的值进行比较:如果小于该值,则应该在右半部分进行进一步的二分搜索;如果大于该值,则在左半部分进行;等于该值,则搜索成功。
- 具体搜索的方式可以根据数据结构进行灵活的调整。比如说二叉树的搜索就是一种经过修改的二分搜索。