这里不写书面的官方的话语,均是我个人的理解。
列举三种排序:冒泡排序、直接插入排序和快速排序。
10个元素从小到大排序,待排序数组为:
int arr[]={55,2,6,4,32,12,9,73,26,37};
int len = sizeof(arr)/sizeof(int); // 计算长度
冒泡排序(改进版)
算法思想:简单来说,从前往后逐个比较相邻元素,保证后一个元素比前一个元素大。每一轮都选出了一个当前待排序元素中的最大值置于底部(数组末端),且这就是该元素的最终位置。每经过一轮,就确定一个元素的位置,所以待排序元素个数就减1。第i轮中,要在待排序的n-(i-1)个元素中选一个最大值。n个元素,至多需要n-1轮比较。
改进之处,就是增加一个flag变量,用来标记该轮是否发生元素交换。若没有发生,表示每一个元素都比前一个元素大,则排序完成。
void bubble(int arr[], int len){
int flag = 1;
for(int times = len-1; times > 0; times--){
for(int i = 0; i<times; i++){
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
flag = 0;
}
}
for(int i =0; i<len; i++){ // 输出每一趟的排序结果
cout<<arr[i]<<",";
}
cout<<endl;
if(flag)
return;
else
flag = 1;
}
}
改进的效果:对于本文所设数组来说,如果不改进,10个元素需要比较9轮。改进后仅需4轮。
运行结果如下 :
直接插入排序
算法思想:该算法运行过程中,会使待插入元素的左侧为有序序列。第一个元素已有序,因此直接从第二个元素开始“插入”操作。待插元素ins与左侧元素a[i]从右向左逐个比较,当a[i]大于ins时,a[i]后挪(注意不是交换);直至a[i]<ins,即确定ins的位置。
void insert(int a[],int len){
// 待插元素不断与左侧元素比较,待插元素较小则交换
for(int i =1; i<len; i++){
// index指向待插入元素左侧紧邻的元素
int index = i-1,ins = a[i];
for(; index >= 0 && a[index] > ins; index--){ // 将i之前的元素逐个与a[i]进行比较
// 一定要注意for循环的终止条件
a[index+1] = a[index]; // 把大元素a[index]后挪一个位置
}
a[index+1] = ins; // 插入
for(int i =0; i<len; i++){ // 输出每一趟的排序结果
cout<<a[i]<<",";
}
cout<<endl;
}
}
运行结果如下 :
快速排序
算法思想:采用分治思想,选定一个枢轴元素为基准,比他小的元素放在左侧,比它大的元素放在右侧。因此,每一趟都至少能确定所选枢轴元素在序列中的最终位置。
这里贴一个助于理解的表:
代码实现如下:
void quick(int a[],int low, int high){
int pivot = a[low]; // 取第一个元素为枢轴元素
int i=low, j = high; // 两个指针
while(i<j){ // 通过指针移动,将元素从两端逐个与枢轴元素比较
while(a[j] >= pivot && i<j) j--;
a[i] = a[j]; // 从右侧找到第一个小于枢轴元素的元素,放至左侧
while(a[i] <= pivot && i<j) i++;
a[j] = a[i]; // 从左侧找到第一个大于枢轴元素的元素,放至右侧
}
a[i] = pivot; // 此时i=j,将枢轴元素放入序列
// 递归调用,对左右两个子序列再进行划分,直至每个子序列内只有一个元素或空为止
if(i-low > 1 || high - i > 1){
quick(a,low,i);
quick(a,i+1,high);
}
}
运行结果如下 :
时空复杂度:
分析过程全网都有(想看分析过程请留言),在这直接贴结果: