二分法

1.算法设计

用一维数组S[]存储该有序序列,设变量low和high表示查找范围的下界和上界,middle表示查找范围的中间位置,x为特定的查找元素。

(1)初始化。令low=0,high=n-1。

(2)middle=(high-low)/2。

(3)判断low<=high是否成立,若成立,到第4步,否则算法结束。

(4)判断S[middle]与x的关系:若S[middle]>x,则x位于low~middle之间,令high=middle-1,;若S[middle]<x,则x位于middle~high之间,令low=middle+1;转第2步。若S[middle]==x,则搜索成功,退出。

2.图解

数据结构学习笔记已有整理,在此省略图解。

3.完整代码

 

排序复杂度为O(nlogn),二分查找复杂度为O(logn)。

递归版:

 

此方法二分查找复杂度仍旧是O(logn)。

合并排序

1.算法原理

将一大组数据,采用分治策略,分成很多个小问题。由于给定的是一个无序的大序列,可以把待排序列分解成两个规模大致相等的子序列,如果排序还是比较困难,则将子序列继续分解成两个规模大致相等的子序列,直到每个子序列的元素个数为1。然后进行合并,合并成完整的有序序列。

2.算法设计

(1)分解:将待排序列分解成大小相等的两个子序列;

(2)治理:将两个子序列进行合并排序;

(3)合并:将排好序的有序子序列进行合并,得到最终的序列。

合并的策略:采用i,j,k三个标志位,以及一个名为B的辅助数组。i,j分别位于A数组的0和mid+1位置,k指向B的起始位。由于A数组的0~mid,mid+1~end均为已经排好序的子序列,所以开始比较A[i]和A[j]。若A[i]>A[j],则将A[j]的数字存入B[k],然后j++,k++;若A[i]<A[j],则将A[i]的数字存入B[k],然后i++,k++;若此时某个数组已经比较完,则直接将另一个数组依序存入B,然后把B重新一一赋值给A,完成对A的排序。

3.完整代码

 

4.复杂度分析

时间复杂度:

(1)分解:O(1);

(2)治理:2T(n/2);

(3)合并:O(n)。

总的来说,T(n)=O(nlogn)。

空间复杂度:O(logn)。

快速排序

1.算法原理

通过一组排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分所有数据都要笑,然后按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此使所有数据变成有序序列。

快速排序是不稳定的算法!

2.算法设计

(1)分解:先从数列中取出一个元素作为基准元素,以基准元素为准,将问题分解为两个子序列,使小于等于基准元素的子序列在左侧,大于基准元素的子序列在右侧。

(2)治理:对两个子序列进行快速排序。

(3)合并:将排好序的两个子序列合并在一起,得到原问题的解。由于排序是原地进行的,所以在排好序后,合并步骤无需做什么,序列就已经排好序了。

一般来说,基准元素的选取有以下几种方法:

(1)取第一个元素;

(2)取最后一个元素;

(3)取中间位置的元素;

(4)取第一个、最后一个、中间位置元素三者的中位数;

(5)去第一个和最后一个之间位置的随机数k(low<=k<=high),选取R[k]做基准元素。

这个算法的难点在于“划分”,也就是分解这个步骤。

我们编写划分函数对原序列进行分解,分解为两个子序列,以基准元素pivot为界,左侧子序列都比pivot小,右侧子序列逗比pivot大。先从右向左扫描,找到小于等于pivot的数后,执行R[i]与R[j]的交换,然后进行i++,也就是从左向右扫描,找到比基准元素大的数,执行R[i]与R[j]的交换,然后进行j++……如此交替进行,直到i=j的时候停止。

3.完整代码

 

4.复杂度分析

1.时间复杂度分析

(1)最好时间复杂度

①分解:O(n);

②解决子问题:在最理想情况下,每次将问题划分为规模相当的两个子序列,也就是规模为n/2,即求解两个规模为n/2的子问题,所需时间为2T(n/2);

③合并:没有任何操作,不需要时间复杂度。

所以

经过递归求解(规模为1,令n=2x,则x=logn),所以T(n)=O(nlogn)

(2)最坏时间复杂度

①分解:O(n);

②解决子问题:在最坏情况下,基准元素的一侧无元素,另一次有1个规模为n-1的子问题,那么递归出来所需要的时间为T(n-1);

③合并:没有任何操作,不需要时间复杂度。

所以

所以T(n)=O(n2)

空间复杂度:O(n)

(3)平均时间复杂度:O(nlogn)

5.算法优化扩展

上述算法都是每次交换与基准元素进行交换,实际上没必要这样,我们的目的就是把原序列分为以基准元素为界的两个子序列,左侧子序列小于等于基准元素,右侧子序列大于基准元素。步骤如下:

步骤1:首先取数据第一个元素作为基准元素pivot=R[low]。i=low, j=high。

步骤2:从右向左扫描,找小于等于pivot的数R[j]。

步骤3:从左向右扫描,找大于pivot的数R[i]。

步骤4:R[i]和R[j]交换,然后i++, j–。

步骤5:重复步骤2~步骤4,直到i和j相等,如果R[i]大于pivot,则R[i-1]和基准元素R[low]交换,返回该位置mid=i-1;否则R[i]跟基准元素交换,返回该位置mid=i,该位置的数正好是基准元素。

至此完成一趟排序。此时以mid为界,左侧子序列均小于pivot,右侧子序列均大于pivot。

代码如下: