插入排序,希爾排序,選擇排序,快速排序,氣泡排序,歸併排序
插入排序
1、介紹:
簡單插入排序演算法原理:從整個待排序列中選出一個元素插入到已經有序的子序列中去,得到一個有序的、元素加一的子序列,直到整個序列的待插入元素為0,則整個序列全部有序。
在實際的演算法中,我們經常選擇序列的第一個元素作為有序序列(因為一個元素肯定是有序的),我們逐漸將後面的元素插入到前面的有序序列中,直到整個序列有序。
2,圖解
9 為第一個元素,第一個元素都是有序的。
具體程式碼:
/*插入排序*/
private static int[] InserSort(int[] a){
int i,j,num=0;
//下包從第二個值開始,遍歷所有值
for(i=1; i<a.length ; i++){
//每次排序a[i]與前面的所有值比較
for(j=0;j<=i;j++){
if(a[i]<a[j]){
//如果小,則插入到數列中,所有資料往後移動
int temp = a[i];
for(int k=i;k>j;k--){
a[k] = a[k-1];
}
a[j] = temp;
}
}
System.out.println("第"+(++num)+"次排序: "+Arrays.toString(a));
}
return a;
}
希爾排序
1、介紹:
希爾排序是希爾(Donald Shell)於1959年提出的一種排序演算法。希爾排序也是一種插入排序,它是簡單插入排序經過改進之後的一個更高效的版本,也稱為縮小增量排序,同時該演算法是衝破O(n2)的第一批演算法之一。本文會以圖解的方式詳細介紹希爾排序的基本思想及其程式碼實現。
2,圖解
3、程式碼
/*希爾排序*/
private static void shell() {
int i; //i為掃描次數
int j; //以j來定位比較的元素
int k=1; //k列印計數
int tmp; //tmp用來暫存資料
int jmp; //設定間隔位移量
jmp=size/2; //獲取增量,且分組
while (jmp != 0)
{
for (i=jmp ;i<size ;i++)
{
tmp=data[i]; //組內最後一個值賦值給tmp
j=i-jmp;
System.out.println(" j ==>" + j+"\t"+" i ==>" + i +"\t"+"data["+j+"]==>"+data[j]+"\t"+"tmp ==>"+tmp );
if(tmp < data[j]) //組內排序
{
data[i] = data[j];
data[j]=tmp;
}
System.out.println(Arrays.toString(data));
}
System.out.println("第"+ (k++) +"次增量為【"+jmp+"】排序結果:"+Arrays.toString(data));
System.out.println("==================================================");
jmp=jmp/2; //控制迴圈數
}
}
選擇排序
1、簡介
選擇排序(Selection sort)是一種簡單直觀的排序演算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
2、圖解
1、從左到右順序比較
2、第一個元素依次比較第二個元素直到最後一個元素
(這裡需要注意,當第一個元素髮生交換時,並不停止比較,而是用替換後的元素,繼續比較,這就能在首次找到最小值)
3、第二個元素依次比較第三個元素直到最後一個元素
4、以此類推
3、程式碼
/*選擇排序*/
private static int[] SelectSort(int[] a){
int temp,num = 0;
int i ,j;
for(i=0;i<a.length-1;i++){
for(j=i+1;j<a.length;j++){
if(a[i] > a[j]){
temp = a[j];
a[j] = a[i];
a[i]=temp;
}
}
System.out.println("第"+(++num)+"趟排序: "+Arrays.toString(a));
}
return a;
}
快速排序
方法其實很簡單:分別從初始序列“6 1 2 7 9 3 4 5 10 8”兩端開始“探測”。這裡將 6 設定為基準數,先從右往左找一個小於6的數,再從左往右找一個大於6的數,然後交換他們。這裡可以用兩個變數i和j,分別指向序列最左邊和最右邊。我們為這兩個變數起個好聽的名字“哨兵i”和“哨兵j”。剛開始的時候讓哨兵i指向序列的最左邊(即i=1),指向數字6。讓哨兵j指向序列的最右邊(即=10),指向數字。
首先哨兵j開始出動。因為此處設定的基準數是最左邊的數,所以需要讓哨兵j先出動,這一點非常重要(請自己想一想為什麼)。哨兵j一步一步地向左挪動(即j–),直到找到一個小於6的數停下來。接下來哨兵i再一步一步向右挪動(即i++),直到找到一個數大於6的數停下來。最後哨兵j停在了數字5面前,哨兵i停在了數字7面前。
現在交換哨兵i和哨兵j所指向的元素的值。交換之後的序列如下:
6 1 2 5 9 3 4 7 10 8
到此,第一次交換結束。接下來開始哨兵j繼續向左挪動(再友情提醒,每次必須是哨兵j先出發)。他發現了4(比基準數6要小,滿足要求)之後停了下來。哨兵i也繼續向右挪動的,他發現了9(比基準數6要大,滿足要求)之後停了下來。此時再次進行交換,交換之後的序列如下:
6 1 2 5 4 3 9 7 10 8
第二次交換結束,“探測”繼續。哨兵j繼續向左挪動,他發現了3(比基準數6要小,滿足要求)之後又停了下來。哨兵i繼續向右移動,糟啦!此時哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。說明此時“探測”結束。我們將基準數6和3進行交換。交換之後的序列如下:
3 1 2 5 4 6 9 7 10 8
到此第一輪“探測”真正結束。此時以基準數6為分界點,6左邊的數都小於等於6,6右邊的數都大於等於6。回顧一下剛才的過程,其實哨兵j的使命就是要找小於基準數的數,而哨兵i的使命就是要找大於基準數的數,直到i和j碰頭為止。
OK,解釋完畢。現在基準數6已經歸位,它正好處在序列的第6位。此時我們已經將原來的序列,以6為分界點拆分成了兩個序列,左邊的序列是“3 1 2 5 4”,右邊的序列是“9 7 10 8”。接下來還需要分別處理這兩個序列。因為6左邊和右邊的序列目前都還是很混亂的。不過不要緊,我們已經掌握了方法,接下來只要模擬剛才的方法分別處理6左邊和右邊的序列即可。現在先來處理6左邊的序列現吧。
左邊的序列是“3 1 2 5 4”。請將這個序列以3為基準數進行調整,使得3左邊的數都小於等於3,3右邊的數都大於等於3。好了開始動筆吧
如果你模擬的沒有錯,調整完畢之後的序列的順序應該是:
2 1 3 5 4
OK,現在3已經歸位。接下來需要處理3左邊的序列“2 1”和右邊的序列“5 4”。對序列“2 1”以2為基準數進行調整,處理完畢之後的序列為“1 2”,到此2已經歸位。序列“1”只有一個數,也不需要進行任何處理。至此我們對序列“2 1”已全部處理完畢,得到序列是“1 2”。序列“5 4”的處理也仿照此方法,最後得到的序列如下:
1 2 3 4 5 6 9 7 10 8
對於序列“9 7 10 8”也模擬剛才的過程,直到不可拆分出新的子序列為止。最終將會得到這樣的序列,如下
1 2 3 4 5 6 7 8 9 10
到此,排序完全結束。細心的同學可能已經發現,快速排序的每一輪處理其實就是將這一輪的基準數歸位,直到所有的數都歸位為止,排序就結束了。下面上個霸氣的圖來描述下整個演算法的處理過程。
2、程式碼
private static void quick(int d[], int left, int right) {
int i, j, tmp;
int lf_idx;
int rg_idx;
//1:第一個鍵值為d[lf]
if (left < right) {
lf_idx = left + 1;
rg_idx = right;
//單次迴圈內排序
while (true) {
System.out.print("[處理過程" + (++process) + "]=> " + Arrays.toString(data) + "\n");
for (j = right; j >= left + 1; j--) //3:由右向左找出一個鍵值小於d[lf]者
{
if (d[j] <= d[left]) {
rg_idx = j;
for (i = left + 1; i <= rg_idx; i++) //2:由左向右找出一個鍵值大於d[lf]者
{
if ((d[i] >= d[left]) || (i == rg_idx)) {
lf_idx = i;
break;
}
}
break;
}
}
if (lf_idx < rg_idx) //4-1:若lf_idx<rg_idx
{
tmp = d[lf_idx];
d[lf_idx] = d[rg_idx]; //則d[lf_idx]和d[rg_idx]互換
d[rg_idx] = tmp; //然後繼續排序
} else {
break; //否則跳出排序過程
}
}
//重新設定哨兵,通過遞迴先將左邊排好,再將右邊排好
if (lf_idx == rg_idx) //5-1:若lf_idx大於等於rg_idx
{ //則將d[left]和d[rg_idx]互換
tmp = d[left];
d[left] = d[rg_idx];
d[rg_idx] = tmp;
//5-2:並以rg_idx為基準點分成左右兩半
quick(d, left, rg_idx - 1); //以遞迴方式分別為左右兩半進行排序
quick(d, rg_idx + 1, right); //直至完成排序
}
}
}
氣泡排序
1、簡介:氣泡排序(Bubble Sort,臺灣譯為:泡沫排序或氣泡排序)是一種簡單的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
2、圖解
3、程式碼
private static int[] BubbleSort(int[] a){
int temp,num = 0;
int i , j ;
for(i = a.length - 1; i > 0; i--){
for(j = 0 ; j < i; j++) {
if(a[j] > a[j+1]){
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
System.out.println("第"+(++num)+"趟排序: "+Arrays.toString(a));
}
return a;
}
歸併排序
1、簡介: 歸併排序(MERGE-SORT)是利用歸併的思想實現的排序方法,該演算法採用經典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題然後遞迴求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。
2、圖解
3、程式碼:
private static int[] sort(int[] nums, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左邊
sort(nums, low, mid);
// 右邊
sort(nums, mid + 1, high);
// 左右歸併
merge(nums, low, mid, high);
}
return nums;
}
private static void merge(int[] nums, int low, int mid, int high) {
System.out.println("合併的次數"+(++k));
int[] temp = new int[high - low + 1];
int i = low;// 左指標
int j = mid + 1;// 右指標
int k = 0;
// 把較小的數先移到新陣列中
while (i <= mid && j <= high) {
if (nums[i] < nums[j]) {
temp[k++] = nums[i++];
} else {
temp[k++] = nums[j++];
}
}
// 把左邊剩餘的數移入陣列
while (i <= mid) {
temp[k++] = nums[i++];
}
// 把右邊邊剩餘的數移入陣列
while (j <= high) {
temp[k++] = nums[j++];
}
// 把新陣列中的數覆蓋nums陣列
for (int k2 = 0; k2 < temp.length; k2++) {
nums[k2 + low] = temp[k2];
}
}