[C/C++基础知识] 一篇就让你彻底搞懂qsort快速排序的文章
? ? ? ? 最近在做LeetCode的題目、面試和筆試后發現經??疾炜焖倥判虻闹R。通過這篇文章介紹,能讓你徹底的了解和學習快排,主要從一下三個部分進行介紹:
? ? ? ? 一.C語言實現qsort快速排序
? ? ? ? 二.快速排序的原理及手寫快排源碼
? ? ? ? 三.LeetCode關于Two Sum的快排實現
參考文獻:
? ? ? ?《算法分析與設計》關于分治法那章內容
? ? ? ? 如何利用C語言中的qsort庫函數實現快速排序 - by:stpeace
? ? ? ? 白話經典算法系列之六 快速排序 快速搞定 - by:MoreWindows
? ? ? ? https://leetcode.com/problems/two-sum/
? ? ? ??[leetcode] Two Sum ?by:陽光島主
? ? ? ?
一. C語言實現qsort快速排序
? ? ? ? 這段介紹參考百度百科,編譯器函數庫自帶的快速排序函數qsort。使用qsort()排序并用 bsearch()搜索是一個比較常用的組合,使用方便快捷。
? ? ? ? 頭文件:stdlib.h
? ? ? ? 用 法: void qsort(void *base, int nelem, int width, int (*fcmp)(const void *, const void *));
? ? ? ? 參數: 1 待排序數組首地址
? ? ? ? ? ? ? ? ? ?2 數組中待排序元素數量
? ? ? ? ? ? ? ? ? ?3 各元素的占用空間大小
? ? ? ? ? ? ? ? ? ?4 指向函數的指針,用于確定排序的順序
?
? ? ? ??整數快排
? ? ? ??int cmp1(const void *a, const void *b) ?
? ? ? ? { ?
? ? ? ? ? ? ? ?return *(int*)a - *(int*)b; ?
? ? ? ? }?
? ? ? ? qsort(num, len, sizeof(int), cmp1);
?
#include <stdio.h> #include <stdlib.h> //整數從小到大排序 int cmp1(const void *a, const void *b) { return *(int*)a - *(int*)b; } //整數從大到小排序 int cmp2(const void *a, const void *b) { return *(int*)b - *(int*)a; } int main() { int num[8] = {4,11,2,-3,15,4,0,7}; int len = 8; int i; printf("整數從小到大排序:\n"); qsort(num, len, sizeof(int), cmp1); for(i = 0; i < len; i ++) { printf("%d ", num[i]); } printf("\n\n"); printf("整數從大到小排序:\n"); qsort(num, len, sizeof(int), cmp2); for(i = 0; i < len; i ++) { printf("%d ", num[i]); } printf("\n"); system("PAUSE");return 0; }? ? ? ? 運行結果如下圖所示:
? ? ? ? 二維數組快排
? ? ? ??int cmp(const void *a, const void *b) ?
? ? ? ? { ?
? ? ? ? ? ? ?return ((int*)a)[0] - ((int*)b)[0]; ?
? ? ? ? }
? ? ? ? qsort(num, len, sizeof(int)*2, cmp);
?
#include <stdio.h> #include <stdlib.h> //二維數組按照num[i][0]從小到大排序 int cmp(const void *a, const void *b) { return ((int*)a)[0] - ((int*)b)[0]; } int main() { int num[6][2] = {4, 1,11, 2,2, 3,-3, 4,15, 5,0, 7,}; int len = 6; int i; printf("排序前:\n"); for(i = 0; i < len; i ++) { printf("%4d %4d\n", num[i][0], num[i][1]); } printf("\n"); qsort(num, len, sizeof(int)*2, cmp); printf("排序后:\n"); for(i = 0; i < len; i ++) { printf("%4d %4d\n", num[i][0], num[i][1]); } printf("\n"); system("PAUSE");return 0; }? ? ? ? 運行結果如下圖所示,其中num[i][0]和num[i][1]同時移動。
?
?
? ? ? ??字符串快排
? ? ? ? int cmp(const void *a, const void *b) ?
? ? ? ? { ?
? ? ? ? ? ? ?return *(char*)a - *(char*)b; ?
? ? ? ? } ??
? ? ? ? qsort(str, sum, sizeof(char)*10, cmp);
?
#include <stdio.h> #include <stdlib.h> int cmp(const void *a, const void *b) { return *(char*)a - *(char*)b; } int main() { char str[7][10] = {"Monday","Tuesday","Wednesday","Thursday","Friday","Staturday","Sunday"}; int sum = 7; int i; printf("排序前:\n"); for(i = 0; i < sum; i++) { printf("%s\n", str[i]); } printf("\n"); qsort(str, sum, sizeof(char)*10, cmp); printf("排序后:\n"); for(i = 0; i < sum; i++) { printf("%s\n", str[i]); } printf("\n"); system("PAUSE");return 0; }? ? ? ? 同int類型一樣,輸出如下所示:
?
? ? ? ??double快排
? ? ? ??int cmp(const void *a, const void *b) ?
? ? ? ? { ?
? ? ? ? ? ? return *(double*)a > *(double*)b ? 1 : -1; ?
? ? ? ? } ?
? ? ? ? qsort(num, sum, sizeof(double), cmp);?
?
#include <stdio.h> #include <stdlib.h> int cmp(const void *a, const void *b) { return *(double*)a > *(double*)b ? 1 : -1; } int main() { double num[7] = {12.2324,-4.3457,0.00000,5.64375,1.25879,0.00001,7.85435}; int sum = 7; int i; printf("排序前:\n"); for(i = 0; i < sum; i++) { printf("%10f\n", num[i]); } printf("\n"); qsort(num, sum, sizeof(double), cmp); printf("排序后:\n"); for(i = 0; i < sum; i++) { printf("%10f\n", num[i]); } printf("\n"); system("PAUSE");return 0; }? ? ? ? 輸出如下圖所示:
?
? ? ? ??結構體排序
? ? ? ? int compare1(const void *a,const void *b) ?
? ? ? ? { ?
? ? ? ? ? ? return ((Student*)a)->score - ((Student*)b)->score; ?
? ? ? ? }
? ? ? ? qsort(s, N, sizeof(Student), compare1);
?
#include <stdio.h> #include <stdlib.h> #define N 6 typedef struct { char name[15]; int score; }Student; int compare1(const void *a,const void *b) { return ((Student*)a)->score - ((Student*)b)->score; } int compare2(const void *a,const void *b) { return *(((Student*)a)->name) - *(((Student*)b)->name); } int main() { Student s[N] = { "Zhang San", 94, "Li Si", 80, "You", 94, "I", 100, "He", 72, "She", 60 }; int i; printf("按照分數排序:\n");qsort(s, N, sizeof(Student), compare1); for(i = 0; i < N; i++) { printf("%-15s : %d\n", s[i].name, s[i].score); } printf("\n"); qsort(s, N, sizeof(Student), compare2); printf("按照姓名排序:\n");for(i = 0; i < N; i++) { printf("%-15s : %d\n", s[i].name, s[i].score); } system("PAUSE");return 0; }? ? ? ? 代碼輸出如下圖所示:
?
?
?
二. 快速排序原理及手寫快排源碼
? ? ? ? 由于MoreWindows寫的白話文系列太好,所以這部分主要參考他的文章和《算法設計與分析》關于分治法那章內容。強烈推薦大家閱讀系列文章,原文鏈接:
? ? ? ??http://blog.csdn.net/morewindows/article/details/6684558
? ? ? ? 分治法的思想將一個大問題分割成小規模問題各個擊破,分而治之,常用遞歸解決。常用的包括二分查找、歸并排序和快速排序等應用。其中思想如下所示:
? ? ? ? divide-and-conquer(P)
? ? ? ? {
? ? ? ? ? ? ? ? ? if(|P|<=n0) adhoc(p); ? ? ? ? ? ? //解決小規模問題
? ? ? ? ? ? ? ? ? ? ? ? ? ? divide P into smaller subinstances P1,P2…Pk;
? ? ? ? ? ? ? ? ? for(i=1; i<=k; i++) ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ? yi = divide-and-conquer(Pi); //分解
? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? return merge(y1,y2…yk); ? ? ? ? //合并子算法
? ? ? ? }
? ? ? ? 關于快速排序,由于它的時間復雜度在O(N*logN)效率較高,經常使用。該算法的基本思想:
? ? ? ? 1. 先從數列中取出一個數作為基準數。
? ? ? ? 2. 分區過程,將比這個數大的全放到它右邊,小于或等于它的全放到它的左邊。
? ? ? ? 3. 再對左右區間重復第二步,直到各區間只有一個數。
? ? ? ? MoreWindows對快速排序作了進一步的說明:挖坑填數+分治法?
? ? ? ? 以一個數組作為示例,取區間第一個數為基準數。
? ? ? ? 初始時,i = 0;?j = 9;? ?X = a[i] = 72
? ? ? ? 由于已經將a[0]中的數保存到X中,可以理解成在數組a[0]上挖了個坑,可以將其它數據填充到這來。
? ? ? ? 從j開始向前找一個比X小或等于X的數。當j=8,符合條件,將a[8]挖出再填到上一個坑a[0]中。a[0]=a[8]; i++;? 這樣一個坑a[0]就被搞定了,但又形成了一個新坑a[8],這怎么辦了?
? ? ? ? 簡單,再找數字來填a[8]這個坑。這次從i開始向后找一個大于X的數,當i=3,符合條件,將a[3]挖出再填到上一個坑中a[8]=a[3]; j--; 數組變為:
? ? ? ? i = 3;?? j =7;?? X=72
? ? ? ? 再重復上面的步驟,先從后向前找,再從前向后找。
? ? ? ??從j開始向前找,當j=5,符合條件,將a[5]挖出填到上一個坑中,a[3] = a[5]; i++;
? ? ? ? 從i開始向后找,當i=5時,由于i==j退出。此時,i = j = 5,而a[5]剛好又是上次挖的坑,因此將X填入a[5]。數組變為:
? ? ? ? 可以看出a[5]前面的數字都小于它,a[5]后面的數字都大于它。因此再對a[0…4]和a[6…9]這二個子區間重復上述步驟就可以了。
? ? ? ? 對挖坑填數進行總結:
? ? ? ? 1. i =L; j =R; 將基準數挖出形成第一個坑a[i]。
? ? ? ? 2. j--由后向前找比它小的數,找到后挖出此數填前一個坑a[i]中。
? ? ? ? 3. i++由前向后找比它大的數,找到后也挖出此數填到前一個坑a[j]中。
? ? ? ? 4. 再重復執行2,3二步,直到i==j,將基準數填入a[i]中。
? ? ? ? 照著這個總結很容易實現挖坑填數的代碼:
? ? ? ? 分治法:
? ? ? ?挖坑填數:
int AdjustArray(int s[], int l, int r) //返回調整后基準數的位置 { int i = l, j = r; int x = s[l]; //s[l]即s[i]就是第一個坑 while (i < j) { // 從右向左找小于x的數來填s[i] while(i < j && s[j] >= x) j--; if(i < j) { s[i] = s[j]; //將s[j]填到s[i]中,s[j]就形成了一個新的坑 i++; } // 從左向右找大于或等于x的數來填s[j] while(i < j && s[i] < x) i++; if(i < j) { s[j] = s[i]; //將s[i]填到s[j]中,s[i]就形成了一個新的坑 j--; } } //退出時,i等于j。將x填到這個坑中。 s[i] = x; return i; }? ? ? ?PS:去一些大公司面試,經常會讓你手寫快排、二叉樹非遞歸遍歷、鏈表翻轉等
?
三. LeetCode關于Two Sum的快排實現
? ? ? ? 下面講一個經典的題目。做過LeetCode的肯定做個這個題目,LeetCode的第一題Two Sum,求兩個數的和。鏈接:https://leetcode.com/problems/two-sum/
Given an array of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input:?numbers={2, 7, 11, 15}, target=9
Output:?index1=1, index2=2
? ? ? ? 找到兩個數字和為target,并輸出兩個值的下標。由于數組可能是無序狀態,故需要排序后輸出即可 。但你需要注意一下幾點:
? ? ? ? 1.首先想到的方法是兩層循環遍歷普通排序,時間復雜度O(N^2)會TLE
? ? ? ? 2.輸出下標不是從0開始,故i+1即可
? ? ? ? 3.輸出時負數需要判斷小的在前
? ? ? ? ? ?易錯:Input:[-1,-2,-3,-4,-5] -8 ?Output:[5,3] ?Expected:[3,5]
? ? ? ? 4.錯誤 Input:[3,2,4] 6 ?Output:[1,3] ?Expected:[2,3]輸出原始位置,所以使用快速排序怎樣解決呢?方案一采用結構,方案二采用二維數組調用qsort:
? ? ? ? ? ? ? (1)先定義結構NODE存儲<值,下標>在進行qsort排序
? ? ? ? ? ? ? (2)再一層循環前后兩個指針移動遍歷求和,時間復雜度O(N*logN)
? ? ? ? ?由于結果唯一,可以通過左右下標移動實現,如果sum<target移動左下標
? ? ? ? 5.通過Hash實現時間復雜度O(n)
? ? ? ? 代碼如下: //定義結構 因為需要數組值和下標同時排序 typedef struct NODE {int value;int pos; }node;//調用cmp排序實現結構排序 int cmp(const void *a, const void *b) {return ((node *)a)->value - ((node *)b)->value; }int* twoSum(int* nums, int numsSize, int target) {int *result;int i,j;int sum;node *numNode;result=(int*)malloc(sizeof(int)*2);numNode=(node*)malloc(sizeof(node)*numsSize);for(i=0; i<numsSize; i++) {numNode[i].value=nums[i]; numNode[i].pos=i;}qsort(numNode,numsSize,sizeof(node),cmp);for(i=0,j=numsSize-1; i<j; ) {sum=numNode[i].value+numNode[j].value;if(sum==target) {if(numNode[i].pos<numNode[j].pos) { //小在前result[0]=numNode[i].pos+1;result[1]=numNode[j].pos+1;break;}else {result[0]=numNode[j].pos+1;result[1]=numNode[i].pos+1;break;}}else if(sum<target) { //左移i++;} else { //右移j--;}}return result; }
? ? ? ? 當然你也可以調用unordered_map實現哈希表O(n)算法:
?
class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> vec;unordered_map<int,int> map;for(int i=0; i<nums.size(); i++){if(map.find(target-nums[i]) != map.end()){vec.push_back(map[target-nums[i]]+1);vec.push_back(i+1);return vec;}map[nums[i]]=i;}} };
? ? ? ? 希望文章對你有所幫助吧!如果有錯誤或不足之處,還請海涵。最后分析一個今天的故事:
? ? ? ? 今天去一個小公司面試(百度百科沒有該公司詞條),那個Leader感覺很不尊重人,一方面總是不屑的搖頭與嘲笑人,一方面又總和旁邊的人聊天,還不斷打斷人說話。確實我普通話帶有西南口音,但是我就不明白了,難道這能影響到我寫程序嗎?雖然他直言不諱的說我編程語言不說精通,連熟悉都達不到,但是我前面都說了我不精通任何東西,只是一般。讓我給他講知識圖譜,他說要用通俗的話,那我就舉例子喬布斯、蘋果等,他又說沒難度,那我就講神經網絡,他說他不會編程。我不知道怎么說了~他居然還說百度可能僅僅是通過傳統的搜索引擎現在返回一個值,如姚明的身高,而沒有真正去做這個知識圖譜,我不知道怎么吐槽。
? ? ? ? ?僅僅面試了10分鐘,我就想走了,后面的問題我都說不知道,問我Python的優勢,我說腳本語言、開發快、語言簡潔,面向對象。他非說是面向過程的,和我爭論怎么是面向對象的,我說舉個例子建房子,他又說那是老師教的,我讓你講實例。好吧!我承認我比較弱,但是Python是面向對象這種常識都不知道,我只能呵呵了~
? ? ? ? ?然后他說你這也不會,那也不會,那就簡單講講你認為自己最優秀的地方。我就說我最大的優點就是堅持,不論10年,5年也好,我能做出東西來;同時以后還是想會貴州當老師。他最后說我們要的人還是有要求的,明確告訴你不會給你offer,我當時也回了句:我也不會來的。
? ? ? ? 最后離開的時候我給HR說我就是很不爽他,就沒心思答了,而且我表達能力也不好,但還是非常感謝你的推薦。最后當得知他是國內最好的大學畢業的時候,我也非常震驚。我脾氣只有這么好了,二十幾年的光陰里沒怎么生過氣,當時確實生氣。只恨臉皮薄,當時沒損他幾句,但那又顯得不夠大度。
? ? ? ? 其實我想說:面試本身就是一個相互學習的過程,確實很多基礎知識我都不記得了,可能也不善言表,但是你一個面試官不尊重面試人員,那我就是看不起你。不論你賺再多的錢,再有本事,學校再厲害,我就是看不起,做人似乎比工作更重要吧!我也大大小小參加了不少的面試,阿里、360、百度都有,各種大牛也遇到過,馬云、吳恩達、劉知遠的視頻和講座也看過不少,清華北大也都有同學,但是沒見過這么不尊重人的,面試過程中又嘲諷又聊天的,各種秀英語,還吐槽我普通話。
? ? ? ? 先做人,后做事吧!看著比我年輕幾歲,這種秀優越感只能理解為不成熟。
? ? ? ? 如果他們公司都是這樣招人的,那早晚也會毀在他們手上的,雖然他們公司現在有很大的潛力;如果我還在他說下干活,遇到個東西整天吐槽你做得不夠好,給他說代碼他又說不會,我也早晚會辭職的。總之,以后如果我走到當面試官那一步了,最起碼要尊重面試人員,因為你是在尋財,而不是秀優越感!而且我都不知道哪里來的優越感,學校嗎?好吧!但我還是看不起你這個人~
? ? ? ? 當然面試也讓我知道了自己還存在很多不足,尤其是底層的算法很多都忘了,最近扎扎實實看書和做LeetCode吧!自己技術還是不夠精通,非常致命,包括同步異步通訊,線程進程代碼等~但還是感謝那個熱心的HR吧!以后也盡量別參加這種沒有筆試,通過獵頭直接去小公司面試了。
? ? ? (By:Eastmount 2015-10-11 清晨6點?http://blog.csdn.net/eastmount/)
??
總結
以上是生活随笔為你收集整理的[C/C++基础知识] 一篇就让你彻底搞懂qsort快速排序的文章的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [python学习] 专题七.网络编程之
- 下一篇: C语言中能运算符重载吗,C++语言中什么