整理的排序算法總結

一,三種代價爲O(n*n)的排序算法
(1)插入排序
思路:逐個處理待排序的紀錄,每個新紀錄與前面的已經排序的子序列進行比較,插入正確的位置中。
(從小到大)
public static void insertSort(int[] A) {
          for (int i = 1; i < A.length; i ++)
              for (int j = i ; j > 0 && A[j] < A[j - 1]; j--) {
                  int temp = A[j];
                 A[j] = A[j - 1];
                 A[j - 1] = temp;
             }
    }
最佳時間 比較O(n)    序列按照從小到大排列好
最差時間 比較O(n*n)  系列完全反過來排列
(2)冒泡排序
    思路:每次從序列最後一個往上比較,把這趟最小的冒泡至上面

public static void bubbleSort(int[] A) {
          for (int i = 0; i < A.length; i++)
              for (int j = A.length - 1; j > i; j--) {
                  if (A[j] < A[j - 1]) {
                       int temp = A[j];
                      A[j] = A[j - 1];
                      A[j - 1] = temp;
                 }
             }
    }
時間:O(N*N)
(3)選擇排序
思路:每次找到未排序中的最小的值交換一下即可,交換n-1次
public static void selectSort(int[] A) {
          for (int i = 0; i < A.length - 1; i++) {
              int lowIndex = i;
              for (int j = i+1; j <A.length; j++) {
                  if (A[j] < A[lowIndex])
                      lowIndex = j;
             }
              if(lowIndex!=i){
              int temp = A[lowIndex];
             A[lowIndex] = A[i];
             A[i] = temp;
             }
         }
    }
時間:O(N*N) 比較次數還是n*n,但是交換的次數只是n-1比冒泡排序少很多,選擇排序的實質就是冒泡排序!!!
(2)代價爲N(1.5)次方的Shell排序
思路:將序列分成子序列,然後分別對子序列進行排序,最後將子序列組合起來
(每個子序列用插入排序方法進行排序)
//incr 是增量 n是數組長度
void insertSortForSehll(int A[],int n,int incr){
     for(int i = incr;i<n;i+=incr)
     for(int j=i;(j>=incr)&&A[j]<A[j-incr];j-=incr)
         swap(A,j,j-incr);
}

void shellSort(int A[],int n){
   for(int i=n/2;i>2;i/=2    ){
     for(int j=0;j<i;j++)
        insertSort(&A[j],n-j,i);
   }
   insertSort(A,n,1);
}
3.代價爲O(n*longN)的算法
(1)快速排序
int partition(int A[] ,int l,int r,int pivot){
    do{
       while(A[++l]<pivot)
       while(r!=0&&A[--r]>pivot);
       swap(A[l],A[r]);
       }while(l<r);
  swap(A[l],A[r]);  //do while 導致多了一次交換,會把l和r換一次,所以需要一次換回來(這個其實不太好,主要是條件不對)
return l;
}

void  qsort(int A[],int i,int j){
      if(i<=j)return ;
      int pivotIndex = findPivot(A,i,j);
      swap(A,pivotIndex,j);
      int k = partition(A,i-1,j,A[j]);
      swap(A,k,j);
      qsort(A,i,k-1);
      qsort(A,k+1,j);
}
時間:O(N*logN)
(2)歸併排序
(感覺和shell排序是反過來 的)
36 20 17 13 28 14 23 15
20 36|13 1714|28|15 23
13 17 20 36|14 15 23 28
13 14 15 17 20 23 28 36
    privateint[] MergeSort(int[] i_list) {
        if(i_list.length == 1) {
            returni_list;
        }else {
            int[] listL = new int[i_list.length / 2];
            int[] listR = new int[i_list.length - i_list.length / 2];
            intCenter = i_list.length / 2;
            for(inti = 0; i < Center; i++) {
                listL[i] = i_list[i];
            }
            for(inti = Center, j = 0; i < i_list.length; i++, j++) {
                listR[j] = i_list[i];
            }
 
            int[] SortedListL=MergeSort(listL);
            int[] SortedListR=MergeSort(listR);
            int[] o_list = MergeTwoList(SortedListL, SortedListR);
            returno_list;
        }
    } 
 privateint[] MergeTwoList(int[] listL, int[] listR) {
        inti = 0, j = 0;
        int[] o_list = new int[listL.length + listR.length];
        intfoot = 0;
        while(i < listL.length && j < listR.length) {
            if(listL[i] <= listR[j]) {
                o_list[foot] = listL[i];
                i++;
            }else {
                o_list[foot] = listR[j];
                j++;
            }
            foot++;
        }
 
        if(i == listL.length) {
            while(j < listR.length) {
                o_list[foot++] = listR[j++];
            }
        }else { // j==listR.length
            while(i < listL.length) {
                o_list[foot++] = listL[i++];
            }
        }
        returno_list;
    }
}
(3)堆排序
定義:堆可以視爲一棵完全的二叉樹,完全二叉樹的一個“優秀”的性質是,除了最底層之外,每一層都是滿的,這使得堆可以利用數組來表示(普通的一般的二叉樹通常用鏈表作爲基本容器表示),每一個結點對應數組中的一個元素。
思路:
1.建堆 o(n)
2.刪除堆最大值放入數組的末尾
3.刪除之後繼續進行維持堆性質
4,直到堆爲空

通常“堆”是通過數組來實現,這樣可以利用數組的特點快速定位指定索引的元素。

當面試的時候被問到,你對堆有沒有做過一些瞭解??

當時我我反問了一下:您是指堆數據結構還是JVM裏面存儲對象等信息的堆時??

當時感覺自己還挺高端的,居然可以反問,但是當面試官說是堆排序的時候我就傻眼了,

不會,只記得個大概,於是今天花了點時候把堆排序搞懂了!!

詳解如下:

對於一個數組:

int a[] = { 0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };

他可以用來表示堆,堆是一顆完全二叉樹,分大值堆和小值堆,最後得出的分別是從大到小的排列順序和從小到達的排列順序,看你要排序的順序覺定大小堆!

                         0(1)

         4 (2)                       1(3)

      3(4)          2(5)          16(6)       9(7)

10(8)   14(9)    8(10)     7(11)

存在一個如上圖的關係,

left[i] = 2* i ;

right[i] = 2+i +1;

知道這個關係後,我們就可以開始建堆了:

從 a.length/2 到第一個元素 開始對每個元素進行建堆(遞歸),使每個元素下的樹都維持最小值堆的特性;

a.length/2+1 到 a.length 個元素都是葉子節點,所以不需要維持最小值堆操作,因爲已經是最小值堆了!!

代碼如下:

public static void max_heapify(int[] a, int i) {
int left = leftChild(i);
int right = rightChild(i);
int largest = 0;
if (left < heap_size && a[i] < a[left]) {
largest = left;
} else {
largest = i;
}
if (right < heap_size && a[right] > a[largest]) {
largest = right;
}
if (largest == i) {
return;
} else {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest);
}
}


上一步驟理解了,那麼之後的操作就很簡單了。

建好最小值堆後,每次取堆頂元素和數組最後一個元素(最後第n個元素,n爲交換次數)交換,剩下的元素採用維持堆性質的操作,

循環至最後堆只剩下一個元素即可,代碼如下:

public static void heapSort(int[] a) {
build_max_heap(a);
for (int i = a.length - 1; i >= 2; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heap_size--;
max_heapify(a, 1);
}
}

時間複雜度O(N*logN)

至此,堆排序講解完畢!希望大家可以明白堆排序的原理了!

附上完整可運行代碼:


package ddl.com;


public class HeapSort {
public static int heap_size;


// 雙親編號
public static int parent(int i) {
return i / 2;
}


// 左孩子編號
public static int leftChild(int i) {
return 2 * i;
}


// 右孩子編號
public static int rightChild(int i) {
return 2 * i + 1;
}


/**
* 堆排序:首先使用建立最大堆的算法建立好最大堆,然後將堆頂元素(最大值)與最後一個值交換,同時使得堆的長度減小1
* ,調用保持最大堆性質的算法調整,使得堆頂元素成爲最大值,此時最後一個元素已被排除在外、
*/
public static void heapSort(int[] a) {
build_max_heap(a);
for (int i = a.length - 1; i >= 2; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heap_size--;
max_heapify(a, 1);
}
}


/**
* 建立最大堆。在數據中,a.length/2+1一直到最後的元素都是葉子元素,也就是平凡最大堆,因此從其前一個元素開始,一直到
* 第一個元素,重複調用max_heapify函數,使其保持最大堆的性質

* @param a
*/
public static void build_max_heap(int[] a) {
for (int i = a.length / 2; i >= 1; i--) {
max_heapify(a, i);
}
}


/**
* 保持最大堆的性質

* @param a
*            ,堆中的數組元素
* @param i
*            ,對以該元素爲根元素的堆進行調整,假設前提:左右子樹都是最大堆

*            由於左右孩子都是最大堆,首先比較根元素與左右孩子,找出最大值,假如不是根元素,則調整兩個元素的值;
*            由於左孩子(右孩子)的值與根元素交換,有可能打破左子樹(右子樹)的最大堆性質,因此繼續調用,直至葉子元素。
*/
public static void max_heapify(int[] a, int i) {
int left = leftChild(i);
int right = rightChild(i);
int largest = 0;
if (left < heap_size && a[i] < a[left]) {
largest = left;
} else {
largest = i;
}
if (right < heap_size && a[right] > a[largest]) {
largest = right;
}
if (largest == i) {
return;
} else {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest);
}
}


public static void main(String[] args) {
int a[] = { 0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };
heap_size = a.length;
heapSort(a);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + "  ");
}
}
}
4分配排序和基數排序
(1)分配排序和位排序差不多O(n)典型的以空間換時間
for(i = 0;i<n;i++)
   B[A[i]] = A[[i];
基數排序:

第一步

以LSD爲例,假設原來有一串數值如下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81
首先根據個位數的數值,在走訪數值時將它們分配至編號0到9的桶子中:
0
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39

第二步

接下來將這些桶子中的數值重新串接起來,成爲以下的數列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
接着再進行一次分配,這次是根據十位數來分配:
0
1 14
2 22 28
3 39
4 43
5 55
6 65
7 73
8 81
9 93

第三步

接下來將這些桶子中的數值重新串接起來,成爲以下的數列:
14, 22, 28, 39, 43, 55, 65, 73, 81, 93
這時候整個數列已經排序完畢;如果排序的對象有三位數以上,則持續進行以上的動作直至最高位數爲止。
LSD的基數排序適用於位數小的數列,如果位數多的話,使用MSD的效率會比較好。MSD的方式與LSD相反,是由高位數爲基底開始進行分配,但在分配之後並不馬上合併回一個數組中,而是在每個“桶子”中建立“子桶”,將每個桶子中的數值按照下一數位的值分配到“子桶”中。在進行完最低位數的分配後再合併回單一的數組中。

2效率分析

時間效率:設待排序列爲n個記錄,d個關鍵碼,關鍵碼的取值範圍爲radix,則進行鏈式基數排序的時間複雜度爲O(d(n+radix)),其中,一趟分配時間複雜度爲O(n),一趟收集時間複雜度爲O(radix),共進行d趟分配和收集。 空間效率:需要2*radix個指向隊列的輔助空間,以及用於靜態鏈表的n個指針

O(n*longN)

public class RadixSort {
      public static void sort( int[] number, int d) {
           int k = 0;
           int n = 1;
           int m = 1;// 控制鍵值排序依據在哪一位
           int [][] temp = new int [number.length ][number. length];
           int [] order = new int[number. length];
           while (m <= d) {
               for (int i = 0; i < number. length; i++) {
                    int lsd = ((number[i] / n) % 10);
                   temp[lsd][order[lsd]] = number[i];
                   order[lsd]++;
              }
               for (int i = 0; i < d; i++) {
                    if (order[i] != 0)
                         for (int j = 0; j < order[i]; j++) {
                             number[k] = temp[i][j];
                             k++;
                        }
                   order[i] = 0;
              }
              n *= 10;
              k = 0;
              m++;
          }
     }

      public static void main(String[] args) {
           int [] data = { 73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100 };
          RadixSort. sort(data, 10);
           for (int i = 0; i < data. length; i++) {
              System. out .print(data[i] + " " );
          }
     }
}


































發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章