本文目錄一覽:
基於C語言的幾種排序算法的分析
相關知識介紹(所有定義只為幫助讀者理解相關概念,並非嚴格定義):
1、穩定排序和非穩定排序
簡單地說就是所有相等的數經過某種排序方法後,仍能保持它們在排序之前的相對次序,我們就
說這種排序方法是穩定的。反之,就是非穩定的。
比如:一組數排序前是a1,a2,a3,a4,a5,其中a2=a4,經過某種排序後為a1,a2,a4,a3,a5,
則我們說這種排序是穩定的,因為a2排序前在a4的前面,排序後它還是在a4的前面。假如變成a1,a4,
a2,a3,a5就不是穩定的了。
2、內排序和外排序
在排序過程中,所有需要排序的數都在內存,並在內存中調整它們的存儲順序,稱為內排序;
在排序過程中,只有部分數被調入內存,並藉助內存調整數在外存中的存放順序排序方法稱為外排序。
3、算法的時間複雜度和空間複雜度
所謂算法的時間複雜度,是指執行算法所需要的計算工作量。
一個算法的空間複雜度,一般是指執行這個算法所需要的內存空間。
================================================================================
*/
/*
================================================
功能:選擇排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:
在要排序的一組數中,選出最小的一個數與第一個位置的數交換;
然後在剩下的數當中再找最小的與第二個位置的數交換,如此循環
到倒數第二個數和最後一個數比較為止。
選擇排序是不穩定的。算法複雜度O(n2)–[n的平方]
=====================================================
*/
void select_sort(int *x, int n)
{
int i, j, min, t;
for (i=0; in-1; i++) /*要選擇的次數:0~n-2共n-1次*/
{
min = i; /*假設當前下標為i的數最小,比較後再調整*/
for (j=i+1; jn; j++)/*循環找出最小的數的下標是哪個*/
{
if (*(x+j) *(x+min))
{
min = j; /*如果後面的數比前面的小,則記下它的下標*/
}
}
if (min != i) /*如果min在循環中改變了,就需要交換數據*/
{
t = *(x+i);
*(x+i) = *(x+min);
*(x+min) = t;
}
}
}
/*
================================================
功能:直接插入排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:
在要排序的一組數中,假設前面(n-1) [n=2] 個數已經是排
好順序的,現在要把第n個數插到前面的有序數中,使得這n個數
也是排好順序的。如此反覆循環,直到全部排好順序。
直接插入排序是穩定的。算法時間複雜度O(n2)–[n的平方]
=====================================================
*/
void insert_sort(int *x, int n)
{
int i, j, t;
for (i=1; in; i++) /*要選擇的次數:1~n-1共n-1次*/
{
/*
暫存下標為i的數。注意:下標從1開始,原因就是開始時
第一個數即下標為0的數,前面沒有任何數,單單一個,認為
它是排好順序的。
*/
t=*(x+i);
for (j=i-1; j=0 t*(x+j); j–) /*注意:j=i-1,j–,這裡就是下標為i的數,在它前面有序列中找插入位置。*/
{
*(x+j+1) = *(x+j); /*如果滿足條件就往後挪。最壞的情況就是t比下標為0的數都小,它要放在最前面,j==-1,退出循環*/
}
*(x+j+1) = t; /*找到下標為i的數的放置位置*/
}
}
/*
================================================
功能:冒泡排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:
在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上
而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較
小的往上冒。即:每當兩相鄰的數比較後發現它們的排序與排序要
求相反時,就將它們互換。
下面是一種改進的冒泡算法,它記錄了每一遍掃描後最後下沉數的
位置k,這樣可以減少外層循環掃描的次數。
冒泡排序是穩定的。算法時間複雜度O(n2)–[n的平方]
=====================================================
*/
void bubble_sort(int *x, int n)
{
int j, k, h, t;
for (h=n-1; h0; h=k) /*循環到沒有比較範圍*/
{
for (j=0, k=0; jh; j++) /*每次預置k=0,循環掃描後更新k*/
{
if (*(x+j) *(x+j+1)) /*大的放在後面,小的放到前面*/
{
t = *(x+j);
*(x+j) = *(x+j+1);
*(x+j+1) = t; /*完成交換*/
k = j; /*保存最後下沉的位置。這樣k後面的都是排序排好了的。*/
}
}
}
}
/*
================================================
功能:希爾排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:
在直接插入排序算法中,每次插入一個數,使有序序列只增加1個節點,
並且對插入下一個數沒有提供任何幫助。如果比較相隔較遠距離(稱為
增量)的數,使得數移動時能跨過多個元素,則進行一次比較就可能消除
多個元素交換。D.L.shell於1959年在以他名字命名的排序算法中實現
了這一思想。算法先將要排序的一組數按某個增量d分成若干組,每組中
記錄的下標相差d.對每組中全部元素進行排序,然後再用一個較小的增量
對它進行,在每組中再進行排序。當增量減到1時,整個要排序的數被分成
一組,排序完成。
下面的函數是一個希爾排序算法的一個實現,初次取序列的一半為增量,
以後每次減半,直到增量為1。
希爾排序是不穩定的。
=====================================================
*/
void shell_sort(int *x, int n)
{
int h, j, k, t;
for (h=n/2; h0; h=h/2) /*控制增量*/
{
for (j=h; jn; j++) /*這個實際上就是上面的直接插入排序*/
{
t = *(x+j);
for (k=j-h; (k=0 t*(x+k)); k-=h)
{
*(x+k+h) = *(x+k);
}
*(x+k+h) = t;
}
}
}
/*
================================================
功能:快速排序
輸入:數組名稱(也就是數組首地址)、數組中起止元素的下標
================================================
*/
/*
====================================================
算法思想簡單描述:
快速排序是對冒泡排序的一種本質改進。它的基本思想是通過一趟
掃描後,使得排序序列的長度能大幅度地減少。在冒泡排序中,一次
掃描只能確保最大數值的數移到正確位置,而待排序序列的長度可能只
減少1。快速排序通過一趟掃描,就能確保某個數(以它為基準點吧)
的左邊各數都比它小,右邊各數都比它大。然後又用同樣的方法處理
它左右兩邊的數,直到基準點的左右只有一個元素為止。它是由
C.A.R.Hoare於1962年提出的。
顯然快速排序可以用遞歸實現,當然也可以用棧化解遞歸實現。下面的
函數是用遞歸實現的,有興趣的朋友可以改成非遞歸的。
快速排序是不穩定的。最理想情況算法時間複雜度O(nlog2n),最壞O(n2)
=====================================================
*/
void quick_sort(int *x, int low, int high)
{
int i, j, t;
if (low high) /*要排序的元素起止下標,保證小的放在左邊,大的放在右邊。這裡以下標為low的元素為基準點*/
{
i = low;
j = high;
t = *(x+low); /*暫存基準點的數*/
while (ij) /*循環掃描*/
{
while (ij *(x+j)t) /*在右邊的只要比基準點大仍放在右邊*/
{
j–; /*前移一個位置*/
}
if (ij)
{
*(x+i) = *(x+j); /*上面的循環退出:即出現比基準點小的數,替換基準點的數*/
i++; /*後移一個位置,並以此為基準點*/
}
while (ij *(x+i)=t) /*在左邊的只要小於等於基準點仍放在左邊*/
{
i++; /*後移一個位置*/
}
if (ij)
{
*(x+j) = *(x+i); /*上面的循環退出:即出現比基準點大的數,放到右邊*/
j–; /*前移一個位置*/
}
}
*(x+i) = t; /*一遍掃描完後,放到適當位置*/
quick_sort(x,low,i-1); /*對基準點左邊的數再執行快速排序*/
quick_sort(x,i+1,high); /*對基準點右邊的數再執行快速排序*/
}
}
/*
================================================
功能:堆排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:
堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。
堆的定義如下:具有n個元素的序列(h1,h2,…,hn),當且僅當
滿足(hi=h2i,hi=2i+1)或(hi=h2i,hi=2i+1)(i=1,2,…,n/2)
時稱之為堆。在這裡只討論滿足前者條件的堆。
由堆的定義可以看出,堆頂元素(即第一個元素)必為最大項。完全二叉樹可以
很直觀地表示堆的結構。堆頂為根,其它為左子樹、右子樹。
初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲順序,
使之成為一個堆,這時堆的根節點的數最大。然後將根節點與堆的最後一個節點
交換。然後對前面(n-1)個數重新調整使之成為堆。依此類推,直到只有兩個節點
的堆,並對它們作交換,最後得到有n個節點的有序序列。
從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最後一個元素
交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,二是反覆調用滲透函數
實現排序的函數。
堆排序是不穩定的。算法時間複雜度O(nlog2n)。
*/
/*
功能:滲透建堆
輸入:數組名稱(也就是數組首地址)、參與建堆元素的個數、從第幾個元素開始
*/
void sift(int *x, int n, int s)
{
int t, k, j;
t = *(x+s); /*暫存開始元素*/
k = s; /*開始元素下標*/
j = 2*k + 1; /*右子樹元素下標*/
while (jn)
{
if (jn-1 *(x+j) *(x+j+1))/*判斷是否滿足堆的條件:滿足就繼續下一輪比較,否則調整。*/
{
j++;
}
if (t*(x+j)) /*調整*/
{
*(x+k) = *(x+j);
k = j; /*調整後,開始元素也隨之調整*/
j = 2*k + 1;
}
else /*沒有需要調整了,已經是個堆了,退出循環。*/
{
break;
}
}
*(x+k) = t; /*開始元素放到它正確位置*/
}
/*
功能:堆排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
*/
void heap_sort(int *x, int n)
{
int i, k, t;
int *p;
for (i=n/2-1; i=0; i–)
{
sift(x,n,i); /*初始建堆*/
}
for (k=n-1; k=1; k–)
{
t = *(x+0); /*堆頂放到最後*/
*(x+0) = *(x+k);
*(x+k) = t;
sift(x,k,0); /*剩下的數再建堆*/
}
}
void main()
{
#define MAX 4
int *p, i, a[MAX];
/*錄入測試數據*/
p = a;
printf(“Input %d number for sorting :\n”,MAX);
for (i=0; iMAX; i++)
{
scanf(“%d”,p++);
}
printf(“\n”);
/*測試選擇排序*/
p = a;
select_sort(p,MAX);
/**/
/*測試直接插入排序*/
/*
p = a;
insert_sort(p,MAX);
*/
/*測試冒泡排序*/
/*
p = a;
insert_sort(p,MAX);
*/
/*測試快速排序*/
/*
p = a;
quick_sort(p,0,MAX-1);
*/
/*測試堆排序*/
/*
p = a;
heap_sort(p,MAX);
*/
for (p=a, i=0; iMAX; i++)
{
printf(“%d “,*p++);
}
printf(“\n”);
system(“pause”);
}
c語言中排序方法
1、冒泡排序(最常用)
冒泡排序是最簡單的排序方法:原理是:從左到右,相鄰元素進行比較。每次比較一輪,就會找到序列中最大的一個或最小的一個。這個數就會從序列的最右邊冒出來。(注意每一輪都是從a[0]開始比較的)
以從小到大排序為例,第一輪比較後,所有數中最大的那個數就會浮到最右邊;第二輪比較後,所有數中第二大的那個數就會浮到倒數第二個位置……就這樣一輪一輪地比較,最後實現從小到大排序。
2、雞尾酒排序
雞尾酒排序又稱雙向冒泡排序、雞尾酒攪拌排序、攪拌排序、漣漪排序、來回排序或快樂小時排序, 是冒泡排序的一種變形。該算法與冒泡排序的不同處在於排序時是以雙向在序列中進行排序。
原理:數組中的數字本是無規律的排放,先找到最小的數字,把他放到第一位,然後找到最大的數字放到最後一位。然後再找到第二小的數字放到第二位,再找到第二大的數字放到倒數第二位。以此類推,直到完成排序。
3、選擇排序
思路是設有10個元素a[1]-a[10],將a[1]與a[2]-a[10]比較,若a[1]比a[2]-a[10]都小,則不進行交換。若a[2]-a[10]中有一個以上比a[1]小,則將其中最大的一個與a[1]交換,此時a[1]就存放了10個數中最小的一個。同理,第二輪拿a[2]與a[3]-a[10]比較,a[2]存放a[2]-a[10]中最小的數,以此類推。
4、插入排序
插入排序是在一個已經有序的小序列的基礎上,一次插入一個元素*
一般來說,插入排序都採用in-place在數組上實現。
具體算法描述如下:
⒈ 從第一個元素開始,該元素可以認為已經被排序
⒉ 取出下一個元素,在已經排序的元素序列中從後向前掃描
⒊ 如果該元素(已排序)大於新元素,將該元素移到下一位置
⒋ 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
⒌ 將新元素插入到下一位置中
⒍ 重複步驟2~5
數據結構C語言——實現各種排序算法
剛做完的
#include iostream
using namespace std;
void BiInsertsort(int r[], int n) //插入排序(折半)
{
for(int i=2;i=n;i++)
{
if (r[i]r[i-1])
{
r[0] = r[i]; //設置哨兵
int low=1,high=i-1; //折半查找
while (low=high)
{
int mid=(low+high)/2;
if (r[0]r[mid]) high=mid-1;
else low = mid+1;
}
int j;
for (j=i-1;jhigh;j–) r[j+1] = r[j]; //後移
r[j+1] = r[0];
}
}
for(int k=1;k=n;k++) coutr[k]” “;
cout”\n”;
}
void ShellSort ( int r[], int n) //希爾排序
{
for(int d=n/2;d=1;d=d/2) //以d為增量進行直接插入排序
{
for (int i=d+1;i=n;i++)
{
r[0] = r[i]; //暫存被插入記錄
int j;
for( j=i-d; j0 r[0]r[j]; j=j-d) r[j+d] = r[j]; //記錄後移d個位置
r[j+d] = r[0];
}
}
for(int i=1;i=n;i++) coutr[i]” “;
cout”\n”;
}
void BubbleSort(int r[], int n) //起泡排序
{
int temp,exchange,bound;
exchange=n; //第一趟起泡排序的範圍是r[0]到r[n-1]
while (exchange) //僅當上一趟排序有記錄交換才進行本趟排序
{
bound=exchange;
exchange=0;
for (int j=1; jbound; j++) //一趟起泡排序
if (r[j]r[j+1])
{
temp=r[j];
r[j]=r[j+1];
r[j+1]=temp;
exchange=j; //記錄每一次發生記錄交換的位置
}
}
for(int i=1;i=n;i++) coutr[i]” “;
cout”\n”;
}
int Partition(int r[], int first, int end) //快速排序一次劃分
{
int i=first; //初始化
int j=end;
r[0]=r[first];
while (ij)
{
while (ij r[0]= r[j]) j–; //右側掃描
r[i]=r[j];
while (ij r[i]= r[0]) i++; //左側掃描
r[j]=r[i];
}
r[i]=r[0];
return i; //i為軸值記錄的最終位置
}
void QuickSort(int r[], int first, int end) //快速排序
{
if (firstend)
{ //遞歸結束
int pivot=Partition(r, first, end); //一次劃分
QuickSort(r, first, pivot-1);//遞歸地對左側子序列進行快速排序
QuickSort(r, pivot+1, end); //遞歸地對右側子序列進行快速排序
}
}
void SelectSort(int r[ ], int n) //簡單選擇排序
{
int i,j,index,temp;
for (i=1; in; i++) //對n個記錄進行n-1趟簡單選擇排序
{
index=i;
for (j=i+1; j=n; j++) //在無序區中選取最小記錄
if (r[j]r[index]) index=j;
if (index!=i)
{
temp=r[i];
r[i]=r[index];
r[index]=temp;
}
}
for(i=1;i=n;i++) coutr[i]” “;
cout”\n”;
}
void main()
{
const int numv=12;
int a[3][numv]={{0,6,13,19,23,37,39,41,45,48,58,86},{0,86,58,48,45,41,39,37,23,19,13,6},{0,23,13,48,86,19,6,41,58,37,45,39}};
int z1[numv],z2[numv];
int m,n;
cout”請選擇測試數據類型:⑴正序 ⑵逆序 ⑶隨機 [ 若跳出,請按⑷ ]” endl;
cinm;
while(m0m4)
{
cout”請選擇排序算法:⑴直接插入排序 ⑵希爾排序 ⑶冒泡排序 ⑷快速排序 \n ⑸簡單選擇排序”endl;
cinn;
switch(n)
{
case 1:
cout “直接插入排序前:” “\n”;
for(int j=1;jnumv;j++) couta[m-1][j]” “;
cout “\n直接插入排序結果為:” “\n”;
BiInsertsort(a[m-1],numv-1);
break;
case 2:
cout “\n希爾排序前:” “\n”;
for(int j=1;jnumv;j++) couta[m-1][j]” “;
cout “\n希爾排序結果為:” “\n”;
ShellSort(a[m-1], numv-1);
break;
case 3:
cout “\n冒泡排序前:” “\n”;
for(int k=1;knumv;k++) couta[m-1][k]” “;
cout “\n冒泡排序結果為:” “\n”;
BubbleSort(a[m-1], numv-1);
break;
case 4:
cout “\n快速排序前:” “\n”;
for(int j=1;jnumv;j++) couta[m-1][j]” “;
cout “\n快速排序結果為:” “\n”;
QuickSort(a[m-1],0,numv-1);
for(int i=1;inumv;i++)
couta[m-1][i]” “;
cout”\n”;
break;
case 5:
cout “\n簡單選擇排序前:” “\n”;
for(int j=1;jnumv;j++) couta[m-1][j]” “;
cout “\n簡單選擇排序結果為:” “\n”;
SelectSort(a[m-1],numv-1);
break;
default:
cout”輸入錯誤!”endl;
}
m=0;
cout”請選擇測試數據類型:⑴正序 ⑵逆序 ⑶隨機 [ 若跳出,請按⑷ ]” endl;
cinm;
}
if(m==4) cout”(*^__^*) 再見!”endl;
else cout”輸入錯誤!”endl;
}
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/196952.html