交换类排序之快速排序
交换类排序
- 所谓交换,是指根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置。
- 基于交换的排序算法很多,主要介绍冒泡排序和快速排序。
快速排序(Quick Sort)
- 背景:由冒泡排序改进而得。
- 冒泡排序只对相邻的两个记录进行比较,每次交换两个相邻记录只能消除一个逆序。
- 快速排序一次交换消除多个逆序。通过两个(不相邻)记录的一次交换,消除多个逆序,加快排序的速度。
- 快速排序是所有内部排序算法中平均性能最优的排序算法(O(nlog2n))。
- 核心思想:通过多次划分操作实现排序。以升序为例,每一趟选择当前所有子序列中的一个关键字(默认选第一个)作为枢轴,将子序列中比枢轴小的移到枢轴左边,比枢轴大的移到枢轴右边。当本趟所有子序列都被枢轴以上述规则划分完毕后会得到新的一组更短的子序列。
算法步骤
补充
- i++和++i的效率问题。CodeBlocks反汇编窗口。
时间复杂度
- 1.最好时间复杂度:O(nlog2n)。
待排序列越接近无序,该算法效率越高
在最理想的状态下,即做到最平衡的划分,得到的两个子问题的大小都不可能大于n/2,在这种情况下,快速排序的运行速度将大大提升,此时,时间复杂度为O(nlog2n)。 - 2.最坏时间复杂度:O(n2)
待排序列越接近有序,该算法效率越低
快速排序的运行时间与划分是否对称有关,快速排序的最坏情况发生在两个区域分别包含n- 1个元素和0个元素时,这种最大限度的不对称性若发生在每层递归上,即对应于初始排序表基本有序或基本逆序时,就得到最坏情况下的时间复杂度为0(n2)。 - 3.平均时间复杂度:O(nlog2n)
空间复杂度
- 1.空间复杂度:O(log2n)。
由于快速排序是递归的,需要借助一个递归工作栈来保存每层递归调用的必要信息,其容量与递归调用的最大深度一致。
稳定性
代码示例
#include <iostream>
using namespace std;
int cnt=0;
///快速排序
void quickSort(int R[],int low,int high)
{
if(low<high)
{
int temp=R[low];
int i=low,j=high;
while(i<j)
{
while(j>i && R[j]>=temp) j--;//从右向左扫描,找到第一个小于temp的关键字
if(i<j)
{
R[i]=R[j];//放到temp左边
i++;
}
while(i<j&&R[i]<=temp) i++;//从左向右扫描,找到第一个大于temp的关键字
if(i<j)
{
R[j]=R[i];//放到temp右边
j--;
}
}//一趟排序后枢轴元素temp到达最终位置,左边元素比temp小,右边元素比temp大
R[i]=temp;
cout<<++cnt<< ": ";
for(int i=0; i<8; i++)
{
cout<<R[i]<< " ";
}
cout<<endl;
quickSort(R,low,i-1);//依次对两个子序列进行递归排序
quickSort(R,i+1,high);
}
}
int main()
{
int a[8]= {49,38,65,97,76,13,27,49};
cout<<"before: ";
for(int i=0; i<8; i++)
{
cout<<a[i]<< " ";
}
cout<<endl;
quickSort(a,0,7);
cout<<"after: ";
for(int i=0; i<8; i++)
{
cout<<a[i]<< " ";
}
return 0;
}
运行结果
和冒泡排序对比
冒泡排序(Bubble Sort)
- 核心思想:从左至右依次两两比较,若发生逆序,则进行交换,小气泡漂浮(小关键字左移),大气泡下沉(小关键字右移)。一趟冒泡排序后,待排序关键字中的最大值下沉到待排序关键字中的最后一个,到达最终位置,成为已排序关键字的第一个。若一趟比较中没有发生逆序,则冒泡排序结束
特点:只对相邻的两个记录进行比较,每次交换两个相邻记录只能消除一个逆序(不足) - 时间复杂度:最坏和平均时间复杂度O(n^2),最好时间复杂度O(n)
- 空间复杂度:O(1) 。
快速排序(Quick Sort)
- 背景:由冒泡排序改进而得。
- 核心思想:通过多次划分操作实现排序。以升序为例,每一趟选择当前所有子序列中的一个关键字(默认选第一个)作为枢轴,将子序列中比枢轴小的移到枢轴左边,比枢轴大的移到枢轴右边。当本趟所有子序列都被枢轴以上述规则划分完毕后会得到新的一组更短的子序列
- 特点:一次交换消除多个逆序。通过两个(不相邻)记录的一次交换,消除多个逆序,加快排序的速度。
- 时间复杂度:平均和最好时间复杂度:O(nlog2n) ,最坏时间复杂度O(n2)
- 空间复杂度:空间复杂度:O(log2n)。
思考问题
- 1.为什么先从右往左而不是从左往右?
-
- if(i<j)
{
R[i]=R[j];
i++;/j–;
}中的
if(i<j) 和i++/j–可以省去吗?
- if(i<j)
- 3.如何改为降序?
- 4.最好时间复杂度:O(nlog2n)怎么算?
- 5…
应用
题目描述
有N个学生的数据,将学生数据按成绩从低到高排序,如果成绩相同则按姓名字符的字典序由小到大排序,如果姓名的字典序也相同则按照学生的年龄从小到大排序,并输出N个学生排序后的信息。
输入描述:
测试数据有多组,每组输入第一行有一个整数N(N<=1000),接下来的N行包括N个学生的数据。 每个学生的数据包括姓名(长度不超过100的字符串)、年龄(整形数)、成绩(小于等于100的正数)。
输出描述:
将学生信息按成绩进行排序,成绩相同的则按姓名的字母序进行排序。 然后输出学生信息,按照如下格式: 姓名 年龄 成绩 学生姓名的字母序区分字母的大小写,如A要比a的字母序靠前(因为A的ASC码比a的ASC码要小)。
示例1
输入
3
abc 20 99
bcd 19 97
bed 20 97
输出
bcd 19 97
bed 20 97
abc 20 9
提示:利用STL库中的sort()函数并实现比较规则。
评测平台:
https://www.nowcoder.com/practice/7a2f7d304d9e43b1bb2a6e72ed65bf51?tpId=40&rp=1&ru=%2Fta%2Fkaoyan&qru=%2Fta%2Fkaoyan%2Fquestion-ranking&tab=answerKey