日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

排序算法整合(冒泡,快速,希爾,拓撲,歸并)

 xiaoyimin 2019-08-26

冒泡排序介紹

冒泡排序(Bubble Sort),又被稱為氣泡排序或泡沫排序。

它是一種較簡單的排序算法。它會遍歷若干次要排序的數(shù)列,每次遍歷時,它都會從前往后依次的比較相鄰兩個數(shù)的大小;如果前者比后者大,則交換它們的位置。這樣,一次遍歷之后,最大的元素就在數(shù)列的末尾! 采用相同的方法再次遍歷時,第二大的元素就被排列在最大元素之前。重復(fù)此操作,直到整個數(shù)列都有序為止!

冒泡排序圖文說明

  1. /*
  2. * a -- 待排序的數(shù)組
  3. * n -- 數(shù)組的長度
  4. */
  5. public static void bubbleSort(int[] a, int n) {
  6. int i,j;
  7. for (i=n-1; i>0; i--) {
  8. // 將a[0...i]中最大的數(shù)據(jù)放在末尾
  9. for (j=0; j<i; j++) {
  10. if (a[j] > a[j+1]) {
  11. // 交換a[j]和a[j+1]
  12. int tmp = a[j];
  13. a[j] = a[j+1];
  14. a[j+1] = tmp;
  15. }
  16. }
  17. }
  18. }

運行:

  1. int[] a = {20,40,30,10,60,50,70};
  2. String aa = '冒泡排序';
  3. bubbleSort(a,a.length);
  4. System.out.print(aa);
  5. for (int d : a) {
  6. System.out.print(d+',');
  7. }

快速排序介紹

快速排序(Quick Sort)使用分治法策略。

它的基本思想是:選擇一個基準數(shù),通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分;其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小。然后,再按此方法對這兩部分數(shù)據(jù)分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數(shù)據(jù)變成有序序列。

快速排序流程:

  1. 從數(shù)列中挑出一個基準值。

  2. 將所有比基準值小的擺放在基準前面,所有比基準值大的擺在基準的后面(相同的數(shù)可以到任一邊);在這個分區(qū)退出之后,該基準就處于數(shù)列的中間位置。

  3. 遞歸地把'基準值前面的子數(shù)列'和'基準值后面的子數(shù)列'進行排序。

  4. 圖文介紹

 

代碼實現(xiàn):

  1. /**
  2. *
  3. * 參數(shù)說明:
  4. * a -- 待排序的數(shù)組
  5. * l -- 數(shù)組的左邊界(例如,從起始位置開始排序,則l=0)
  6. * r -- 數(shù)組的右邊界(例如,排序截至到數(shù)組末尾,則r=a.length-1)
  7. */
  8. public static void quickSort(int[] a, int l, int r) {
  9. if (l < r) {
  10. int i,j,x;
  11. i = l;
  12. j = r;
  13. x = a[i];
  14. while (i < j) {
  15. while(i < j && a[j] > x)
  16. j--; // 從右向左找第一個小于x的數(shù)
  17. if(i < j)
  18. a[i++] = a[j];
  19. while(i < j && a[i] < x)
  20. i++; // 從左向右找第一個大于x的數(shù)
  21. if(i < j)
  22. a[j--] = a[i];
  23. }
  24. a[i] = x;
  25. quickSort(a, l, i-1); /* 遞歸調(diào)用 */
  26. quickSort(a, i+1, r); /* 遞歸調(diào)用 */
  27. }
  28. }

運行:

  1. String aa = '快速排序';
  2. quickSort(a,0,a.length-1);
  3. System.out.print(aa);
  4. for (int d : a) {
  5. System.out.print(d+',');
  6. }

直接插入排序介紹

直接插入排序(Straight Insertion Sort)的基本思想是:把n個待排序的元素看成為一個有序表和一個無序表。開始時有序表中只包含1個元素,無序表中包含有n-1個元素,排序過程中每次從無序表中取出第一個元素,將它插入到有序表中的適當(dāng)位置,使之成為新的有序表,重復(fù)n-1次可完成排序過程。

直接插入排序圖文說明

 

代碼實現(xiàn):

  1. /**
  2. * @param
  3. * a -- 待排序的數(shù)組
  4. * n -- 數(shù)組的長度
  5. */
  6. public static void insertSort(int[] a, int n) {
  7. int i, j, k;
  8. for (i = 1; i < n; i++) {
  9. //為a[i]在前面的a[0...i-1]有序區(qū)間中找一個合適的位置
  10. for (j = i - 1; j >= 0; j--)
  11. if (a[j] < a[i])
  12. break;
  13. //如找到了一個合適的位置
  14. if (j != i - 1) {
  15. //將比a[i]大的數(shù)據(jù)向后移
  16. int temp = a[i];
  17. for (k = i - 1; k > j; k--)
  18. a[k + 1] = a[k];
  19. //將a[i]放到正確位置上
  20. a[k + 1] = temp;
  21. }
  22. }
  23. }

 運行和冒泡一樣。。。。。

希爾排序:

希爾(Shell)排序又稱為縮小增量排序,它是一種插入排序。它是直接插入排序算法的一種威力加強版。該方法因DL.Shell于1959年提出而得名。

希爾排序的基本思想是:

把記錄按步長 gap 分組,對每組記錄采用直接插入排序方法進行排序。

隨著步長逐漸減小,所分成的組包含的記錄越來越多,當(dāng)步長的值減小到 1 時,整個數(shù)據(jù)合成為一組,構(gòu)成一組有序記錄,則完成排序。

我們來通過演示圖,更深入的理解一下這個過程。 

在上面這幅圖中:

初始時,有一個大小為 10 的無序序列。

在第一趟排序中,我們不妨設(shè) gap1 = N / 2 = 5,即相隔距離為 5 的元素組成一組,可以分為 5 組。接下來,按照直接插入排序的方法對每個組進行排序。

在第二趟排序中,我們把上次的 gap 縮小一半,即 gap2 = gap1 / 2 = 2 (取整數(shù))。這樣每相隔距離為 2 的元素組成一組,可以分為 2 組。按照直接插入排序的方法對每個組進行排序。

在第三趟排序中,再次把 gap 縮小一半,即gap3 = gap2 / 2 = 1。 這樣相隔距離為 1 的元素組成一組,即只有一組。按照直接插入排序的方法對每個組進行排序。此時,排序已經(jīng)結(jié)束。

需要注意一下的是,圖中有兩個相等數(shù)值的元素 5 和 5 。我們可以清楚的看到,在排序過程中,兩個元素位置交換了。

所以,希爾排序是不穩(wěn)定的算法。

代碼實現(xiàn):

  1. /**
  2. * 希爾排序
  3. * @param list
  4. */
  5. public static void shellSort(int[] a) {
  6. int gap = a.length / 2;
  7. while (1 <= gap) {
  8. // 把距離為 gap 的元素編為一個組,掃描所有組
  9. for (int i = gap; i < a.length; i++) {
  10. int j = 0;
  11. int temp = a[i];
  12. // 對距離為 gap 的元素組進行排序
  13. for (j = i - gap; j >= 0 && temp < a[j]; j = j - gap) {
  14. a[j + gap] = a[j];
  15. }
  16. a[j + gap] = temp;
  17. }
  18. System.out.format('gap = %d:\t', gap);
  19. printAll(a);
  20. gap = gap / 2; // 減小增量
  21. }
  22. }
  23. // 打印完整序列
  24. public static void printAll(int[] a) {
  25. for (int value : a) {
  26. System.out.print(value + '\t');
  27. }
  28. System.out.println();
  29. }

運行參考冒泡、、、、、

拓撲排序介紹

拓撲排序(Topological Order)是指,將一個有向無環(huán)圖(Directed Acyclic Graph簡稱DAG)進行排序進而得到一個有序的線性序列。

這樣說,可能理解起來比較抽象。下面通過簡單的例子進行說明! 

例如,一個項目包括A、B、C、D四個子部分來完成,并且A依賴于B和D,C依賴于D。現(xiàn)在要制定一個計劃,寫出A、B、C、D的執(zhí)行順序。這時,就可以利用到拓撲排序,它就是用來確定事物發(fā)生的順序的。

在拓撲排序中,如果存在一條從頂點A到頂點B的路徑,那么在排序結(jié)果中B出現(xiàn)在A的后面。

拓撲排序的算法圖解

拓撲排序算法的基本步驟:

1. 構(gòu)造一個隊列Q(queue) 和 拓撲排序的結(jié)果隊列T(topological); 

2. 把所有沒有依賴頂點的節(jié)點放入Q; 

3. 當(dāng)Q還有頂點的時候,執(zhí)行下面步驟: 

3.1 從Q中取出一個頂點n(將n從Q中刪掉),并放入T(將n加入到結(jié)果集中); 

3.2 對n每一個鄰接點m(n是起點,m是終點); 

3.2.1 去掉邊<n,m>; 

3.2.2 如果m沒有依賴頂點,則把m放入Q; 

注:頂點A沒有依賴頂點,是指不存在以A為終點的邊。

以上圖為例,來對拓撲排序進行演示。

第1步:將B和C加入到排序結(jié)果中。 

頂點B和頂點C都是沒有依賴頂點,因此將C和C加入到結(jié)果集T中。假設(shè)ABCDEFG按順序存儲,因此先訪問B,再訪問C。訪問B之后,去掉邊<B,A>和<B,D>,并將A和D加入到隊列Q中。同樣的,去掉邊<C,F>和<C,G>,并將F和G加入到Q中。 

  1. 將B加入到排序結(jié)果中,然后去掉邊<B,A>和<B,D>;此時,由于A和D沒有依賴頂點,因此并將A和D加入到隊列Q中。 

  2. 將C加入到排序結(jié)果中,然后去掉邊<C,F>和<C,G>;此時,由于F有依賴頂點D,G有依賴頂點A,因此不對F和G進行處理。 

第2步:將A,D依次加入到排序結(jié)果中。 

    第1步訪問之后,A,D都是沒有依賴頂點的,根據(jù)存儲順序,先訪問A,然后訪問D。訪問之后,刪除頂點A和頂點D的出邊。 

第3步:將E,F,G依次加入到排序結(jié)果中。

因此訪問順序是:B -> C -> A -> D -> E -> F -> G

拓撲排序的代碼說明

拓撲排序是對有向無向圖的排序。下面以鄰接表實現(xiàn)的有向圖來對拓撲排序進行說明。

1. 基本定義

  1. public class ListDG {
  2. // 鄰接表中表對應(yīng)的鏈表的頂點
  3. private class ENode {
  4. int ivex;
  5. // 該邊所指向的頂點的位置
  6. ENode nextEdge;
  7. // 指向下一條弧的指針
  8. }
  9. // 鄰接表中表的頂點
  10. private class VNode {
  11. char data;
  12. // 頂點信息
  13. ENode firstEdge;
  14. // 指向第一條依附該頂點的弧
  15. };
  16. private VNode[] mVexs;
  17. // 頂點數(shù)組
  18. ...
  19. }
  1. ListDG是鄰接表對應(yīng)的結(jié)構(gòu)體。 mVexs則是保存頂點信息的一維數(shù)組。 

  2. VNode是鄰接表頂點對應(yīng)的結(jié)構(gòu)體。 data是頂點所包含的數(shù)據(jù),而firstEdge是該頂點所包含鏈表的表頭指針。 

  3. ENode是鄰接表頂點所包含的鏈表的節(jié)點對應(yīng)的結(jié)構(gòu)體。 ivex是該節(jié)點所對應(yīng)的頂點在vexs中的索引,而nextEdge是指向下一個節(jié)點的。

2. 拓撲排序

  1. /*
  2. * 拓撲排序
  3. *
  4. * 返回值:
  5. * -1 -- 失敗(由于內(nèi)存不足等原因?qū)е?
  6. * 0 -- 成功排序,并輸入結(jié)果
  7. * 1 -- 失敗(該有向圖是有環(huán)的)
  8. */
  9. public int topologicalSort() {
  10. int index = 0;
  11. int num = mVexs.size();
  12. int[] ins;
  13. // 入度數(shù)組
  14. char[] tops;
  15. // 拓撲排序結(jié)果數(shù)組,記錄每個節(jié)點的排序后的序號。
  16. Queue<Integer> queue;
  17. // 輔組隊列
  18. ins = new int[num];
  19. tops = new char[num];
  20. queue = new LinkedList<Integer>();
  21. // 統(tǒng)計每個頂點的入度數(shù)
  22. for(int i = 0; i < num; i++) {
  23. ENode node = mVexs.get(i).firstEdge;
  24. while (node != null) {
  25. ins[node.ivex]++;
  26. node = node.nextEdge;
  27. }
  28. }
  29. // 將所有入度為0的頂點入隊列
  30. for(int i = 0; i < num; i ++)
  31. if(ins[i] == 0)
  32. queue.offer(i);
  33. // 入隊列
  34. while (!queue.isEmpty()) {
  35. // 隊列非空
  36. int j = queue.poll().intValue();
  37. // 出隊列。j是頂點的序號
  38. tops[index++] = mVexs.get(j).data;
  39. // 將該頂點添加到tops中,tops是排序結(jié)果
  40. ENode node = mVexs.get(j).firstEdge;
  41. // 獲取以該頂點為起點的出邊隊列
  42. // 將與'node'關(guān)聯(lián)的節(jié)點的入度減1;
  43. // 若減1之后,該節(jié)點的入度為0;則將該節(jié)點添加到隊列中。
  44. while(node != null) {
  45. // 將節(jié)點(序號為node.ivex)的入度減1。
  46. ins[node.ivex]--;
  47. // 若節(jié)點的入度為0,則將其'入隊列'
  48. if( ins[node.ivex] == 0)
  49. queue.offer(node.ivex);
  50. // 入隊列
  51. node = node.nextEdge;
  52. }
  53. }
  54. if(index != num) {
  55. System.out.printf('Graph has a cycle\n');
  56. return 1;
  57. }
  58. // 打印拓撲排序結(jié)果
  59. System.out.printf('== TopSort: ');
  60. for(int i = 0; i < num; i ++)
  61. System.out.printf('%c ', tops[i]);
  62. System.out.printf('\n');
  63. return 0;
  64. }

說明: 

  1. queue的作用就是用來存儲沒有依賴頂點的頂點。它與前面所說的Q相對應(yīng)。 

  2. tops的作用就是用來存儲排序結(jié)果。它與前面所說的T相對應(yīng)。

歸并排序

基本思想

歸并排序(MERGE-SORT)是利用歸并的思想實現(xiàn)的排序方法,該算法采用經(jīng)典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題然后遞歸求解,而治(conquer)的階段則將分的階段得到的各答案'修補'在一起,即分而治之)。

分而治之

可以看到這種結(jié)構(gòu)很像一棵完全二叉樹,本文的歸并排序我們采用遞歸去實現(xiàn)(也可采用迭代的方式去實現(xiàn))。分階段可以理解為就是遞歸拆分子序列的過程,遞歸深度為log2n。

合并相鄰有序子序列

再來看看治階段,我們需要將兩個已經(jīng)有序的子序列合并成一個有序序列,比如上圖中的最后一次合并,要將[4,5,7,8]和[1,2,3,6]兩個已經(jīng)有序的子序列,合并為最終序列[1,2,3,4,5,6,7,8],來看下實現(xiàn)步驟。

代碼實現(xiàn)

  1. package sortdemo;
  2. import java.util.Arrays;
  3. /**
  4. * Created by chengxiao on 2016/12/8.
  5. */
  6. public class MergeSort {
  7. public static void main(String []args){
  8. int []arr = {9,8,7,6,5,4,3,2,1};
  9. sort(arr);
  10. System.out.println(Arrays.toString(arr));
  11. }
  12. public static void sort(int []arr){
  13. int []temp = new int[arr.length];
  14. //在排序前,先建好一個長度等于原數(shù)組長度的臨時數(shù)組,
  15. //避免遞歸中頻繁開辟空間
  16. sort(arr,0,arr.length-1,temp);
  17. }
  18. private static void sort(int[] arr,int left,int right,int []temp){
  19. if(left<right){
  20. int mid = (left+right)/2;
  21. sort(arr,left,mid,temp);
  22. //左邊歸并排序,使得左子序列有序
  23. sort(arr,mid+1,right,temp);
  24. //右邊歸并排序,使得右子序列有序
  25. merge(arr,left,mid,right,temp);
  26. //將兩個有序子數(shù)組合并操作
  27. }
  28. }
  29. private static void merge(int[] arr,int left,int mid,int right,int[] temp){
  30. int i = left;//左序列指針
  31. int j = mid+1;//右序列指針
  32. int t = 0;//臨時數(shù)組指針
  33. while (i<=mid && j<=right){
  34. if(arr[i]<=arr[j]){
  35. temp[t++] = arr[i++];
  36. }else {
  37. temp[t++] = arr[j++];
  38. }
  39. }
  40. while(i<=mid){//將左邊剩余元素填充進temp中
  41. temp[t++] = arr[i++];
  42. }
  43. while(j<=right){//將右序列剩余元素填充進temp中
  44. temp[t++] = arr[j++];
  45. }
  46. t = 0;
  47. //將temp中的元素全部拷貝到原數(shù)組中
  48. while(left <= right){
  49. arr[left++] = temp[t++];
  50. }
  51. }
  52. }

最后

歸并排序是穩(wěn)定排序,它也是一種十分高效的排序,能利用完全二叉樹特性的排序一般性能都不會太差。java中Arrays.sort()采用了一種名為TimSort的排序算法,就是歸并排序的優(yōu)化版本。從上文的圖中可看出,每次合并操作的平均時間復(fù)雜度為O(n),而完全二叉樹的深度為|log2n|。總的平均時間復(fù)雜度為O(nlogn)。而且,歸并排序的最好,最壞,平均時間復(fù)雜度均為O(nlogn)。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多