快速排序(分而治之策略及C語言實現)
阿新 • • 發佈:2019-01-26
提出的問題
分而治之的策略
重要的分而治之演算法
快速排序
問題
要在一個長為x,寬為y的長方形中畫出均勻且大小相等的正方形 那麼正方形的邊長為多少 (1)可以看出正方形的邊長需要是x和y的最大公約數 如何求最大公約數? 找查約數法: 分別找出兩個數的所有約數,再找出兩個數的所有公約數,最大的那個就是最大公約數(最好別用,太煩人,容易出錯) 更相減損法: 任意兩個數,判定是否為偶數,是就用2約簡,不是就用較大的數減較小的數,所得的差和較小的數比較,再用大的減小的,直到所得的減數和差相等,再用約掉的每個2相乘(如果約掉3個2,就是2^3)z再與所得的相等的數的乘積就是最大公約數【內部原理需要再想想。。。】 輾轉相除法: 以小的數除大數,所得的是整數,那這個數就是最大公約數,不然就用餘數來除剛才的除數,直到得到整數,這時作為除數的就是最大公約數【思想:處理餘數】 演算法實現(遞迴和迴圈): #include <bits/stdc++.h> using namespace std; int Division(int a,int b) { if(b == 0) { return a; } else { return Division(b,a%b); } } int Division_while(int c,int d) { while(d!=0) { int r = d; d = c%d; c = r; } return c; } int main() { int ac ,bc; scanf("%d %d",&ac,&bc); if(ac >bc) { cout<<"Division:\n"<<Division(ac,bc)<<endl; cout<<"Division_while\n"<<Division_while(ac,bc)<<endl; } else { cout<<"Division:\n"<<Division(bc,ac)<<endl; cout<<"Division_while\n"<<Division_while(bc,ac)<<endl; } return 0; } (2)如果長不是寬的整數倍,那麼假如x = 2*y +m;那麼可以畫兩個邊長為y的正方形,還剩下一部分面積,然後再看看可以在剩下的這片區域中畫幾個最大的正方形(邊長等於寬),不斷縮小剩餘的面積,直到沒有剩餘為止。【其實類似上面的輾轉相除法,都是出來餘下的部分,而且在不斷縮小問題的規模】 不過問題是,為什麼剩餘部分的最大正方形就是整個長方形區域的最大正方形呢? 課本上說歐幾里得演算法已經證明完了 那就看看歐幾里得演算法 歐幾里得演算法好像就是輾轉相除法(lll¬ω¬),這個演算法的實現好像不算是問題(lll¬ω¬)
分而治之的原理
由上面的可以看出,分而治之其實是縮小問題規模再解決
快速排序的實現
先分而治之再歸納證明
分而治之是指如果有一個大規模的陣列需要排序,那麼可以分成多個小陣列排序再組合成大陣列,而歸納證明是指我們可以將只有兩個元素的陣列通過比較進行排序,也可以使用比較對有三個元素的陣列進行排序,那麼以此類推,我們可以對一個大規模的陣列進行排序,通過分而治之可以轉變為對兩個或三個元素的
問題
如何建立未知大小的陣列?
在C中如果建立陣列而沒有指定大小會報錯 int e[]; ||=== Build file: "no target" in "no project" (compiler: unknown) ===| E:\C_code\kuaisupaixu.cpp|11|error: storage size of 'e' isn't known| 要實現元素動態新增有什麼方法? 使用STL,vector 這個效率怎麼樣,消耗資源多麼 使用連結串列 如果需要陣列的話,可以再轉為陣列 使用連結串列的話,可以很容易的將多個片段連線起來,但是使用連線時需要找到這一段連結串列的最後一個元素將他的指標指向要連線的元素,僅僅找片段中的最後一個元素就會消耗不少時間及資源 而且這種排序操作幾乎打亂了所有指標的指向可能會出現問題,導致指標指向錯誤 如果是大規模的陣列使用快速排序,由於連結串列的建立,銷燬,指標的重新分配以及連結串列不可隨機訪問只能一個個訪問的原因,也可能會抵消掉快速排序的效率 還是使用陣列 (1) 一開始使用malloc,如果元素增加使用realloc,這個方法使用的是堆記憶體,而堆記憶體一般受限於虛擬記憶體,所以一般堆記憶體比較大,有比較多的空間,這種方法可以實現認為上的動態增加,的確通過再申請空間讓人以為空間在增加,好像有更多的空間可以用,實際上也是, 不過問題一是在遞迴時,儲存的映像中使用此操作是所有映像改變還是隻影響當前狀態 而且如果會有很多元素需要新增那麼需要很多次realloc那麼會不會消耗很多的資源,讓陣列在記憶體中移來移去 (2) 直接建立一個足夠大的陣列,使用一個位置指標用來代表這個陣列用了多少空間,這樣不用使用新的方法,還是使用陣列,雖然使用了幾個大陣列,但是不需要再對陣列進行什麼改變,只需要在陣列的不同位置變換元素數值即可
如何在C語言中實現傳遞陣列?
方法有兩種:值傳遞和地址傳遞(額 一看就差不多了) 值傳遞 說明和定義函式時,要在陣列引數的尾部加上一對方括號([]),呼叫函式時只需將陣列的地址(即陣列名)傳遞給函式 在值傳遞方式中,陣列將被複制一份,複製所得的陣列將被存放在棧中 值傳遞方式的開銷是非常大的, 因為首先複製原來陣列就會佔用額外的記憶體, 還會有程式碼去執行復制的操作, 這種操作又會需要比較多的時間, 資源,額外指令,時間都會有消耗,所以效率會比較低 地址傳遞 地址傳遞只需要傳遞個地址就行了,也就是個指標, #在C/C++不能直接返回一個數組。這是由於在C/C++中,陣列不是一種型別,因此不能被直接返回。 #定義了一個數組名之後,他就是常量了,不能再給陣列賦值 Python實現 def quicksort(array): if len(array) < 2: return array else: pivot = array[0] less = [i for i in array[1:] if i <= pivot] greater = [i for i in array[1:] if i > pivot] return quicksort(less) + [pivot] + quicksort(greater) C/C++實現(有問題,再改) #include <bits/stdc++.h> using namespace std; int* QuicklySort(int* a,int size_n) { if(size_n<2) { return a; } else{ int b = a[0]; int e[70]; int small[70],big[70]; int k=0; int s_num=0,b_num=0; for(int j =0;j<size_n;j++) { if(a[j]<b) { small[s_num] = a[j]; s_num++; } if(a[j]>=b) { big[b_num] = a[j]; b_num++; } } int *s_1; s_1= QuicklySort(small,s_num); int *b_1; b_1 = QuicklySort(big,b_num); for( k;k<s_num;k++) { e[k] = s_1[k]; } e[k] = a[0]; k++; int temp = 0; for(k;k<size_n;k++) { if(temp<=b_num) { e[k] = b_1[temp++]; } } return e; } } int main() { int a[70]; int ll = 0; for(int i=99;i>30;i--) { a[ll] = i; ll++; } QuicklySort(a,70); cout<<"finished"<<endl; return 0; } 陣列傳遞問題 如果在函式A將陣列x作為引數傳遞給函式B 地址傳遞 int *b 值傳遞 int b[] #include <bits/stdc++.h> using namespace std; int ss(int b[]) { //printf("%d\n",*b); cout<<"(sizeof(b)/sizeof(b[0]))"<<(sizeof(b)/sizeof(b[0]))<<endl; for(int j=0;j<(sizeof(b)/sizeof(b[0]));j++) { cout<<b[j]<<endl; } } int main() { int a[9]; int num =0; for(int i =11;i<20;i++) { a[num++] = i; } for(int l=0;l<num;l++) { cout<<"a[]"<<a[l]<<endl; } ss(a); return 0; } 如果使用 for(int j=0;j<(sizeof(b)/sizeof(b[0]));j++),那麼計算出的是j<1,所以顯示出的是陣列的第一個元素,【陣列名傳遞進函式作為引數會退化為指標,使用sizeof是得不到陣列的長度的】 如果使用for(int j=0;j<9;j++),那麼就可以顯示出所有值了
如何實現return 一個數組?
C程式語言不允許返回整個陣列作為函式的引數。但是可以返回一個指標
#include <bits/stdc++.h>
using namespace std;
int* ss(int *b,int size_n)
{
//printf("%d\n",*b);
cout<<"(sizeof(b)/sizeof(b[0]))"<<(sizeof(b)/sizeof(b[0]))<<endl;
for(int j=0;j<size_n;j++)
{
b[j]=b[j]+100;
cout<<b[j]<<endl;
}
return b;
}
int main()
{
int a[9];
int num =0;
for(int i =11;i<20;i++)
{
a[num++] = i;
}
for(int l=0;l<num;l++)
{
cout<<"a[]"<<a[l]<<endl;
}
int *y;
y = ss(a,9);
cout<<"y:"<<y[6]<<endl;
return 0;
}
將這個方法用在遞迴中【這是一個沒有結束條件的遞迴程式,會一直執行下去】
#include <bits/stdc++.h>
using namespace std;
int* ss(int *b,int size_n)
{
//printf("%d\n",*b);
//cout<<"(sizeof(b)/sizeof(b[0]))"<<(sizeof(b)/sizeof(b[0]))<<endl;
for(int j=0;j<size_n;j++)
{
b[j]=b[j]+100;
cout<<b[j]<<endl;
}
return ss(b,size_n);
}
int main()
{
int a[9];
int num =0;
for(int i =11;i<20;i++)
{
a[num++] = i;
}
for(int l=0;l<num;l++)
{
cout<<"a[]"<<a[l]<<endl;
}
int *y;
y = ss(a,9);
cout<<"y:"<<y[6]<<endl;
return 0;
}
如果return的陣列比較大的話,在函式B中返回由main函式接收的話,只剩陣列首地址處(即第一個元素)可以訪問了,其他位置的元素都變了,這可能是因為main函式呼叫函式B完成後,函式B中的連線好的陣列被銷燬,雖然一個數組的首地址被傳到了main函式,但是也只有一個元素了,所以要保證呼叫完函式B後還得讓陣列存在,可以嘗試結構體或者全域性變數
下面是使用全域性變數後的情況,完全可以在main函式中訪問已經處理過的陣列,不過有個限制,就是全域性變數宣告時,宣告的陣列範圍需要比較大,
#include <bits/stdc++.h>
#include <stdlib.h>
using namespace std;
int z[100];
int* ss(int *b,int size_n)
{
int num = 0;
int num_2=0;
//printf("%d\n",*b);
//cout<<"(sizeof(b)/sizeof(b[0]))"<<(sizeof(b)/sizeof(b[0]))<<endl;
for(int j=0; j<size_n; j++)
{
b[j]=b[j]+100;
cout<<b[j]<<endl;
}
int a[size_n];
int x[size_n];
for(int k =0; k<size_n; k++)
{
if(b[k]%2==0)
{
a[num]=b[k];
num++;
}
}
cout<<"num:"<<num<<endl;
for(int nn=0; nn<num; nn++)
{
cout<<"a[num]:"<<a[nn]<<endl;
}
for(int k =0; k<size_n; k++)
{
if(b[k]%5==0)
{
x[num_2]=b[k];
num_2++;
}
}
cout<<"num_2:"<<num_2<<endl;
for(int nn=0; nn<num_2; nn++)
{
cout<<"x[num_2]:"<<x[nn]<<endl;
}
int f = num+num_2;
///連線兩個陣列
for(int d =0;d<f;d++)
{
if(d<num)
{
z[d]=a[d];
}
if(d>=num)
{
z[d] = x[d-num];
}
}
for(int nn=0; nn<f; nn++)
{
cout<<"z[nn]:"<<z[nn]<<endl;
}
return z;
}
int main()
{
int a[9];
int num =0;
for(int i =11; i<20; i++)
{
a[num++] = i;
}
for(int l=0; l<num; l++)
{
cout<<"a[]"<<a[l]<<endl;
}
int *y;
y = ss(a,9);
cout<<"y0:"<<y[0]<<endl;
cout<<"y1:"<<y[1]<<endl;
cout<<"y2:"<<y[2]<<endl;
cout<<"y3:"<<y[3]<<endl;
cout<<"y4:"<<y[4]<<endl;
int yy =0;
for(int g =0;g<9;g++)
{
if(y[g])
{
cout<<"y:"<<y[g]<<endl;
}
}
return 0;
}
如何實現連線兩個陣列(int型別陣列)
方法一:據說linux kernel中有一個flex_array的實現
方法二:新建一個數組,將需要連線的陣列內元素的值放到新陣列中
#include <bits/stdc++.h>
#include <stdlib.h>
using namespace std;
int* ss(int *b,int size_n)
{
int num = 0;
int num_2=0;
//printf("%d\n",*b);
//cout<<"(sizeof(b)/sizeof(b[0]))"<<(sizeof(b)/sizeof(b[0]))<<endl;
for(int j=0; j<size_n; j++)
{
b[j]=b[j]+100;
cout<<b[j]<<endl;
}
int a[size_n];
int x[size_n];
for(int k =0; k<size_n; k++)
{
if(b[k]%2==0)
{
a[num]=b[k];
num++;
}
}
cout<<"num:"<<num<<endl;
for(int nn=0; nn<num; nn++)
{
cout<<"a[num]:"<<a[nn]<<endl;
}
for(int k =0; k<size_n; k++)
{
if(b[k]%5==0)
{
x[num_2]=b[k];
num_2++;
}
}
cout<<"num_2:"<<num_2<<endl;
for(int nn=0; nn<num_2; nn++)
{
cout<<"x[num_2]:"<<x[nn]<<endl;
}
int f = num+num_2;
int z[f];
///連線兩個陣列
for(int d =0;d<f;d++)
{
if(d<num)
{
z[d]=a[d];
}
if(d>=num)
{
z[d] = x[d-num];
}
}
for(int nn=0; nn<f; nn++)
{
cout<<"z[nn]:"<<z[nn]<<endl;
}
return z;
}
int main()
{
int a[9];
int num =0;
for(int i =11; i<20; i++)
{
a[num++] = i;
}
for(int l=0; l<num; l++)
{
cout<<"a[]"<<a[l]<<endl;
}
int *y;
y = ss(a,9);
int yy =0;
while(*y)
{
cout<<"y:"<<y[yy++]<<endl;
y+=sizeof(int);
}
return 0;
}
快速排序最終實現
陣列比較多的話,遞迴也比較慢
#include <bits/stdc++.h>
using namespace std;
int z[100];
int a[70];
int* QuicklySort(int* a,int size_n)
{
if(size_n<2)
{
return a;
}
else
{
int b = a[0];
int small[70],big[70];
int s_num=0,b_num=0;
for(int j =0; j<size_n; j++)
{
if(a[j]<b)
{
small[s_num] = a[j];
s_num++;
}
if(a[j]>=b)
{
big[b_num] = a[j];
b_num++;
}
}
int *s_1;
s_1= QuicklySort(small,s_num);
int *b_1;
b_1 = QuicklySort(big,b_num);
for(int d =0; d<size_n; d++)
{
if(d<s_num)
{
z[d]=s_1[d];
}
if(d==s_num)
{
z[d] = b;
}
if(d>s_num)
{
z[d]= b_1[d-1-s_num];
}
}
return z;
}
}
int main()
{
//int a[70];
int ll = 0;
for(int i=99; i>30; i--)
{
a[ll] = i;
ll++;
}
int *u;
u = QuicklySort(a,70);
for(int yy =0;yy<70;yy++)
{
cout<<"z[]"<<u[yy]<<endl;
}
cout<<"finished"<<endl;
return 0;
}