交换排序
所谓交换,是根据序列中的俩个元素关键字的比较结果来对换这俩个记录在序列中的位置。主要是冒泡排序和快速排序。
冒泡排序
排序的基本思想:从后往前(或从前往后)两两比较相邻元素的值,若为逆序(即A[i-1]>A[i]),直到序列比较完,我们称它完成了第一次冒泡,结果是将最小的元素交换到待排序列的第一个位置(或将最大的元素交换到待排序列的最后一个位置),关键字最小元素如气泡一般逐渐往上漂浮直至水面,下一趟冒泡时,前一趟确定的最小元素不再参与比较,每趟冒泡的结果是把序列中最小的元素(或最大的元素)放到了最终位置,这样最多做n-1趟冒泡就能把所有元素都排好。
冒泡排序示例(从下往上)
初始状态 | 第一趟后 | 第二趟后 | 第三趟后 | 第四趟后 | 第五趟后 | 最终状态 |
---|---|---|---|---|---|---|
49 | 13 | 13 | 13 | 13 | 13 | 13 |
38 | 49 | 27 | 27 | 27 | 27 | 27 |
65 | 38 | 49 | 38 | 38 | 38 | 38 |
97 | 65 | 38 | 49 | 49 | 49 | 49 |
76 | 97 | 65 | 49* | 49* | 49* | 49* |
13 | 76 | 97 | 65 | 65 | 65 | 65 |
27 | 27 | 76 | 97 | 76 | 76 | 76 |
49* | 49* | 49* | 76 | 97 | 97 | 97 |
冒泡排序的算法的代码如下:
void BubbleSort(int A[],int n){
int i ,int j ,flag =1,temp;
for(i=0;i<n-1;i++){
flag = 0;//表示本次冒泡是否发生交换的标志
for(j=n-1;j>i;j--){//一趟冒泡过程
if(A[j-1]>A[j]){//若为逆序
temp = A[j];//交换
A[j]=A[j-1];
A[j-1]=temp;
flag = 1;
}
if(flag ==0){
return;//本趟遍历中没有发生交换,说明表已经有序
}
}
}
}
冒泡排序性能分析:
1.空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1)
2.时间效率:当初始序列有序时,显然第一趟冒泡后flag依然为false(本趟冒泡没有元素交换),从而直接跳出循环,比较次数为n-1,移动次数为0,从而最好情况下的时间复杂度为O(n);当初始序列为逆序时,需要进行n-1趟排序,第i趟排序要进行n-i次关键字的比较,而且每次比较厚都必须移动元素3次来交换元素位置。最欢时间复杂度为O(n2),其平均时间复杂度也为O(n2)
3.稳定性:由于i>j,且a[i]=a[j]时,不会发生交换,因此冒泡排序是一种稳定的排序方法。
冒泡排序中所产生的有序序列一定时全局有序的(不同于直接插入排序),也就是说,有序子序列中的所有关键字一定小于或大于无序子序列中所有元素的关键字,这样每趟排序都会将一个元素放置其到最终位置。
快速排序
基本思想:
- 任取一个元素(如:第一个)为中心
- 所有比它小的元素一律前放,比它大的元素一律后放,形成左右俩个子表;
- 对各个子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个
通过一趟排序,将待排序记录分割成独立的俩个部分,其中一部分记录的关键字比另一部分记录的关键字小,则可分别对这俩部分记录进行排序,以达到整个序列有序。
具体实现:选定一个中间数作为参考,所有元素之比较,小的调到其左边,大的调到其右边。小的从左往右放大的从右往左放。
中间数:可以是第一个数,最后一个数,最中间一个数,任选一个数
- 每一趟子表的形成都是采用从两头向中间交替式逼急法
- 由于每趟中对各子表的操作都相似,可采用递归算法
算法:
#include <stdio.h>
#include <stdlib.h>
void swap(int k[],int low,int high){
int temp;
temp = k[low];
k[low] = k[high];
k[high] = temp;
}
int Partition(int k[],int low ,int high){
int point;
point = k[low];
while(low < high){
while (low<high&&k[high]>=point){
high--;
}
swap(k,low,high);
while (low<high&&k[low]<=point){
low++;
}
swap(k,high,low);
}
}
void QSort(int k[],int low ,int high){
int point;
if(low<high){
point = Partition(k,low,high);//partition划分
QSort(k,low,point-1);
QSort(k,point+1,high);
}
}
void QuickSort(int k[],int n ){
QSort(k,0,n-1);
}
int main()
{
int i ,a[10] = {5,2,6,0,3,9,1,7,4,8};
QuickSort(a,10);
printf("排序后的结果是:");
for(i = 0;i < 10 ;i++){
printf("%d",a[i]);
}
printf("\n\n");
return 0;
}
快速排序算法的性能分析:
空间效率:由于快速排序是递归的额,需要借助一个递归栈来保存每层递归调用的必要信息,其容量应与递归调用的最大深度一致。最好情况下为O(log2n),最坏情况下因为要进行n-1次递归调用,所以栈的深度为O(n);平均情况下,栈的深度为O(log2n)。
时间效率:快速排序的运行时间与划分是否对称有关,快速排序的最坏的情况发生在俩个区域分别包含n-1个元素和0个元素,这种最大限度的不对称发生在每层递归上,即对应于初始排序表基本有序或者基本逆序,就得到最坏情况下的时间复杂度O(n^2)。
有很多方法可以提高算法的效率:一种方法是尽量选取一个可以将数据中分的枢轴元素,如从序列中间选取三个元素,再取三个元素的中间值作为最终的枢轴元素;或者随机的从当前的表中选取枢轴元素,这样可以是最欢的请开给你在实际排序中几乎不会发生。
在最理想的情况下,即Partition()可能左到最平衡的划分,得到的俩个子问题的大小都可能大于n/2,在这种情况下,快速排序的运行速度将大大提升,此时时间复杂度为O(nlog2n),好在快速排序平均市况下的运行时间与其最佳情况下的运行时间很接近,而不是接近其最坏情况下的运行时间。快速排序是所有内部排序中平均性能最优的排序算法。
稳定性:在划分算法中,若右端区间有俩个关键字相同,且均小于基准值的记录,则在交换到左端区间后,它们的相对位置会发生变化,即快速排序是一种不稳定排序方法。
在快速排序算法中,并不产生有序子序列,但每趟排序后将枢轴(基准)元素放到最终的位置上。