快速排序的两种实现方法(c语言版本)
經過調研發現,對任意無序整數數組,快速排序有兩種實現方法,這里簡單闡述下思路:
思路一:隨意選擇一個基準元,一般選擇數組的起始元或末尾元,Weiss這本書上特意搞了個算法來選擇基準元,……,總之就是基準元的選擇要盡量隨機。選定基準元之后,比如選擇數組起始元為基準元,從數組右邊開始,向左邊遍歷,遇到比基準元大的跳過,直至遇到比基準元小的元素停下來;再從左邊向右邊遍歷,跳過比基準元小的,直至遇到比基準元大的元素停下來,交換左右兩邊的元素,再重復之前的遍歷,比較和交換步驟,……,這樣經過一趟排序就能將整個數組劃分為兩個部分,左邊的部分小于基準元,右邊的部分大于基準元,使用“二分法”和遞歸的思想,我們就能最終快速完成整個數組的排序,借用網上的效果動態如下:
啊哈磊的這篇文章對快速排序的思想做了非常深入淺出的描述,感興趣的讀者可以看看
http://bbs.ahalei.com/thread-4419-1-1.html
思路二:在不方便從數組最右端向最左端遍歷的情形下,比如超長單向鏈表只有next指針,只能往前遍歷不能往后遍歷,此時還有一種變通方法,就是,選擇數組的首個元素為基準元,使用兩個索引,一快一慢,索引1從首個元素的前一個開始(索引為-1),索引2遍歷數組每個元素,發現比基準元小的元素,就讓索引1增加1,如果發現兩個索引不相等,就交換兩個對應兩個元素的值,這樣的效果等價于把數組分為兩部分,比基準元大的部分在基準元的右邊,比基準元小的部分在基準元的左邊,同樣按照“二分法”和遞歸思想,最后完成整個數組的排序。
下面給出這兩種思路的算法實現源碼
//description: 給出快速排序的兩種實現形式
//date: 2019-03-16#include <stdio.h>
#include <stdlib.h>int RandomInRange(int min, int max){int random = rand()%(max-min+1) + min;return random;
}int swap(int* a, int* b){int tmp = *b;*b = *a;*a = tmp;
}//利用三點確定基準點
int Median3(int arr[], int left, int right){int center = (left+right)/2;//使left,center,right升序if(arr[left]>arr[center])swap(&arr[left], &arr[center]);if(arr[left]>arr[right])swap(&arr[left], &arr[right]);if(arr[center]>arr[right])swap(&arr[center], &arr[right]);//隱藏基準點swap(&arr[center], &arr[right-1]);return arr[right-1];
}int Partition(int arr[], int n, int start, int end){if(arr==NULL || n<=0 || start<0 || end >= n){printf("Invalid input params, exit ...");exit(-1);}//生成隨機索引作為基準元素,并放到數組尾部int idx = RandomInRange(start, end);swap(&arr[idx], &arr[end]);//現在arr[end]就是基準元素了//設置兩個索引, small表示比基準元素小的元素的索引int small = start - 1;for(idx=start; idx<end; idx++){if(arr[idx]<arr[end]){small++;//small指向比基準元素大的,idx指向比基準元素小的,兩者交換一下if(small != idx)swap(&arr[small], &arr[idx]);}}//將基準元素交換回來small++;swap(&arr[small], &arr[end]);return small;
}//借用一段插入排序算法
void InsertSort(int arr[], int n){int i,j,t;for(i=1; i<n; i++){t=arr[i];for(j=i; j>0 && t<arr[j-1]; j--){arr[j] = arr[j-1];}arr[j]=t;}
}#define CutOff 3void Qsort(int arr[], int left, int right){int i,j,pivot;if(left+CutOff <= right){pivot = Median3(arr, left, right);i=left;j=right-1;for(;;){while(arr[++i]<pivot){}while(arr[--j]>pivot){}if(i<j)swap(&arr[i], &arr[j]);elsebreak;}//恢復pivotswap(&arr[i], &arr[right-1]);Qsort(arr, left, i-1);Qsort(arr, i+1, right);}else{//對子數組做插入排序InsertSort(arr+left, right-left+1);}
}//找數組中第k大的元素,即arr[k-1]元素
void Qselect(int arr[], int k, int left, int right){int i,j,pivot;if(left+CutOff <= right){pivot = Median3(arr, left, right);i=left;j=right-1;for(;;){while(arr[++i]<pivot){}while(arr[--j]>pivot){}if(i<j)swap(&arr[i], &arr[j]);elsebreak;}//恢復pivotswap(&arr[i], &arr[right-1]);if(k<=i)Qselect(arr, k, left, i-1);else if(k>i+1)Qselect(arr, k, i+1, right);}else{//對子數組做插入排序InsertSort(arr+left, right-left+1);}
}void QuickSort2(int arr[], int n){Qsort(arr, 0, n-1);
}void QuickSort(int arr[], int n, int start, int end){if(start == end)return;int pivot = Partition(arr, n, start, end);if(pivot>start)QuickSort(arr, n, start, pivot-1);if(pivot<end)QuickSort(arr, n, pivot+1, end);
}int main(){int a[] = {4,12,35,98,4,44,58,13,15};int i, n=sizeof(a)/sizeof(int);//QuickSort(a, n, 0, n-1);//QuickSort2(a, n);Qselect(a, n/2, 0, n-1);printf("%d\n", a[n/2]);for(i=0; i<n; i++)printf("%d ", a[i]);printf("\n");return 0;
}
下面是函數運行效果截圖
?
引申
使用快速排序的思想,還可以高效解決“查找第k大數”的問題,特別是“查找數組中位數”的問題,這是后話,打算今后另做一篇博文分析。
求無序數組的中位數(c語言版本)
?
參考文獻
[1]. Allen Weiss 《數據結構與算法分析---C語言描述》第2版第七章
[2].何海濤 《劍指Offer名企面試官精講典型編程題》第2版p.80
總結
以上是生活随笔為你收集整理的快速排序的两种实现方法(c语言版本)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 删除单链表中的重复节点(c语言版本)
- 下一篇: 数据结构与算法常用名词术语整理