本文主要讲解基础排序算法中的快速排序和归并排序;这两种排序是非常非常简单的两个小算法案例。闲来被小伙伴问了一下,于是就对这两个算法的内容进行一个讲解。
快速排序
快速排序的背景和性能就不加赘述了,这里就介绍一下,在知道想法之后,一些小伙伴也会困扰和出错的地方。
算法讲解
通过待排序的元素和排序好的序列进行插入,依次进行。
例如:
a1
,
a2
,
a3
,
a4
已经排列好了,那么插入进来的
a5
,应该放在其中哪个位置;
一般的方法就是依次从右往左遍历,直到找到了适合插入的位置,此时正好大于等于前面一个元素
ak
。接着是让比它大的元素右移。这里有的同学经常用的交换,也可以;
快排程序示例1
int quickSort2(int arr[],size_t n){
for(int i=0;i != n-1; ++i){
int k = i+1;
int next = arr[i+1];
while(arr[k-1]> next && k>-1){
arr[k] = arr[k-1];
k--;
}
arr[k] = next;
}
return 0;
}
快排的时间复杂度是
O(nlog2(n))
,在最坏的情况下就是
O(n2)
;
为了便于理解对这个一般的方法可以进行一个小小的改良,例如在找到合适的位置插入的过程中,我们使用二分查找,那么二分查找的复杂度
O(log2(n))
, 正好外面一层是
O(n)
, 所以进行一个嵌套之后就知道了,是
O(nlog2(n))
快速排序程序示例
可以在上面k值待插时,使用二分查找的想法; 这里返回待插入的位置。并且保证稳定性。二分查找示例。
int binFind(const int arr[],int start,int end,int key){
int left,right;
int mid;
left=start;
right=end;
while(left<=right){
mid=(left+right)/2;
if(key==arr[mid]) return mid+1;
else if(key < arr[mid]) right=mid-1;
else if(key>arr[mid]) left=mid+1;
}
return left;//没找到这个元素,正好错过去了;那么就返回left的角标
}
归并排序
归并的背景和性能也不加赘述了,这里就介绍一下,常见的困扰地方或者复写容易出错的地方。
算法讲解
通过分治的想法对数列进行排序。
什么叫分治?分治就是把问题分解为一个个非常小的规模,解决之后再合并起来组成全局问题的解。
很显然,归排就是这样的一个经典例子。
A1
:首先任何一个数列都可以依次分裂成两半, 两半中的任意一半又可以分裂成均匀两半,直到都分解到非常小的规模后,再进行比较;
A2
:当规模足够小的时候,可以想到总会出现像二叉树一样的结构;依次从根部层序向上归并。在这里分离出一个功能函数Merge()来维护;
B1
:可以看到这个程序是一个从上往底,从底往上的一个过程;内部是用递归维持的,这个结构也就是二叉树的结构;所以时间复杂度是
O(nlog2(n))
, 这个就不加赘述了。
归排程序示例
int Merge(int arr[],int tempArr[], int start, int mid, int end){
int i = start, j=mid+1, k = start;
while(i!=mid+1 && j!=end+1){
if(arr[i] > arr[j])
tempArr[k++] = arr[j++];
else
tempArr[k++] = arr[i++];
}
while(i != mid +1)
tempArr[k++] = arr[i++];
while(j != end+1)
tempArr[k++] = arr[j++];
for(i=start; i<=end; i++)
arr[i] = tempArr[i];
return 0;
}
int MergeSort(int arr[], int tempArr[], int start, int end){
int mid;
if(start < end){
mid = (start + end) / 2;
MergeSort(arr, tempArr, start, mid);
MergeSort(arr, tempArr, mid+1, end);
Merge(arr, tempArr, start, mid, end);
}
return 0;
}
上面两种方法可以看到本质上非常接近,至于 O(log2(n)) 可以直观了解到,有二分查找,有递归或者树结构; 有兴趣的关于更多排序方法可以在wiki百科中查找到;