几种常用排序算法的复习
《数据结构》中介绍了好几种排序算法,有时候觉得晕晕的,所以就再拿出来复习下。
插入类排序
插入类排序是将一个记录插入到一个已经排好序的有序表中,使得新表仍然有序,经常接触到的插入类排序的有直接插入排序,折半插入排序,希尔排序等。
为了以后复习的时候可以尽快回忆起各类的排序,这里均以一个序列作为例子:{2,3,1,5,4,9,7,8,6,10}
直接插入排序
直接插入排序的思想是每趟将一个待排序的元素作为关键字,按照其关键字值的大小插入到已经排好的部分序列的适当位置上,,直到插入完成。
如:原始:2,3,1,5,4,9,7,8,6,10(第一个元素默认有序)
(2,3)1,5,4,9,7,8,6,10(括号内为有序区)
(1,2,3)5,4,9,7,8,6,10
(1,2,3,5)4,9,7,8,6,10
(1,2,3,4,5)9,7,8,6,10
(1,2,3,4,5,9)7,8,6,10
(1,2,3,4,5,7,9)8,6,10
(1,2,3,4,5,7,8,9)6,10
(1,2,3,4,5,6,7,8,9)10
(1,2,3,4,5,6,7,8,9,10)
查找的时候可用一个变量存储待排序的元素,然后往前找到合适的插入位置,程序可参考如下:
/**
*InserSort - 直接插入排序
*@Array[]:待排序数组
*@n:数组长度
*/
void DirInsertSort(int Array[],int n)
{
int i, j, temp;
for (i = 1; i < n ; ++i)//从1开始,第0个元素看成本身有序
{
temp = Array[i];//暂存第i位
j = i - 1;
while (j >= 0 && temp < Array[j])
{
Array[j + 1] = Array[j];
--j;
}
Array[j + 1] = temp;
}
}
一般情况下,本算法的时间复杂度是O(n^2)折半插入排序
折半插入排序的思想和直接插入排序一样区别在于寻找插入位置的方法不同,折半插入排序时采用折半查找法来寻找插入的位置。其程序参考如下:
/**
*BInsertSort - 折半插入排序
*@Array[]:待排序数组
*@n:数组长度
*/
void BInsertSort(int Array[], int n)
{
int i,j,temp;
int low,mid,high;
for (i = 1; i < n; ++i)
{
temp = Array[i];//
low = 0;
high = i - 1;//利用折半查找找到插入位置
while (low < high)
{
mid = (low + high) / 2;
if (Array[mid] < temp)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
for (j = i - 1; j >= high + 1; j--)//i-1之前的都需要后移
{
Array[j + 1] = Array[j];
}
Array[j + 1] = temp;
}
}
如果数据量比较大,折半插入排序查找的效率会比直接插入排序的好,其算法时间复杂度是O(n^2)希尔排序
希尔排序又称缩小增量排序,其本质还是插入排序,但是它将待排得序列按某种规则分成几个子序列,分别对这几个子序列进行直接插入排序,所以希尔排序的算法和直接插入排序的算法很相近。
算法的思想是将整个序列分成若干子序列,对各个子序列进行直接插入排序,得到一趟希尔排序序列,然后缩短步长,重复以上的动作,直到步长为1。
例如:2,3,1,5,4,9,7,8,6,10,步长d一般初始化可取n/2,那么第一次增量d=5
d=5时,可以得到以下5个子序列
此时恰好每个子序列都是有序的,故进行插入排序的结果一样
第二次可取d=3,那么可得到如下的几个序列:
每个子序列经过直接插入排序可得(因为这个例子本来就是基本有序的,所以不是很好):
第三次可取d=1,此时数据已基本有序,再使用直接插入排序效率会比较高,程序可参考如下:
/**
*BInsertSort - 希尔排序(缩小增量)
*@Array[]:待排序数组
*@n:数组长度
*/
void ShellSort(int Array[], int n)
{
int i, j, k, temp;//增量k
k = n / 2;
while (k > 0)
{
for (j = k; j < n; j++)//从后到前
{
temp = Array[j];
i = j - k;//根据增量找上一个
while (i >= 0 && Array[i] > temp)
{
Array[i + k] = Array[i];
i = i - k;
}
Array[i + k] = temp;
}
k = k / 2;
}
}
交换排序
交换排序常见的有冒泡排序和快速排序
冒泡排序
冒泡排序是交换排序中一种简单的排序方法。基本思想是从前往后(或从后往前)对所有相邻记录的关键值进行比较,如果是逆序,则将其交换,最终达到有序化。
例如:2,3,1,5,4,9,7,8,6,10,我们假设从前往后,则一趟冒泡排序可将最大值放到最后的位置,整个排序的过程如下所示:
原序列:2,3,1,5,4,9,7,8,6,10
(1) 2,1,3,4,5,7,8,6,9,10
(2) 1,2,3,4,5,7,6,8,9,10
(3) 1,2,3,4,5,6,7,8,9,10(此时序列已经有序,没有发生交换,排序完成)
冒泡排序的程序可参考如下:
/**
*BInsertSort - 冒泡排序
*@Array[]:待排序数组
*@n:数组长度
*/
void BubbleSort(int Array[], int n)
{
int temp, flag;
for (int i = 0; i < n - 1;i++)
{
flag = 0;
for (int j = 0; j < n - 1 - i; j++)
{
if (Array[j] > Array[j + 1])//大的数据交换到后面
{
temp = Array[j + 1];
Array[j + 1] = Array[j];
Array[j] = temp;
flag = 1;//有交换flag置为1
}
}
if (flag == 0)break;//若一趟冒泡排序没有发生交换,则排序完成
}
}
算法时间复杂度是O(n^2)快速排序
快速排序的节本思想是:通过一趟快速排序将待排序的记录分成独立的两部分,一部分记录的关键字比另一部分的关键字小。然后对着这两部分再继续排序,一直达到整个序列有序。
例如:序列2,3,1,5,4,9,7,8,6,10,我们从低位开始排序,即第一次确定2在整个序列中的最终位置,即经过一趟快速排序后,2前面的都是比2小的,2后面的都是比2大的
原序列:2,3,1,5,4,9,7,8,6,10
(1) 1,2,3,5,4,9,7,8,6,10
(2) 1,2,3,5,4,9,7,8,6,10
(3) 1,2,3,4,5,9,7,8,6,10
(4) 1,2,3,4,5,6,7,8,9,10
其程序可参考如下:
(4) 1,2,3,4,5,6,7,8,9,10
其程序可参考如下:
/**
*BInsertSort - 快速排序
*@Array[]:待排序数组
*@low,high:数组下标
*/
void QuickSort(int Array[], int low,int high)
{
int l = low, h = high;
if (low < high)
{
int temp = Array[low];//num暂存要比较的一个记录
while (l < h)
{
while (h > l && Array[h] >= temp)
--h;//从后找比他小的
Array[l] = Array[h];
while (l < h && Array[l] <= temp)
++l;//从前找比他大的
Array[h] = Array[l];
}
Array[l] = temp;
QuickSort(Array, low, l - 1);
QuickSort(Array, l + 1, high);
}
}
该排序算法是不稳定排序,其时间复杂度是O(nlog2n)
选择排序
简单选择排序
简单选择排序的基本思想是,每一趟排序都在n-i+1个记录中选取关键字最小的记录做作为有序序列的第i个记录。例如序列:2,3,1,5,4,9,7,8,6,10,第一趟选择排序选出最小值1,放到第一个位置,第二趟选择排序选出除1外的最小值,放到第二个位置,以此类推,如下所示:
原序列:2,3,1,5,4,9,7,8,6,10
(1) 1,3,2,5,4,9,7,8,6,10
(2) 1,2,3,5,4,9,7,8,6,10
(3) 1,2,3,5,4,9,7,8,6,10
(4) 1,2,3,4,5,9,7,8,6,10
(5) 1,2,3,4,5,9,7,8,6,10
(6) 1,2,3,4,5,6,7,8,9,10
其实现代码可参考如下:
/**
*BInsertSort - 选择排序
*@Array[]:待排序数组
*@n:数组长度
*/
void SelectSort(int Array[], int n)
{
int i, j, index, temp;
for (i = 0; i < n - 1; i++)//对n个记录进行n-1趟的简单选择排序
{
index = i;
for (j = i + 1; j <= n - 1; j++)
{
if (Array[j] < Array[index])
index = j;//index一直是最小记录的下标
}
if (index != i)
{//index发生改变,交换
temp = Array[i];
Array[i] = Array[index];
Array[index] = temp;
}
}
}
其时间复杂度为O(n^2).其他排序如堆排序,桶排序有空再来