排序是計算機內經常進行的一種操作,其目的是將一組「無序」的記錄序列調整為「有序」的記錄序列。分內部排序和外部排序,若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序。反之,若參加排序的記錄數量很大,整個序列的排序過程不可能在記憶體中完成,則稱此類排序問題為外部排序。內部排序的過程是乙個逐步擴大記錄的有序序列長度的過程。
本文將介紹幾種常見的內部排序
以下排序方法通常是建立在順序表上,也就是我們通常所說的陣列
時間複雜度o(n2)
演算法思想:
l(0)不存放元素,作為「哨兵」(起監督作用),如下圖所示,乙個完整 元素陣列邏輯上可看做成下面四個板塊,哨兵、有序序列、l(i)、無序序列
目的是將元素l(i)插入前面板塊的有序序列中,l(1)只有乙個元素,故預設有序,然後將第二個元素,也就是l(2)插入到前面的有序板塊中,首先將自己賦值給l(0),然後利用哨兵l(0)與前面有序板塊的末位向前依次比較大小,如遇到比自己大的元素,則將該大元素向後挪一位,一直向前比較,直到遇到比自己小(演算法不穩定)/小於等於(演算法穩定)的元素(最差情況是遇到與自己相等的哨兵),然後插入這個元素的前面一位(直接將哨兵的值賦值給其前面一位)
舉例說明:4、6、8、5、3(哨兵l(0)= l(i)= 5)
(4、6、8 處於有序序列板塊,現要將5插入有序板塊中;)
1.哨兵l(0)=5 與8比較,5小於8,8向後挪位,整個序列變為 4、6、8、8、3;
2.下一步:6與l(0)比較,6比l(0)大,6向後挪位,序列變為4、6、6、8、3;
3.下一步:4與l(0)比較,4比l(0)小,不挪位,將哨兵插入,第乙個6換成l(0),序列變為4、5、6、8、3;
4.後面的 3 進行插入排序同理
具體程式實現如下:
//插入排序
void
insertion_sort
(int a,
int n)
a[j+1]
=a[0];
//直接將哨兵的值賦值給其前面一位}}
}
時間複雜度o(n2)
演算法思想:主體思想與上文中的直接插入排序相同,區別在於簡單插入排序是在有序區逐一與哨兵比較,而折半插入排序是先定位到待插入位置loci,然後將有序區loci後面的元素向後挪位,最後將哨兵賦值安置在待插入位置loci;
注意這裡就是為了找到失敗位置,即使有序區有與哨兵相同的元素,也不能像之前我們學習的折半查詢一樣退出(break),而是在high//折半插入排序
void
half_insert_sort
(int a,
int n)
else
}for
(j = i-
1; j>high;
--j)
a[high+1]
=a[0];
//將哨兵賦值安置在待插入位置loci
}}空間複雜度為o(1)、時間複雜度約為o(n1.3)、最壞情況下時間複雜度為o(n2)
演算法思想:隸屬於插入排序,將整個排序列表以dk增量序列(此增量序列為認為隨機定義,唯一要求是增量序列的最後乙個元素是1,如5、3、1或者6、5、3、1或者9、5、3、1都可以)分成dk個組,然後在各個組中分別進行插入排序,注意是插入排序(簡單插入排序),不是比較然後交換位置
常用增量序列(希爾推薦)是dk = n/2、dk=dk/2、、、1
具體程式實現如下:
(注意最內部的for迴圈中j>0是必須的,這個是很重要的for迴圈終止條件)
//希爾排序
void
shell_sort
(int a,
int n)
a[j+dk]
=a[0];
}}}}
時間複雜度o(n2)
演算法思想:氣泡排序比較簡單,也見得比較多,也就是在原n個兩兩比較,然後一直將大的元素往後排(兩兩交換位置),直到最後,實現最大(最小)元素浮出,也就是冒泡過程,接下來在剩下的n-1個元素中同樣進行此操作。
具體程式實現如下:
//氣泡排序
void
bubble_sort
(int a,
int n)}if
(flag ==
false)}
}
下文講解快速排序應該是很清晰了,可檢視下文,用python解釋的,不會python的也能看明白
利用python詳講快速排序演算法
時間複雜度o(n2)
簡單選擇是穩定的(參考於嚴蔚敏老師主編的《資料結構》)
演算法思想:每次找到剩餘元素的最小元素,然後將每趟最前面元素與該最小元素互換位置,實現最小元素沉底,最小元素一次存放於i=1、2、3…的位置
外層迴圈實現驅動,定位於每趟的最前面元素;內層迴圈致力於尋找每趟剩餘元素中的最小元素的座標;然後回到外層迴圈,將每趟最前面元素與該最小元素互換位置。
具體程式實現如下:
//選擇排序
void
select_sort
(int a,
int n)
} tempelement = a[i]
; a[i]
= a[tempkey]
; a[tempkey]
= tempelement;
}}
演算法思想:與鍊錶的歸併排序一樣的原理。
區別:在於順序錶用的是遞迴的方法,鍊錶中用的是迴圈。
相同:宣告備用空陣列(鍊錶中的是空鍊錶),然後每次歸併比較,將大的數放入備用陣列,最後將備用陣列中的值複製進入源陣列,遞迴實現的時候,能實現分塊存入不同的地方。
int a=
;//a[0]是哨兵,不存放元素
display
(a,12);
int b[12]
=;merge_sort
(a,1,11
,b);
//歸併排序
display
(a,12
);
//歸併排序
void
merge_sort
(int a,
int low,
int high,
int b)
return;}
void
merge
(int a,
int low,
int mid,
int high,
int b)
else
}//如果兩段小序列中的任何一串行未排完
while
(begin_first<=mid)
while
(begin_second<=high)
//printf("b: ");①
//display(b,i);②
//將備份陣列b排序元素還原到a陣列
for(i = low; i <= high;
++i)
}
每次備份陣列中的元素複製進入源陣列時,存入的下標為(我這裡下標0不存放元素):
1、21、2、3
1、2、3(4、5)
1、2、3、4、5
…舉例:如果我開啟上面**中的①②的注釋,結果如下:
演算法思想:逐層剝離(個位、十位、百位…),然後根據每次剝離的數放到不同的佇列中(此處用的是乙個10維陣列,此處命名為桶),然後按佇列(先進先出,因此穩定)讀出,這串串行中最高位有多少位,就迴圈多少次,就可以逐個分離排序
//基數排序
void
radix_sort
(int a,
int n)
//barrel桶歸零
for(
int i =
0; i <10;
++i)
}//將每個元素分裝各個桶
for(
int i =
1; i < n;
++i)
//將元素從各個桶中取出來放到a陣列中
base =1;
for(
int j =
0; j <10;
++j)}}
}}intgetdigits
(int a,
int n)
while
(tempvalue/
10!=0)
if(maxdigits < count)
}return maxdigits;
}
最後,給出各個排序的效能比較,希望能有所收穫~
資料結構 內部排序
內部排序演算法 時間複雜度 o n 2 o n 2 o n 2 空間複雜度 o 1 o 1 o 1 演算法名稱 插入 選擇 冒泡 演算法名稱 希爾 堆 快速 歸併 基數 空間複雜度 o 1 o 1 o logn o n o 2rd 時間複雜度 o nlogn o nlogn o nlogn o nl...
資料結構6 2內部排序
假設含n個記錄的序列為 其對應的關鍵字序列為 這些關鍵字的排列方式有多種,其中至少有一種排列方式能使得關鍵字之間存在著這樣乙個關係 kp1 kp2 kpn 按此關係將記錄序列重新排列為 即為有序記錄,將這一過程稱為排序。若在乙個記錄序列中ki kj,且在排序前的序列中ri領先於rj。若在排序後的序列...
資料結構 內部排序法
總結一些演算法的實現 交換排序 1.冒泡演算法 typedef struct arr1 arr1 void bubblesort arr1 goal,int n if flag m沒有交換時證明排玩了,跳出迴圈結束排序。return 2.快速排序 時間複雜度為o nlog2n 空間複雜度為o log...