冒泡排序 选择排序 快速排序 堆排序 归并排序
本片博客的基本思路:
先简单介绍具体每种算法的基本思想,然后是关键代码实现,最后是整个应用程序的所有源码及Makefile文件我打包放在我的“资源”中了,
资源名字为“sorting”欢迎下载交流。
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1. 冒泡排序(BubbleSort)
冒泡排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。
即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。
至此第一趟结束,将最大的数放到了最后。
在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,
一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。
如此下去,重复以上过程,直至最终完成排序。
由于在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序。
/**
* BubbleSort. Sorting the data use the sortFunc (asc or desc).
*
* @param data An integer pointer that point an integer array.
* @param count An integer number, that is, the elements number in the data.
* @param sortFunc A function that asserting two integer number whether satisfy 'asc' or 'desc'.
*/
void bubbleSort(int *data, int count, Sort sortFunc) {
int i,j;
int noswapFlag = TRUE, continueFlag;
for (i=0; i<count-1; i++) {
continueFlag = FALSE;
for (j=1; j<count-i; j++) {
noswapFlag = sortFunc(data+j-1, data+j);
if (!noswapFlag) {
swap(data+j-1, data+j);
continueFlag = TRUE;
}
}
if (!continueFlag)
break;
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2. 选择排序(SelectSort)
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [76 97 65 49]
第五趟排序后 13 27 38 49 49 [97 65 76]
第六趟排序后 13 27 38 49 49 65 [97 76]
第七趟排序后 13 27 38 49 49 65 76 [97]
最后排序结果 13 27 38 49 49 65 76 97
#include "include/const.h"
#include "include/sort.h"
/**
* SelectSort. Sorting the data use the sortFunc (asc or desc).
*
* @param data An integer pointer that point an integer array.
* @param count An integer number, that is, the elements number in the data.
* @param sortFunc A function that asserting two integer number whether satisfy 'asc' or 'desc'.
*/
void selectSort(int *data, int count, Sort sortFunc) {
int i,j;
int noswapFlag = FALSE;
int min_or_max = 0;
for (i=0; i<count-1; i++) {
min_or_max = i;
for (j=i+1; j<count; j++) {
noswapFlag = sortFunc(data+min_or_max, data+j);
if (!noswapFlag) {
min_or_max = j;
}
}
if (min_or_max == i)
continue;
swap(data+min_or_max, data+i);
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3. 快速排序(QuickSort)
它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
假设要排序的数组是A[1]……A[N],首先任意选取一个数据(通常选用第一个数据)作为关键数据,
然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一躺快速排序。
//--------------一躺快速排序的算法是:-------------------------------------------------------------
1)、设置两个变量I、J,排序开始的时候I:=1,J:=N;
2)、以第一个数组元素作为关键数据,赋值给X,即X:=A[1];
3)、从J开始向前搜索,即由后开始向前搜索(J:=J-1),找到第一个小于X的值,两者交换;
4)、从I开始向后搜索,即由前开始向后搜索(I:=I+1),找到第一个大于X的值,两者交换;
5)、重复第3、4步,直到I=J;
例如:待排序的数组A的值分别是:(初始关键数据X:=49)
A[1] A[2] A[3] A[4] A[5] A[6] A[7]:
49 38 65 97 76 13 27
进行第一次交换后: 27 38 65 97 76 13 49
( 按照算法的第三步从后面开始找
进行第二次交换后: 27 38 49 97 76 13 65
( 按照算法的第四步从前面开始找>X的值,65>49,两者交换,此时I:=3 )
进行第三次交换后: 27 38 13 97 76 49 65
( 按照算法的第五步将又一次执行算法的第三步从后开始找
进行第四次交换后: 27 38 13 49 76 97 65
( 按照算法的第四步从前面开始找大于X的值,97>49,两者交换,此时J:=4 )
此时再执行第三不的时候就发现I=J,
//-----------从而结束一躺快速排序,-------------------------------------------------------------------------------------
那么经过一躺快速排序之后的结果是:27 38 13 49 76 97 65,
即所以大于49的数全部在49的后面,所以小于49的数全部在49的前面
//-----------
快速排序就是递归调用此过程——在以49为中点分割这个数据序列,
分别对前面一部分和后面一部分进行类似的快速排序,从而完成
全部数据序列的快速排序,最后把此数据序列变成一个有序的序列
//============
注意:在“一躺快速排序”中关键key永远不变,永远是和key进行比较,无论在什么位置,最后的目的就是把key放在中间,小的放前面大的放后面。
即所以大于key的数全部在key的后面,所以小于key的数全部在key的前面
但是,在对key前后的子数列进行“一躺快速排序”时,关键字key就变了,具体指定和“一躺快速排序”是完全相同,例如是子数列第一个元素。
#include "include/const.h"
#include "include/sort.h"
#include <stdio.h>
/**
* QuickSort. Sorting the data use the sortFunc (asc or desc).
*
* @param data An integer pointer that point an integer array.
* @param count An integer number, that is, the elements number in the data.
* @param sortFunc A function that asserting two integer number whether satisfy 'asc' or 'desc'.
*/
void quickSort(int *data, int count, Sort sortFunc) {
int i = 0,j = count-1;
int key = 0;
int noswapFlag = TRUE;
if (count <= 1) {
return;
}
start:
for (j=count-1; noswapFlag && i != j; j--) {
noswapFlag = sortFunc(data+key, data+j);
if (!noswapFlag) {
swap(data+key, data+j);
key = j;
break;
}
}
noswapFlag = FALSE;
for (i=0; !noswapFlag && i != j; i++) {
noswapFlag = sortFunc(data+key, data+i);
if (noswapFlag) {
swap(data + key, data+i);
key = i;
break;
}
}
if (i == j) {
printf("i = j = %d, key = %d\n", i, key);
if (i == 0) {
quickSort(data+1, count-key-1, sortFunc);
} else if (i == count-1) {
quickSort(data, key, sortFunc);
} else {
quickSort(data, key, sortFunc);
quickSort(data+key+1, count-key-1, sortFunc);
}
return;
} else {
goto start;
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5. 归并排序(MergeSort)
归并排序是利用递归和分而治之的技术将数据序列划分成为越来越小的半子表,再对半子表排序,
最后再用递归步骤将排好序的半子表合并成为越来越大的有序序列,归并排序包括两个步骤,分别为:
1)划分子表
2)合并半子表
首先我们来讨论归并算法,归并算法将一系列数据放到一个向量中,索引范围为[first,last],
这个序列由两个排好序的子表构成,以索引终点(mid)为分界线,以下面一个序列为例
7,10,19,25,12,17,21,30,48
这样的一个序列中,分为两个子序列 7,10,19,25 和 12,17,21,30,48,
再使用归并算法的时候的步骤如下,对应merge函数:
第一步:比较v[indexA]=7和v[indexB]=12,将较小的v[indexA]取出来放到临时向量tempArray中,然后indexA加1,如下:
tempArray: 7
7,10,19,25 和 12,17,21,30,48,
| |
indexA indexB
第二步:比较v[indexA]=10和v[indexB]=12,将较小的10放到临时变量tempArray中,然后indexA++;
tempArray: 7,10
7,10,19,25 和 12,17,21,30,48,
| |
indexA indexB
第三步:比较v[indexA]=19与v[indexB]=12,将较小的12存放到临时变量tempArray中,然后indexB++;
tempArray: 7,10,12
7,10,19,25 和 12,17,21,30,48,
| |
indexA indexB
第四步到第七步:按照以上规则,进行比对和存储,得到如下结果:
tempArray: 7,10,12,17,19,21,25,30,48
最后一步:将子表b中剩余项添加到临时向量tempArray中
然后将临时变量中的值按照索引位置,拷贝回向量v中,就完成了对向量v的归并排序
#include "include/const.h"
#include "include/sort.h"
#include <stdio.h>
static void merge(int *data1, int *data2, int totalCount, Sort sortFunc);
/**
* MergeSort. Sorting the data use the sortFunc (asc or desc).
*
* @param data An integer pointer that point an integer array.
* @param count An integer number, that is, the elements number in the data.
* @param sortFunc A function that asserting two integer number whether satisfy 'asc' or 'desc'.
*/
void mergeSort(int *data, int count, Sort sortFunc) {
if (count<2) {
return;
} else {
int mid = count/2;
mergeSort(data, mid, sortFunc);
mergeSort(data+mid, count-mid, sortFunc);
merge(data, data+mid, count, sortFunc);
}
}
/**
* Meger two array, data1 and data2 (All is asc or desc sorted) to an array and keep the sorted at the same time.
*
* This 'merge' function used the extra array memory space.
*
* @param data1 An integer pointer that point an integer array and this array is sorted.
* @param data1 An integer pointer that point an integer array and this array is sorted the same to data1 (asc or desc).
* @param count An integer number, that is, the elements number in the data.
* @param sortFunc A function that asserting two integer number whether satisfy 'asc' or 'desc'.
*/
static void merge(int *data1, int *data2, int totalCount, Sort sortFunc) {
int count1 = data2>data1 ? (data2 - data1) : -1;
int count2 = totalCount - count1;
int i,j,n;
int temp[totalCount], *tmp1 = data1, *tmp2 = data2;
int noswapFlag = FALSE;
if (count1 == -1) {
printf("The way of using the 'merge' function is error.\n");
return;
}
for (i=0,j=0,n=0; i<count1 && j<count2 && n<totalCount; n++) {
noswapFlag = sortFunc(tmp1, tmp2);
if (noswapFlag) {
temp[n] = *tmp1;
tmp1++;
i++;
} else {
temp[n] = *tmp2;
tmp2++;
j++;
}
}
if (i < count1) {
for(; i<count1; i++,n++,tmp1++) {
temp[n] = *tmp1;
}
} else if (j < count2) {
for(; j<count2; j++,n++,tmp2++) {
temp[n] = *tmp2;
}
}
for (n=0; n<totalCount; n++){
data1[n] = temp[n];
}
}
//----------------------------改进--不使用临时响亮tempArray---对应merge1函数------------------------------------
并不将较小的v[indexA]取出来放到临时向量tempArray中,而是:
1. 比如在升序排序时,当v[indexA] > v[indexB]将v[indexA]和v[indexB]进行交换;
2. 将交换过的v[indexB]和其后的元素做比较,直到满足升序条件为止(因为v[A]和v[B]在交换前都是满足升序的);
3. 按照步骤1和2,继续比较indexA后的其他元素,直到v[A]结束。
static void merge1(int *data1, int *data2, int totalCount, Sort sortFunc);
/**
* MergeSort. Sorting the data use the sortFunc (asc or desc).
*
* @param data An integer pointer that point an integer array.
* @param count An integer number, that is, the elements number in the data.
* @param sortFunc A function that asserting two integer number whether satisfy 'asc' or 'desc'.
*/
void mergeSort(int *data, int count, Sort sortFunc) {
if (count<2) {
return;
} else {
int mid = count/2;
mergeSort(data, mid, sortFunc);
mergeSort(data+mid, count-mid, sortFunc);
merge1(data, data+mid, count, sortFunc);
}
}
/**
* Meger two array, data1 and data2 (All is asc or desc sorted) to an array and keep the sorted at the same time.
*
* This 'merge' function used the extra array memory space.
*
* @param data1 An integer pointer that point an integer array and this array is sorted.
* @param data1 An integer pointer that point an integer array and this array is sorted the same to data1 (asc or desc).
* @param count An integer number, that is, the elements number in the data.
* @param sortFunc A function that asserting two integer number whether satisfy 'asc' or 'desc'.
*/
static void merge1(int *data1, int *data2, int totalCount, Sort sortFunc) {
int count1 = data2>data1 ? (data2 - data1) : -1;
int count2 = totalCount - count1;
int i,j;
int *tmp1 = data1, *tmp2 = data2;
int noswapFlag = FALSE;
int cmpFlag1 = FALSE, cmpFlag2 = FALSE;
if (count1 == -1) {
printf("The way of using the 'merge' function is error.\n");
return;
}
for (i=0; i<count1; i++,tmp1++) {
noswapFlag = sortFunc(tmp1, tmp2);
if (!noswapFlag) {
int *tmp = tmp2;
swap(tmp1, tmp2);
for(j=1; j<count2; j++) {
noswapFlag = sortFunc(tmp, tmp+1);
if (!noswapFlag) {
swap(tmp, tmp+1);
tmp++;
} else {
break;
}
}
}
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5. 堆排序(HeapSort)
二叉堆定义:
n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n),当然,这是小根堆,大根堆则换成>=号。
//k(i)相当于二叉树的非叶结点,K(2i)则是左孩子,k(2i+1)是右孩子
(1) 树的数组存储
n个节点的树存储在具有n个元素的数组中(下表从0开始),树根下表为i,则其左右孩子坐标为2*i+1, 2*i+2, 其中0<=i<n
(2) 构建大/小根的堆
构建堆算法描述:
从下向上构建堆,即,有一个有n个元素的序列,那么从第n/2个元素开始构建堆(不管下表是从0还是1开始计)。
也就是说,将n/2下标标记的节点为根的二叉树构建为堆;
(注意:如果构建堆的过程中,父节点与其孩子节点进行了交换,那么以该孩子为根的树可能已经不满足堆的性质了,
需要递归的重新构建以该孩子节点为根的树为堆;其实只要不考虑该孩子节点,该孩子节点的所有子节点仍然满足堆的性质,
因此这时不需要“从下向上构建堆”了,直接从以该孩子节点为根的节点开始构建即可)
然后将下表为i(i<n/2)的剩余所有节点为根的二叉数也都构建为堆。
①构建大根的堆
在构建堆时满足根节点大于等于左右子节点即可。
②构建小根的堆
在构建堆时满足根节点小于等于左右子节点即可。
(3) 对大/小根的堆进行排序。
大根堆排序算法的基本操作:
① 初始化操作:将R[1..n]构造为初始堆;
② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。
注意:
①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。
②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。
堆排序和直接选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止
算法分析
堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。
堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。
由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
堆排序是就地排序,辅助空间为O(1),它是不稳定的排序方法。
//------------------注意------------------------
根据(3)中堆排序的思想,在升序排序时,使用大根堆比较方便;降序排序时,使用小根堆比较方便。
#include "include/const.h"
#include "include/sort.h"
#include <stdio.h>
//If the sortFunc is the asc, the large root heap will be created. This will be more easy to asc sort;
//If the sortFunc is the desc, the small root heap will be created. This will be more easy to desc sort
static void createHeap(int *data, int i, int nLength, Sort sortFunc)
{
int nChild;
int nTemp;
int noswapFlag = TRUE;
nChild=2*i + 1;
//printf("nChild = %d\ti = %d\n", nChild, i);
//lchildIndex = 2 * parentIndex + 1
//lchild, that is nChild = 2 * i + 1;
//get the larger child from lchild and rchild
if (nChild < nLength) {
if (nChild < nLength-1 && sortFunc(data+nChild, data+nChild+1))
++nChild;
//If the larger child is larger than the parent, swap the parent and child.
noswapFlag = sortFunc(data+i, data+nChild);
if (noswapFlag) {
swap(data+nChild, data+i);
//printf("data[nChild] = %d\tnChild = %d\n", data[nChild], nChild);
createHeap(data, nChild, nLength, sortFunc);
}
}
}
static void heapSortInternal(int *data, int count, Sort sortFunc) {
int i;
for (i=1; i<count; i++) {
swap(data, data+count-i);
createHeap(data, 0, count-i, sortFunc);
}
}
/**
* HeapSort. Sorting the data use the sortFunc (asc or desc).
*
* @param data An integer pointer that point an integer array.
* @param count An integer number, that is, the elements number in the data.
* @param sortFunc A function that asserting two integer number whether satisfy 'asc' or 'desc'.
*/
void heapSort(int *data, int count, Sort sortFunc) {
int i = 0;
//Create the heap, large root heap or small root heap.
for (i=count/2; i>=0; i--) {
createHeap(data, i, count, sortFunc);
}
/**Only for testing.
printf("Display the heap data:\n");
printData(data, count);
*/
//Heap sorting.
heapSortInternal(data, count, sortFunc);
}