引言
定义
排序 :将一组无需的记录序列调整为有序的记录序列
排序算法:一种能将一串记录序列按照某种特定的方式进行调整的一种方法
格式
void X_sort(ElementType A[], int N)
- X : 排序算法名
- N :正整数,需要排序的元素个数
- 稳定性: 任意两个相等的数据,排序前后的相对位置不发生改变。
没有一种排序算法在任何情况下是最优的,必须根据实际情况选择最优的算法解决问题。
交换排序
冒泡排序
算法思想
对于n个待排序的元素,算法共进行n-1次循环。每次循环完成,最大的元素归位,排在最后面。
在第k次循环时,将第n+1-k大的元素排好。具体实现如下:
从前往后依次比较相邻的两个元素(1~n-k),如果前一个元素比后一个元素大,则交换两个元素的位置。这样,就得到了这n+1-k个元素中最大的元素。
优化:当这一趟排序没有任何元素交换时,说明序列已经全部有序,不需要进行下一趟排序,可直接结束。
增加一个flag标记:检查这一趟排序有没有元素需要交换。
算法模拟
给出一组待排序的数:44 12 59 36 62 43
排序前 | 44 | 12 | 59 | 36 | 62 | 43 |
---|---|---|---|---|---|---|
第一趟排序 | 12 | 44 | 36 | 59 | 43 | 62 |
第二趟排序 | 12 | 36 | 44 | 43 | 59 | 62 |
第三趟排序 | 12 | 36 | 43 | 44 | 59 | 62 |
第四趟排序 | 12 | 36 | 43 | 44 | 59 | 62 |
第五趟排序 | 12 | 36 | 43 | 44 | 59 | 62 |
举例:
第一趟排序:
比较 | 是否交换 | 序列 |
---|---|---|
44 > 12 | 是 | 12 44 59 36 62 43 |
44 < 59 | 否 | 12 44 59 36 62 43 |
59 > 36 | 是 | 12 44 36 59 62 43 |
59 < 62 | 否 | 12 44 36 59 62 43 |
62 > 43 | 是 | 12 44 36 59 43 62 |
算法实现
/* 交换两个元素 */
void swap(ElementType * a, ElementType * b)
{
ElementType t = *a;
*a = *b;
*b = t;
}
/* 冒泡排序 */
void Bubble_sort(ElementType A[], int N)
{
bool flag;
for( int i = N-1; i > 0; i--) /* 进行N-1趟排序 */
{
flag = true; /* 每趟排序前设置flag,标记这趟排序是否发生了交换*/
for( int j = 0; j < i; j++) /* 对0~i个元素进行排序 */
{
if(A[j] > A[j+1]) /* 如果前一个元素大于后一个元素,交换 */
{
swap( &A[j],&A[j+1]);
flag = false;
}
}
if(flag == true) /* 如此趟排序没有任何元素交换,退出排序 */
{
break;
}
}
时间复杂度分析
-
最坏情况:
当序列是逆序排列时,每次比较都要进行交换。
此时,共进行(n-1) + (n-2) + … + 2 + 1 = n(n-1)/2 次。
时间复杂度为O(n2) -
最好情况:
当序列已经排好时,只需第一次比较n-1次,flag标记为true,退出循环。
时间复杂度为O(n)
稳定性分析
是稳定的。
仅当前一个元素大于后一个元素时,才会进行交换。两个元素相等时,相对位置不会发生改变。
优缺点
- 优点: 当需要排序的元素存储在链表中时,也能很好地实现排序(因为只用比较两个相邻的元素)。
- 缺点:时间复杂度太高。
快速排序
算法思想(分而治之)
从未排序的元素中选出一个“主元”。以该“主元”作为基准,将比“主元”小的元素放在“主元”的左边,比“主元”大的元素放在“主元”的右边,得到两个子序列。分别对两个子序列递归地执行此过程,直至只剩一个元素。
1) 选择一个主元,与最后的元素进行交换
2)设置指针low和high分别指向第一个元素和倒数第二个元素
3)low从左至右扫描,找到一个大于等于主元的元素停止;
high从右至左扫描,找到一个小于等于主元的元素停止;
交换这两个元素的位置。
4)当low >= high时,停止扫描,将A[low]与主元交换。
5)递归地分别对主元左边的子序列、主元右边的子序列进行排序。
如果有元素正好等于主元?
1. 停下来交换
考虑所有元素都相等的情况,则每遇到一个元素都要停下来交换。好处是形成n/2和n/2的划分,时间复杂度为O(nlogn),坏处是进行了很多次没有意义的交换。
2. 忽视,继续移动指针
同样考虑所有元素都相等的情况,则low从左至右一直扫描到主元,形成一个n-1和1的划分,好处是不用进行多余的交换,坏处是这将导致时间复杂度为O(n2)
如果待排序的规模比较小时,由于递归,快速排序的效果不如插入排序。
解决方法:在递归过程中检查当前子问题的规模,当其小于某个阈值时,不再继续递归,直接调用插入排序。
主元的选取
一种比较好的选取方法,取第一个元素(A[low])、最后一个元素(A[high])和中间的元素(A[low+high]/2)中的中值。
算法模拟
给出一组待排序的数:44 12 59 36 62 43
第一趟排序:
A[0] = 44, A[5] = 43,A[0+5/2] = 59
A[5] < A[0] < A[2],主元选取A[0] = 44。
将主元A[0]与A[5]交换。
排序前 | 43 | 12 | 59 | 36 | 62 | 44 |
---|---|---|---|---|---|---|
low | high | 基准 | ||||
第一次交换 | 43 | 12 | 59 | 36 | 62 | 44 |
low | high | 基准 | ||||
第一次交换结果 | 43 | 12 | 36 | 59 | 62 | 44 |
low | high | 基准 | ||||
交换A[low]和主元 | 43 | 12 | 36 | 59 | 62 | 44 |
low、high | 基准 | |||||
交换A[low]和主元结果 | 43 | 12 | 36 | 44 | 62 | 59 |
low、high |
算法实现
/* 选取基准 */
ElemntType Median3(ElementTypr A[], int left, int right)
{
int center = (left+right)/2;
/* 对A[left],A[center],A[right] 排序*/
if(A[left] > A[center])
Swap(&A[left],&A[center]);
if(A[left] > A[right])
Swap(&A[left],&A[right]);
if(A[center] > A[right])
Swap(&A[center],&A[right]);
/* 此时,A[left] < A[center]< A[right] ,只需要对A[left+1]~A[right-1]进行排序 */
/*以A[center]作为基准 */
Swap(&A[center],&A[right - 1]);
return A[right - 1];
}
/*对A[left] ~ A[right] 元素进行快排*/
void Qsort(ElementType A[],int left,int right)
{
int pivot,Cutoff,low,high;
if(Cutoff <= right - left) /*元素过多,选择快排 */
{
pivot = Median3(A,left,right); /*选取基准*/
low = left;
high = right -1;/*对A[lef+1]~A[right-2]个元素进行快排*/
while(1)
{
while(A[++low] < pilot); /*找到大于等于pilot的元素*/
while(A[--high] > pilot);/*找到小于等于pilot的元素*/
if(low < high)
{
Swap(&A[low],&A[high]);/*交换两个元素的位置*/
}
else
break; /*当low == right时,退出*/
}
Swap(&A[low],&A[right-1]);
Qsort(A,left,low-1);/*对基准左边序列排序*/
Qsort(A,low+1,high);/*对基准右边序列排序*/
}
else/*元素过少,选择插入排序*/
{
Insertion_sort(A+left,right-left+1);
}
}
/*统一接口*/
void Quik_sort(ElementType A[],int N)
{
Qsort(A,0,N-1);
}
时间复杂度分析
- 最坏情况:
当每次划分都近似于1和N-1时,有(N-1) + (N-2) + … +2 +1 = N(N-1)/2
时间复杂度为O(n2)
-最好情况:
每次划分都将原序列分成两个等长的子序列,每一递归层次上为O(n),递归深度为O(log2n)。
时间复杂度为O(nlog2n)
稳定性分析
不稳定的。
对于比主元小的元素,它可能被交换到和它等值的元素以前;
对于比主元大的元素,它可能被交换到和它等值的元素以后。
导致相对位置发生改变。