zoukankan      html  css  js  c++  java
  • 再看冒泡排序和快速排序

    对于冒泡排序,大家肯定都熟知,每一轮的冒泡都将最大的数排到最前面,每一轮的时间复杂度是O(n),如果要排序的数组大小为n,要经过n轮才能将数组中所有元素排序,所以总共的时间复杂度为O(n2)。

    关于冒泡排序的源码如下:

    迭代型冒泡排序
    #include <stdio.h> #define length(array) sizeof(array)/sizeof(array[0]) #define true 1 #define false 0 void BubbleSort(int *a, int len) //待排数组a以及它的长度len { int ordered = false; int temp, i; while(len && !ordered) { ordered = true; //也许一趟排序过程中没有发生交换,说明数组已有序 for(i = 1; i < len; i++) //此时i从1开始比从0开始更好 { if(a[i-1] > a[i]) { ordered = false; temp = a[i]; a[i] = a[i-1]; a[i-1] = temp; } } len--; } } int main() { int a[5] = {5,1,2,3,4}; int i = 0; int len = length(a); BubbleSort(a,len); for(i = 0; i < len; i++) { printf("%d ",a[i]); } return 0; }

    对 于快速排序,选出一个枢纽元素,然后将这个枢纽元素和数组所有元素进行比较,把彼枢纽元素大的元素放在枢纽元素右边,把比枢纽元素小的放在枢纽元素左边, 而对于一趟快速排序要比较n次,每一趟快排的时间复杂度是O(n),接下来你要对快排划分出来的两个子数组进行递归快排,如果每一趟快排很平均的将数组划分为两个子数组,那么递归快排的深度就是O(lgn),所以总的时间复杂度就是O(nlgn)。但是快排可以退化成冒泡,如果一旦每一趟快排,不幸的选择出最大或最小元素作为枢纽元素,那么递归深度将变成n,则时间复杂度变成了O(n2),此时快排的效率降到最低,退化为冒泡,所以快排对于枢纽元素的选择上很关键,如果能选择出每趟平均划分数组的枢纽元素,那么快排的效率最高,如何选择枢纽元素将成为衡量快排的关键,我推荐使用三者取中法来选择,每趟快排前,先将数组开始位置,中间位置,以及结尾位置的三个元素进行比较,选择其中的中间大的数做为枢纽元素,这样可以降低退化成冒泡的风险,也可以使用任取一个数做为枢纽元素,这样也可以降低风险。

    我准备开始写递归型的冒泡排序和递归型的快速排序,你会发现两者的区别,以及为什么快排会比冒泡好的原因,以及如何将程序从冒泡写成快排。

    递归型冒泡排序
    #include <stdio.h> #define length(array) sizeof(array)/sizeof(array[0]) #define true 1 #define false 0 int Sort(int *a, int len) { int ordered = true; //也许一趟排序过程中没有发生交换,说明数组已有序 int temp, i; for(i = 1; i < len; i++) //由此可以看出Sort()的时间复杂度是O(n),跟待排数组的长度有关。 { if(a[i-1] > a[i]) { ordered = false; temp = a[i]; a[i] = a[i-1]; a[i-1] = temp; } } return ordered; } void BubbleSort(int *a, int len) //冒泡排序的递归算法 { if(len == 1) //如果只有一个元素,它就是最大的元素,无须在冒泡 return; else { int ordered = Sort(a,len); //选出最大的元素,将所有比最大元素小的元素放在最大元素的左边,而快排是将使用一个枢纽元素,将比枢纽元素大的放在枢纽右边,小的放在左边 if(ordered) //如果一趟冒泡,没有交换元素,说明已有序 return; BubbleSort(a,len-1); //递归冒泡出最大元素后面的所有元素,如果快排将作为快排的左递归 //如果快排会有右递归 } } int main() { int a[10] = {10,1,5,2,4,3,6,7,9,8}; int i = 0; int len = length(a); BubbleSort(a,len); for(i = 0; i < len; i++) { printf("%d ",a[i]); } return 0; }
    递归快速排序
    #include <stdio.h> #define length(array) sizeof(array)/sizeof(array[0]) #define true 1 #define false 0 int Sort(int *a, int low, int high) { int pivot = a[low]; //这里每次的枢纽元素都取了待排数组的第一个元素,记住是a[low],而不是a[0] if(low < high) //时间复杂度是O(n),n是数组长度 { while(a[high] >= pivot && low < high) high --; a[low] = a[high]; while(a[low] <= pivot && low <high) low ++; a[high] = a[low]; } a[low] = pivot; return low; } void QuickSort(int *a, int low, int high) { if(low >= high) return ; else { int mid = Sort(a,low,high); //划分子递归数组 QuickSort(a,low,mid-1); //左递归 QuickSort(a,mid+1,high); //右递归,一旦右递归mid+1=high,将退化成冒泡,递归深度将变成n,n为数组长度 } } int main() { int a[5] = {3,1,5,2,4}; int i = 0; int len = length(a); QuickSort(a,0,len-1); for(i = 0; i < len; i++) { printf("%d ",a[i]); } return 0; }

    由上可以看出,递归型的冒泡就是一个只有右子树的递归树,递归深度为O(n),而好的快速排序,将产生一个平衡的二叉树,递归深度为O(lgn),所以快排是对冒泡的一个巨大的改进。

  • 相关阅读:
    PAT B1045 快速排序 (25 分)
    PAT B1042 字符统计 (20 分)
    PAT B1040 有几个PAT (25 分)
    PAT B1035 插入与归并 (25 分)
    PAT B1034 有理数四则运算 (20 分)
    PAT B1033 旧键盘打字 (20 分)
    HDU 1231 最大连续子序列
    HDU 1166 敌兵布阵
    HDU 1715 大菲波数
    HDU 1016 Prime Ring Problem
  • 原文地址:https://www.cnblogs.com/GODYCA/p/2842262.html
Copyright © 2011-2022 走看看