1. 程式人生 > 其它 >c語言折半查詢法_經典排序演算法C語言描述

c語言折半查詢法_經典排序演算法C語言描述

技術標籤:c語言折半查詢法c語言排序演算法c語言插入排序歸併排序c語言插入排序c語言資料結構與演算法分析 c語言描述

最近在學習資料結構與演算法,結合網上資源整理了10個經典排序演算法,以供大家學習C語言程式設計演算法。

排序演算法平均時間複雜度最差時間複雜度空間複雜度資料物件穩定性
氣泡排序O()O()O(1)穩定
選擇排序O()O()O(1)陣列不穩定、連結串列穩定
插入排序O()O()O(1)穩定
快速排序O()O()O()不穩定
堆排序O()O()O(1)不穩定
歸併排序O()O()O(n)穩定
希爾排序O()O()O(1)不穩定
計數排序O(n+m)O(n+m)O(n+m)穩定
桶排序O(n)O(n)O(m)穩定
基數排序O(d(n+r))O(d(n+r))O(r)穩定

1 氣泡排序

演算法思想

  1. 從後往前兩兩比較相鄰元素的值,若為逆序(A[i-1]>A[i]),則交換它們,直到序列比較完,稱為一趟冒泡。
  2. 每趟冒泡的結果是把序列中最小的元素放到了序列的最終位置。
  3. 下一趟冒泡時,前一趟確定的最小元素不再參與比較,待排序列減少一個元素。
  4. 重複上述冒泡過程,若一趟冒泡過程中,沒有元素交換,則完成排序。

程式碼:

//氣泡排序
voidBubbleSort(intA[],intn){
intflag=0;
//用氣泡排序法將序列A中的元素按小到大排列
for(inti=0;i-1;i++){
flag=0;
for(intj=n-1;j>i;j--){
if(A[j-1]>A[j]){
inttemp=A[j-1];//交換
A[j-1]=A[j];
A[j]=temp;
flag=1;
}
}
if(flag==0)
return;
}
}

2 選擇排序

演算法思想

  1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  2. 從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。
  3. 以此類推,直到所有元素均排序完畢。

程式碼:

//選擇排序
voidSelectionSort(intA[],intn){
intminIndex,temp;
for(inti=0;i-1;i++){
minIndex=i;
for(intj=i+1;jif(A[j]//尋找最小的數
minIndex=j;//將最小數的索引儲存
}
}
if(minIndex!=i){
temp=A[i];
A[i]=A[minIndex];
A[minIndex]=temp;
}
}
return;
}

3 插入排序

3.1 直接插入排序

演算法思想

  1. 從第一個元素開始,該元素可以認為已經被排序。
  2. 取出下一個元素,在已經排序的元素序列中從後向前掃描。
  3. 如果該元素(已排序)大於新元素,將該元素移到下一位置。
  4. 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置。
  5. 將新元素插入到該位置後。
  6. 重複步驟2~5。

程式碼:

//直接插入排序
voidInsertSort(intA[],intn){
inti,j;
intx;//哨兵
for(inti=1;i//依次將A[1]~A[n]插入到前面已排序序列
if(A[i]-1]){//若A[i]小於其前驅,則需要將A[i]插入有序表
x=A[i];//複製為哨兵
for(j=i-1;x//從後往前查詢待插入位置
A[j+1]=A[j];//向後挪位
A[j+1]=x;//先後移一個元素
}
}
}

3.2 折半插入排序

程式碼:

//折半插入排序
voidInsertSort(intA[],intn){
inti,j,low,high,mid;
intx;//哨兵
for(inti=1;i//依次將A[1]~A[n]插入到前面已排序序列
x=A[i];
low=0;
high=i-1;
while(low<=high){
mid=(low+high)/2;
if(A[mid]>x)high=mid-1;
elselow=mid+1;
}
for(j=i-1;j>=high;--j)
A[j+1]=A[j];//向後挪位
A[high+1]=x;//先後移一個元素
}
}

4 快速排序

演算法思想

  1. 選取第一個數為樞紐。
  2. 將比樞紐小的數交換到左端,比樞紐大的數交換到右端。
  3. 對左右區間重複第二步,直到各區間只有一個數。

程式碼:

//快速排序
intPartition(intA[],intlow,inthigh){
intpivot=A[low];//將當前表的第一個元素設為樞紐值,對標進行劃分
while(low//迴圈跳出條件
while(low=pivot)--high;
A[low]=A[high];//將比樞紐值小的元素移到左端
while(lowA[high]=A[low];//將比樞紐值大的元素移到右端
}
A[low]=pivot;
returnlow;
}
voidQuickSort(intA[],intlow,inthigh){
if(low//遞迴跳出的條件
intpivotpos=Partition(A,low,high);//劃分
QuickSort(A,low,pivotpos-1);//依次對兩個子表進行遞迴排序
QuickSort(A,pivotpos+1,high);
}
}

5 堆排序

堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。

演算法思想

  1. 將初始待排序關鍵字序列(R1,R2….Rn)構建成大頂堆,此堆為初始的無序區。
  2. 將堆頂元素R[1]與最後一個元素R[n]交換,此時得到新的無序區(R1,R2,……Rn-1)和新的有序區(Rn),且滿足R[1,2…n-1]<=R[n]。
  3. 由於交換後新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,……Rn-1)調整為新堆,然後再次將R[1]與無序區最後一個元素交換,得到新的無序區(R1,R2….Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數為n-1,則整個排序過程完成。

程式碼:

//堆排序:(最大堆,有序區)。從堆頂把根卸出來放在有序區之前,再恢復堆。
voidmax_Heapify(intA[],intstart,intend){
//建立父節點指標和子節點指標
intdad=start;
intson=dad*2+1;
inttemp;
while(son<=end){//若子節點在範圍內才做比較
if(son+1<=end&&A[son]1])//先比較兩個子節點指標,選擇最大的
son++;
if(A[dad]>A[son])//如果父節點大於子節點代表調整完成,直接跳出函式
return;
else{//否則交換父子內容再繼續子節點與孫節點比較
temp=A[dad];
A[dad]=A[son];
A[son]=temp;
dad=son;
son=dad*2+1;
}
}
}

voidHeapSort(intA[],intn){
//初始化,i從最後一個父節點開始調整
inttemp;
for(inti=n/2-1;i>=0;i--)
max_Heapify(A,i,n-1);
//先將第一個元素和已經排好的元素前一位做交換,再從新調整(剛調整的元素之前的元素),直到排序完成
for(inti=n-1;i>0;i--){
temp=A[0];
A[0]=A[i];
A[i]=temp;
max_Heapify(A,0,i-1);
}
}

6 歸併排序

歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為2-路歸併。

演算法思想

  1. 把長度為n的輸入序列分成兩個長度為n/2的子序列。
  2. 對這兩個子序列分別採用歸併排序。
  3. 將兩個排序好的子序列合併成一個最終的排序序列。

程式碼:

//歸併排序
voidMerge(intnums[],inttmpNums[],intlstart,intrstart,intrend){
inti=0,lend=rstart-1,tmpPos=lstart;
constintnumSize=rend-lstart+1;
while((lstart<=lend)&&(rstart<=rend)){
if(nums[lstart]<=nums[rstart]){
tmpNums[tmpPos++]=nums[lstart++];
}else{
tmpNums[tmpPos++]=nums[rstart++];
}
}
while(lstart<=lend){
tmpNums[tmpPos++]=nums[lstart++];
}
while(rstart<=rend){
tmpNums[tmpPos++]=nums[rstart++];
}
for(i=0;inums[rend]=tmpNums[rend];
}
}

voidMsort(intnums[],inttmpNums[],intleft,intright){
intcenter=(left+right)/2;
if(leftMsort(nums,tmpNums,left,center);
Msort(nums,tmpNums,center+1,right);
Merge(nums,tmpNums,left,center+1,right);
}
}

voidMergerSort(intnums[],intN){
int*tmpArray=NULL;
tmpArray=(int*)malloc(N*sizeof(int));
if(tmpArray!=NULL){
Msort(nums,tmpArray,0,N-1);
free(tmpArray);
}
}

7 希爾排序

希爾排序,也稱遞減增量排序演算法,是插入排序的一種更高效的改進版本。但希爾排序是非穩定排序演算法。先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序。

演算法思想

  1. 選擇一個增量序列d1,d2,…,dk,其中di>dj(i
  2. 按增量序列個數k,對序列進行k 趟排序。
  3. 每趟排序,根據對應的增量di,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。

程式碼:

//希爾排序
voidShellSort(intA[],intn){
inttemp,dk,i,j;
for(dk=n/2;dk>0;dk=dk/2){
for(i=dk;iif(A[i]temp=A[i];
for(j=i-dk;j>-1&&tempA[j+dk]=A[j];
A[j+dk]=temp;
}
}
}
}

8 計數排序

計數排序統計小於等於該元素值的元素的個數i,於是該元素就放在目標陣列的索引i位(i≥0)。

  • 計數排序基於一個假設,待排序數列的所有數均為整數,且出現在(0,k)的區間之內。
  • 如果 k(待排陣列的最大值) 過大則會引起較大的空間複雜度,一般是用來排序 0 到 100 之間的數字的最好的演算法,但是它不適合按字母順序排序人名。
  • 計數排序不是比較排序,排序的速度快於任何比較排序演算法。

演算法思想

  1. 找出待排序的陣列中最大和最小的元素。
  2. 統計陣列中每個值為 i 的元素出現的次數,存入陣列 C 的第 i 項。
  3. 對所有的計數累加(從 C 中的第一個元素開始,每一項和前一項相加)。
  4. 向填充目標陣列:將每個元素 i 放在新陣列的第 C[i] 項,每放一個元素就將 C[i] 減去 1。

程式碼:

//計數排序
voidCountSort(intA[],intn){
intmax=A[0];//序列中的最大值
intmin=A[0];//序列中的最小值
for(inti=0;iif(A[i]>=max)
max=A[i];
if(A[i]<=min)
min=A[i];
}
intrange=max-min+1;//需要開闢的空間大小
int*count=(int*)malloc(sizeof(int)*(range));
memset(count,0,sizeof(int)*range);//輔助空間初始化為0,0代表沒有那個數
for(inti=0;icount[A[i]-min]++;//A[i]-min是將該數對應到輔助空間的下標
}
intindex=0;
for(inti=0;i//遍歷輔助空間
while(count[i]--){//下標處的數值是幾,說明該數出現了幾次
A[index++]=i+min;//將下標處的數對應回原陣列
}
}
}

9 桶排序

將值為i的元素放入i號桶,最後依次把桶裡的元素倒出來。

演算法思想

  1. 設定一個定量的陣列當作空桶子。
  2. 尋訪序列,並且把專案一個一個放到對應的桶子去。
  3. 對每個不是空的桶子進行排序。
  4. 從不是空的桶子裡把專案再放回原來的序列中。

程式碼:

//桶排序
structnode{
intdata;
structnode*next;
};
//對每個連結串列(桶)進行插入排序
voidinsert_node(structnode**bucket,intdata){
structnode*p=(structnode*)malloc(sizeof(structnode));
p->data=data;
p->next=NULL;
//桶為空
if(*bucket==NULL){
*bucket=p;
}else{
structnode*pre=NULL;
structnode*cur=*bucket;
while(cur!=NULL&&cur->data<=data){
pre=cur;
cur=cur->next;
}
//對插入到第一個結點前的情況處理
if(pre==NULL){
*bucket=p;
p->next=cur;
}else{
pre->next=p;
p->next=cur;
}
}
}
//k表示資料位數,3為表示取值範圍[000-999]
voidBucketSort(inta[],intlength,intk){
//申請桶空間
structnode**b=(structnode**)calloc(10,sizeof(structnode*));
inti,j,m;
//將待排資料記錄分配到桶
for(i=0;i//獲取對應10個桶的標識0-9
m=a[i];
for(j=k;j>1;j--)
m=m/10;
//分配到桶連結串列中
insert_node(&b[m],a[i]);
}
//方便返回結果,複製到原陣列a中
//複製到陣列a中
structnode*p;
for(i=0,j=0;i<10&&jif(b[i]!=NULL){
p=b[i];
//遍歷每個桶元素
while(p!=NULL){
a[j]=p->data;
j++;
p=p->next;
}
}
}
//釋放儲存空
for(i=0;i<10;i++){
while(b[i]!=NULL){
p=b[i];
b[i]=p->next;
free(p);
}
}
free(b);
}

10 基數排序

一種多關鍵字的排序演算法,可用桶排序實現。

演算法思想

  1. 取得陣列中的最大數,並取得位數。
  2. A為原始陣列,從最低位開始取每個位組成radix陣列。
  3. 對radix進行計數排序(利用計數排序適用於小範圍數的特點)。

程式碼:

//基數排序
#defineMax_10//陣列個數
#defineRADIX_1010//整形排序
#defineKEYNUM_3131//關鍵字個數,這裡為整形位數
intGetNumInPos(intnum,intpos){//找到num的從低到高的第pos位的資料
inttemp=1;
for(inti=0;i1;i++)
temp*=10;
return(num/temp)%10;
}

voidRadixSort(intA[],intn){//基數排序
int*radixArrays[RADIX_10];//分別為0~9的序列空間
for(inti=0;i10;i++){
radixArrays[i]=(int*)malloc(sizeof(int)*(n+1));
radixArrays[i][0]=0;//index為0處記錄這組資料的個數
}
for(intpos=1;pos<=KEYNUM_31;pos++){//從個位開始到31位
for(inti=0;i//分配過程
intnum=GetNumInPos(A[i],pos);
intindex=++radixArrays[num][0];
radixArrays[num][index]=A[i];
}
for(inti=0,j=0;i//收集
for(intk=1;k<=radixArrays[i][0];k++)
A[j++]=radixArrays[i][k];
radixArrays[i][0]=0;//復位
}
}
}

PS:後臺回覆 “排序演算法”獲取本篇文章PDF。

677118d618abe191a604313bb2a805b1.gif

- End -