一. 交换排序基本思想
交换是指根据序列中两个元素关键字的比较结果来对这两个记录在序列中的位置进行交换。
二. 冒泡排序
2.1 基本思想
从后往前(或从前往后),两两比较相邻的元素的值,若为逆序,则交换他们,直到序列比较完,称它为第一趟冒泡,结果是将最小的元素交换到序列的第一个位置(或将最大的元素交换到序列的最后一个位置),关键字最小的元素如同气泡一样网上漂浮直至水面(或关键字最大的元素像石头一样沉至水底),下一趟冒泡时,第一趟排好序的元素不在参与比较,每趟的结果是把序列中最小(大)的元素放在了最终的位置,这样进行n-1趟冒泡就可以把所有的元素排好序,因为最后一个元素一定时有序的。
2.2 C++代码
void BubbleSort(ElemType A[] , int n)
{
for(i=0; i<n-1; i++)
{
flag=false; //表示本趟冒泡是否发生交换的标志
for(j=n-1; j>i; j--) //一趟冒泡过程
{
if(A[j]<A[j-1])
{
swap(A[j] , A[j-1] );//交换
flag=true;
}
}
if(flag==false) //本趟遍历没有发生交换,说明序列已经有序。
return;
}
}
2.3 性能分析
- 空间效率:空间复杂度为O(1)。
- 时间复杂度:
- 最好情况下,在序列已经有序时,第一趟冒泡后flag=false退出排序,比较次数为n-1次,交换0次,所以时间复杂度为O(n)。
- 最坏情况下,在序列逆序时,需要进行n-1趟排序:第一趟要进行n-1次比较,每次比较之后都需要移动三次元素来交换位置,所以最坏情况下时间复杂度为O(n²)。
- 平均情况下,时间复杂度也为O(n²)。
三. 快速排序
3.1 基本思想
快速排序的基本思想时基于分治法的:在待排序表L[ 1 ... n ] 中任取一个元素pivot作为基准(通常取首元素),通过一趟排序将待排序表分为两个部分L[ 1 ... k-1 ] 和 L[ k+1 ... n ],其中L[ 1 ... k-1 ] 中的所有元素小于pivot, L[ k+1 ... n ]中的所有元素大于pivot,则pivot放在了其最终位置L( k )上,这个过程称为一趟快速排序(或一次划分)。然后分别递归的对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素都放在了其最终的位置上。
3.2 C++代码
一趟快速排序的过程是一个交替搜索和交换的过程。
void QuickSort(ElemType A[] , int low . int high) //low:第一个元素。high:最后一个元素,即A[low ... high]。默认以第一个元素为基准
{
if(low<high) //跳出递归的条件
{
int pivotpos=Partition(A , low , high); //划分
QuickSort(A , low , pivotpos-1); //对两个子表进行递归排序
QuickSort(A , pivotpos+1 , high);
}
}
int Partition(ElemType A[] , int low , int high) //一趟划分
{
ElemType pivot=A[low]; //将第一个元素设为基准
while(low<high)
{
while( low<high && A[high]>=pivot )--high;
A[low]=A[high]; //将比基准小的元素移动到左端
while( low<high && A[low]<=pivot )++low;
A[high]=A[low]; //将比基准大的元素移动到右端
}
A[low]=pivot; //将基准元素放在最终位置
return low; //返回存放基准的最终位置
}
3.3 性能分析
- 空间效率:快速排序是递归的,所以需要递归调用栈,其容量应与递归调用的最大深度一致。
- 最好情况下为O(log₂n)。
- 最坏情况下,要进行n-1次递归调用,所以栈的深度为O(n)。
- 平均情况下,栈的深度为O(log₂n)。
- 时间效率:快排的运行时间与划分是否对称有关。
- 最坏情况下,即两个区域分别包含n-1个元素和0个元素时,这种最大程度的不对称性若发生在每层递归上,即对应于初始排序表基本有序或基本逆序时就得到最坏情况下时间复杂度O(n²)。*
- 最好情况下,即能做到平衡划分,时间复杂度为O(nlog₂n)。
- 快排平均情况下运行时间与其最佳情况下的运行时间很接近,而不是接近最坏的运行情况,因而
快排是内部排序中平均性能最优的排序算法。
- 稳定性:快排是不稳定的排序算法。