快速排序之總結
一、演算法思想
(1)要排序的陣列是A[0]……A[N-1],首先任意選取一個數據(通常選用陣列的第一個數)作為關鍵資料,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面,這個過程稱為一趟快速排序。值得注意的是,快速排序不是一種穩定的排序演算法,也就是說,多個相同的值的相對位置也許會在演算法結束時產生變動。
一趟快速排序的演算法是:
1)設定兩個變數i、j,排序開始的時候:i=0,j=N-1;
2)以第一個陣列元素作為關鍵資料,賦值給key,即key=A[0];
3)從j開始向前搜尋,即由後開始向前搜尋(j--),找到第一個小於key的值A[j],將A[j]和A[i]互換;
4)從i開始向後搜尋,即由前開始向後搜尋(i++),找到第一個大於key的A[i],將A[i]和A[j]互換;
5)重複第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j指標位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令迴圈結束)。
二、演算法演示
三、手工實現
void sort(int *a, int left, int right) { if(left >= right)/*如果左邊索引大於或者等於右邊的索引就代表已經整理完成一個組了*/ { return ; } int i = left; int j = right; int key = a[left]; while(i < j) /*控制在當組內尋找一遍*/ { while(i < j && key <= a[j]) /*而尋找結束的條件就是,1,找到一個小於或者大於key的數(大於或小於取決於你想升 序還是降序)2,沒有符合條件1的,並且i與j的大小沒有反轉*/ { j--;/*向前尋找*/ } a[i] = a[j]; /*找到一個這樣的數後就把它賦給前面的被拿走的i的值(如果第一次迴圈且key是 a[left],那麼就是給key)*/ while(i < j && key >= a[i]) /*這是i在當組內向前尋找,同上,不過注意與key的大小關係停止迴圈和上面相反, 因為排序思想是把數往兩邊扔,所以左右兩邊的數大小與key的關係相反*/ { i++; } a[j] = a[i]; } a[i] = key;/*當在當組內找完一遍以後就把中間數key迴歸*/ sort(a, left, i - 1);/*最後用同樣的方式對分出來的左邊的小組進行同上的做法*/ sort(a, i + 1, right);/*用同樣的方式對分出來的右邊的小組進行同上的做法*/ /*當然最後可能會出現很多分左右,直到每一組的i = j 為止*/ }
#include <iostream> using namespace std; void Qsort(int a[], int low, int high) { if(low >= high) { return; } int first = low; int last = high; int key = a[first];/*用字表的第一個記錄作為樞軸*/ while(first < last) { while(first < last && a[last] >= key) { --last; } a[first] = a[last];/*將比第一個小的移到低端*/ while(first < last && a[first] <= key) { ++first; } a[last] = a[first]; /*將比第一個大的移到高階*/ } a[first] = key;/*樞軸記錄到位*/ Qsort(a, low, first-1); Qsort(a, first+1, high); } int main() { int a[] = {57, 68, 59, 52, 72, 28, 96, 33, 24}; Qsort(a, 0, sizeof(a) / sizeof(a[0]) - 1);/*這裡原文第三個引數要減1否則記憶體越界*/ for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++) { cout << a[i] << ""; } return 0; }/*參考資料結構p274(清華大學出版社,嚴蔚敏)*/
** 關於快排的一些小問題 ** 1.快排是不穩定的,這個不穩定一個表現在其使用的時間是不確定的,最好情況(O(n))和最 壞情況(O(n^2))差距太大,我們一般說的O(nlog(n))都是指的是其平均時間. 2.快排是不穩定的,這個不穩定表現在如果相同的比較元素,可能順序不一樣,假設我們有 這樣一個序列,3,3,3,但是這三個3是有區別的,我們標記為3a,3b,3c,快排後的結果不一定 就是3a,3b,3c這樣的排列,所以在某些特定場合我們要用結構體來使其穩定(No.6的例子就 是說明這個問題的) 3.快排的比較函式的兩個引數必須都是const void *的,這個要特別注意,寫a和b只是我的 個人喜好,寫成cmp也只是我的個人喜好.推薦在cmp裡面重新定義兩個指標來強制型別轉換, 特別是在對結構體進行排序的時候 4.快排qsort的第三個引數,那個sizeof,推薦是使用sizeof(s[0])這樣,特別是對結構體, 往往自己定義2*sizeof(int)這樣的會出問題,用sizeof(s[0)既方便又保險 5.如果要對陣列進行部分排序,比如對一個s[n]的陣列排列其從s[i]開始的m個元素,只需要 在第一個和第二個引數上進行一些修改:qsort(&s[i],m,sizeof(s[i]),cmp);
四、常用實現
(一)C/C++中的qsort()函式及其cmp()
功 能: 使用快速排序例程進行排序
標頭檔案:stdlib.h
函式原型:
void __cdecl qsort (
void *base,
size_t num,
size_t width,
int (__cdecl *comp)(const void *, const void *)
)
引數說明:
1. base指向待排序陣列的首地址。一般情況下,base就是陣列的名字。
2. num為陣列中待排序元素的數量。
3. width為陣列中每個元素的大小,以位元組為單位。一般為 sizeof(ElemType);
4. comp為指向比較函式的指標,用於確定排序的順序。一般為cmp;
cmp函式
int cmp(const void *a, const void *b)
返回正數就是說 cmp 傳入引數第一個要放在第二個後面, 負數就是傳入引數第一個要放第二個前面, 如果是 0, 那就無所謂誰前誰後..
(1)對於int類整型陣列
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int s[10000],n,i;
int cmp(const void *a, const void *b)
{
return(*(int *)a-*(int *)b);
}
int main()
{
scanf("%d",&n);
for(i=0;i<n;i++) scanf("%d",&s[i]);
qsort(s,n,sizeof(s[0]),cmp);
for(i=0;i<n;i++) printf("%d ",s[i]);
return(0);
}
對double型陣列排序,原理同int 這裡做個註釋,本來是因為要判斷如果a==b返回0的,但是嚴格來說,兩個double數是不可能相等的,只能說fabs(a-b)<1e-20之類的這樣來判斷,所以這裡只返回了1和-1
#include <stdio.h>
#include <stdlib.h>
double s[1000];
int i,n;
int cmp(const void * a, const void * b)
{
return((*(double*)a-*(double*)b>0)?1:-1);
}
int main()
{
scanf("%d",&n);
for(i=0;i<n;i++) scanf("%lf",&s[i]);
qsort(s,n,sizeof(s[0]),cmp);
for(i=0;i<n;i++) printf("%lf ",s[i]);
return(0);
}
對一個字元陣列排序.原理同int
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char s[10000],i,n;
int cmp(const void *a,const void *b)
{
return(*(char *)a-*(char *)b);
}
int main()
{
scanf("%s",s);
n=strlen(s);
qsort(s,n,sizeof(s[0]),cmp);
printf("%s",s);
return(0);
}
No.5.對結構體排序 註釋一下.很多時候我們都會對結構體排序,比如校賽預選賽的那個櫻花,一般這個時候都在 cmp函式裡面先強制轉換了型別,不要在return裡面轉,我也說不清為什麼,但是這樣程式會 更清晰,並且絕對是沒錯的. 這裡同樣請注意double返回0的問題
#include <stdio.h>
#include <stdlib.h>
struct node
{
double date1;
int no;
} s[100];
int i,n;
int cmp(const void *a,const void *b)
{
struct node *aa=(node *)a;
struct node *bb=(node *)b;
return(((aa->date1)>(bb->date1))?1:-1);
}
int main()
{
scanf("%d",&n);
for(i=0;i<n;i++)
{
s[i].no=i+1;
scanf("%lf",&s[i].date1);
}
qsort(s,n,sizeof(s[0]),cmp);
for(i=0;i<n;i++) printf("%d %lf\n",s[i].no,s[i].date1);
return(0);
}
No.6.對結構體排序.加入no來使其穩定(即data值相等的情況下按原來的順序排)
#include <stdio.h>
#include <stdlib.h>
struct node
{
double date1;
int no;
} s[100];
int i,n;
int cmp(const void *a,const void *b)
{
struct node *aa=(node *)a;
struct node *bb=(node *)b;
if(aa->date1!=bb->date1)
return(((aa->date1)>(bb->date1))?1:-1);
else
return((aa->no)-(bb->no));
}
int main()
{
scanf("%d",&n);
for(i=0;i<n;i++)
{
s[i].no=i+1;
scanf("%lf",&s[i].date1);
}
qsort(s,n,sizeof(s[0]),cmp);
for(i=0;i<n;i++) printf("%d %lf\n",s[i].no,s[i].date1);
return(0);
}
No.7.對字串陣列的排序(char s[][]型)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char s[100][100];
int i,n;
int cmp(const void *a,const void *b)
{
return(strcmp((char*)a,(char*)b));
}
int main()
{
scanf("%d",&n);
for(i=0;i<n;i++) scanf("%s",s[i]);
qsort(s,n,sizeof(s[0]),cmp);
for(i=0;i<n;i++) printf("%s\n",s[i]);
return(0);
}
No.8.對字串陣列排序(char *s[]型)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *s[100];
int i,n;
int cmp(const void *a,const void *b)
{
return(strcmp(*(char**)a,*(char**)b));
}
int main()
{
scanf("%d",&n);
for(i=0;i<n;i++)
{
s[i]=(char*)malloc(sizeof(char*));
scanf("%s",s[i]);
}
qsort(s,n,sizeof(s[0]),cmp);
for(i=0;i<n;i++) printf("%s\n",s[i]);
return(0);
}
9、計算幾何中求凸包的cmp
int cmp(const void *a, const void *b)
{
TPoint *c = (TPoint *)a;
TPoint *d = (TPoint *)b;
double k = multi(*c, *d, point[0]); //p0c×p0d (若>0 說明c的極角小於d, 若<0, c的極角大於d)
if( k< 0) return 1; // 若前面的大於後面的,返回1--- 表示升序(交換)
else if(k == 0 && distance(*c, point[0]) >= distance(*d, point[0]))
return 1; // 把距離遠的丟在後面,這麼做掃描時才可以刪掉近的
else return -1;
}