java幾種排序算法

本文出自 “在雲端的追夢” 博客,請務必保留此出處http://computerdragon.blog.51cto.com/6235984/1161859

 

 

一 簡要介紹

一般排序均值的是將一個已經無序的序列數據重新排列成有序的

常見的排序分爲:

1 插入類排序

主要就是對於一個已經有序的序列中,插入一個新的記錄。它包括:直接插入排序,折半插入排序和希爾排序

2 交換類排序

這類排序的核心就是每次比較都要“交換”,在每一趟排序都會兩兩發生一系列的“交換”排序,但是每一趟排序都會讓一個記錄排序到它的最終位置上。它包括:起泡排序,快速排序

3 選擇類排序

每一趟排序都從一系列數據中選擇一個最大或最小的記錄,將它放置到第一個或最後一個爲位置交換,只有在選擇後才交換,比起交換類排序,減少了交換記錄的時間。屬於它的排序:簡單選擇排序,堆排序

4 歸併類排序

將兩個或兩個以上的有序序列合併成一個新的序列

5 基數排序

主要基於多個關鍵字排序的。

下面針對上面所述的算法,講解一些常用的java代碼寫的算法

二 插入類排序之直接插入排序

直接插入排序,一般對於已經有序的隊列排序效果好。

基本思想:每趟將一個待排序的關鍵字按照大小插入到已經排序好的位置上。

算法思路,從後往前先找到要插入的位置,如果小於則就交換,將元素向後移動,將要插入數據插入該位置即可。時間複雜度爲O(n2),空間複雜度爲O(1)

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

package sort.algorithm;

public class DirectInsertSort {

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        int data[] = { 2, 6, 10, 3, 9, 80, 1, 16, 27, 20 };

        int temp, j;

        for (int i = 1; i < data.length; i++) {

            temp = data[i];

            j = i - 1;

            // 每次比較都是對於已經有序的

            while (j >= 0 && data[j] > temp) {

                data[j + 1] = data[j];

                j--;

            }

            data[j + 1] = temp;

        }

        // 輸出排序好的數據

        for (int k = 0; k < data.length; k++) {

            System.out.print(data[k] + "  ");

        }

    }

}


三 插入類排序之折半插入排序(二分法排序)

條件:在一個已經有序的隊列中,插入一個新的元素

折半插入排序記錄的比較次數與初始序列無關

思想:折半插入就是首先將隊列中取最小位置low和最大位置high,然後算出中間位置mid

將中間位置mid與待插入的數據data進行比較,

如果mid大於data,則就表示插入的數據在mid的左邊,high=mid-1;

如果mid小於data,則就表示插入的數據在mid的右邊,low=mid+1

最後整體進行右移操作。

時間複雜度O(n2),空間複雜度O(1)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

package sort.algorithm;

//折半插入排序

public class HalfInsertSort {

    public static void main(String[] args) {

        int data[] = { 2, 6, 10, 3, 9, 80, 1, 16, 27, 20 };

        // 存放臨時要插入的元素數據

        int temp;

        int low, mid, high;

        for (int i = 1; i < data.length; i++) {

            temp = data[i];

            // 在待插入排序的序號之前進行折半插入

            low = 0;

            high = i - 1;

            while (low <= high) {

                mid = (low + high) / 2;

                if (temp < data[mid])

                    high = mid - 1;

                else

                    // low=high的時候也就是找到了要插入的位置,

                    // 此時進入循環中,將low加1,則就是要插入的位置了

                    low = mid + 1;

            }

            // 找到了要插入的位置,從該位置一直到插入數據的位置之間數據向後移動

            for (int j = i; j >= low + 1; j--)

                data[j] = data[j - 1];

            // low已經代表了要插入的位置了

            data[low] = temp;

        }

        for (int k = 0; k < data.length; k++) {

            System.out.print(data[k] + "  ");

        }

    }

}

四 插入類排序之希爾排序

希爾排序,也叫縮小增量排序,目的就是儘可能的減少交換次數,每一個組內最後都是有序的。

將待續按照某一種規則分爲幾個子序列,不斷縮小規則,最後用一個直接插入排序合成

空間複雜度爲O(1),時間複雜度爲O(nlog2n)

算法先將要排序的一組數按某個增量d(n/2,n爲要排序數的個數)分成若干組,每組中記錄的下標相差d.對每組中全部元素進行直接插入排序,然後再用一個較小的增量(d/2)對它進行分組,在每組中再進行直接插入排序。當增量減到1時,進行直接插入排序後,排序完成。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

package sort.algorithm;

public class ShellSort {

    public static void main(String[] args) {

        int a[] = { 1, 54, 6, 3, 78, 34, 12, 45, 56, 100 };

        double d1 = a.length;

        int temp = 0;

        while (true)

        {

            //利用這個在將組內倍數減小

            //這裏依次爲5,3,2,1

            d1 = Math.ceil(d1 / 2);

            //d爲增量每個分組之間索引的增量

            int d = (int) d1;

            //每個分組內部排序

            for (int x = 0; x < d; x++)

            {

                //組內利用直接插入排序

                for (int i = x + d; i < a.length; i += d) {

                    int j = i - d;

                    temp = a[i];

                    for (; j >= 0 && temp < a[j]; j -= d) {

                        a[j + d] = a[j];

                    }

                    a[j + d] = temp;

                }

            }

                                                                                                                               

            if (d == 1)

                break;

        }

        for (int i = 0; i < a.length; i++)

            System.out.print(a[i]+"  ");

    }

}


五 交換類排序之冒泡排序

交換類排序核心就是每次比較都要進行交換

冒泡排序:是一種交換排序

每一趟比較相鄰的元素,較若大小不同則就會發生交換,每一趟排序都能將一個元素放到它最終的位置!每一趟就進行比較。

時間複雜度O(n2),空間複雜度O(1)

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package sort.algorithm;

//冒泡排序:是一種交換排序

public class BubbleSort {

    // 按照遞增順序排序

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        int data[] = { 2, 6, 10, 3, 9, 80, 1, 16, 27, 20, 13, 100, 37, 16 };

        int temp = 0;

        // 排序的比較趟數,每一趟都會將剩餘最大數放在最後面

        for (int i = 0; i < data.length - 1; i++) {

            // 每一趟從開始進行比較,將該元素與其餘的元素進行比較

            for (int j = 0; j < data.length - 1; j++) {

                if (data[j] > data[j + 1]) {

                    temp = data[j];

                    data[j] = data[j + 1];

                    data[j + 1] = temp;

                }

            }

        }

        for (int i = 0; i < data.length; i++)

            System.out.print(data[i] + " ");

    }

}


六 交換類排序之快速排序

快速排序算法,初始的時候選擇一個軸線,一般來說選擇第一個元素,每一趟排序交換後,最後出現的就是該軸左邊比它小,右邊比他大!交替掃描,先從右邊開始掃描,如果遇到比它小的就停止,將該元素與軸線交換,馬上換成從左開始掃描,如果遇到比它大的就停止,將該元素與軸線數據交換,重複這樣的!一般就是遞歸做的

時間複雜度O(nlog2n),平均時間是最好的

空間複雜度O(long2n),快速排序需要遞歸用到了棧

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

package sort.algorithm;

//快速排序算法:是一種交換排序

public class QuikSort {

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        int data[] = { 2, 6, 10, 3, 9, 80, 1, 16, 27, 20, 105, 34, 44, 19 };

        QuikSort sort = new QuikSort();

        sort.sortArray(data, 0, data.length - 1);

        for (int i = 0; i < data.length; i++)

            System.out.print(data[i] + " ");

    }

    // 這裏不用返回值,直接對傳入的數組進行操作

    public void sortArray(int data[], int first, int end) {

        int temp;

        int i = first, j = end;

        if (first < end) {

            temp = data[i];

            // 當i=j的時候,則說明掃描完成了

            while (i < j) {

                // 從右邊向左邊掃描找到一個小於temp的元素

                while (j > i && data[j] > temp)

                    j--;

                if (i < j) {

                    // 將該元素賦值給temp

                    data[i] = data[j];

                    // 賦值後就應該將i+1指向下一個序號

                    i++;

                }

                // 然後從左邊向右邊開始掃描,找到一個大於temp的元素

                while (i < j && temp > data[i])

                    i++;

                if (i < j) {

                    // 將該元素賦值給temp

                    data[j] = data[i];

                    // 賦值後就應該將j-1指向前一個序號

                    j--;

                }

            }

            // 將軸數據放在i位置中

            data[i] = temp;

            sortArray(data, first, i - 1);

            sortArray(data, i + 1, end);

        }

    }

}


七 選擇類排序之簡單選擇排序

簡單選擇排序,每一趟從數據中選擇一個最小值放到最前面,但是不需要交換位置,只記錄該交換的位置,只有找到後才做一次交換!不同於冒泡之處在於,不進行頻繁的交換,快於冒泡排序

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

package sort.algorithm;

//簡單選擇排序:是一種選擇排序

public class SelectSort {

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        int data[] = { 2, 6, 10, 3, 9, 80, 1, 16, 27, 20, 11, 3, 1, 100, 89 };

        int temp, k;

        // 開始時間

        long start = System.nanoTime();

        // 選擇的每一趟數,每一趟都會將一個最小的放在最前面。

        for (int i = 0; i < data.length - 1; i++) {

            // 使用k來記錄要交換的位置,且k在比較的過程不斷變化

            k = i;

            // 由於每一趟都會將最小的放在最前面,所以索引+1

            for (int j = i; j < data.length; j++)

                // 這裏始終要與k比較

                if (data[j] < data[k])

                    k = j;

            // k已經存放了交換的位置了

            temp = data[i];

            data[i] = data[k];

            data[k] = temp;

        }

        System.out.println(System.nanoTime() - start);

        // 輸出排序好的數據

        for (int m = 0; m < data.length; m++) {

            System.out.print(data[m] + "  ");

        }

    }

}



八 選擇類排序之堆排序

 

堆排序就是建立大頂堆或者小頂堆,若建立大頂堆,每次對於建好的大頂堆將根元素與最後一個元素交換,無序的數目減少,有序的數目增加。

對於求N個數據中的前n個最小的數據,首先就是建立一個n個的大頂堆,然後讓其餘的元素來進行與這堆頂元素比較,如果小於則與堆頂互換元素。

這裏採用數組存儲節點,並且下標統一從0,length-1,所以對於這樣處理的左孩子節點下標爲

2 * i+1,右孩子的節點下標爲2 * i+2

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

package sort.algorithm;

public class HeapSort {

    public static int heap_size;

    // 左孩子編號

    public static int leftChild(int i) {

        return 2 * i+1;

    }

    // 右孩子編號

    public static int rightChild(int i) {

        return 2 * i + 2;

    }

    /**

     * 保持最大堆的性質

     * 堆中的數組元素

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

     * 由於左右孩子都是最大堆,首先比較根元素與左右孩子,找出最大值,假如大於根元素,則調整兩個元素的值;

     * 由於左孩子(右孩子)的值與根元素交換,有可能打破左子樹(右子樹)的最大堆性質,因此繼續調用,直至葉子元素。

     */

    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);

        }

    }

    /**

     * 建立最大堆。在數據中,下標a.length/2+1一直到最後的元素a.length-1都是葉子元素

     * 因此從其前一個元素開始,一直到

     * 第一個元素,重複調用max_heapify函數,使其保持最大堆的性質

     */

    public static void build_max_heap(int[] a) {

        //從0~a.length/2中建立最大堆

        for (int i = a.length / 2; i >= 0; i--)

        {

            max_heapify(a, i);

        }

    }

    /**

     * 堆排序:首先建立最大堆,然後將堆頂元素(最大值)與最後一個值交換,同時使得 堆的長度減小1

     * 調用保持最大堆性質的算法調整,使得堆頂元素成爲最大值,此時最後一個元素已被排除在外、

     */

    public static void heapSort(int[] a) {

        //構建最大堆

        build_max_heap(a);

        for (int i = a.length - 1; i >= 0; i--)

        {

            //將第一個元素和最後一個元素進行互換

            int temp = a[0];

            a[0] = a[i];

            a[i] = temp;

                                                                                                                                                                                                                                                                                

            heap_size--;

            //調整堆爲最大堆

            max_heapify(a, 0);

        }

    }

    public static void main(String[] args) {

        int a[] = {5, 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] + "  ");

        }

    }

}


九 二路歸併排序

歸併排序主要分爲分割和歸併,每次分割後,對於每一個部分進行排序,然後進行歸併,建立一個臨時表存儲歸併後的結果,在將兩路進行歸併的時候,每一路都已經是有序的。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

package sort.algorithm;

import java.util.Arrays;

//二路歸併排序主要分爲

//分割和合並

public class MergeSort {

    public static void main(String[] args) {

        int data[] = { 2, 6, 10, 3, 9, 80, 1, 16, 27, 20 };

        mergeSort(data,0,data.length-1);

        //直接打印

        System.out.println(Arrays.toString(data));

    }

    //二路歸併的分割處理

    public static void mergeSort(int[] array,int start,int end)

    {

        if(start<end)

        {

            //劃分爲兩部分,每次兩部分進行歸併

            int mid=(start+end)/2;

            //兩路歸併

            //先遞歸處理每一個部分

            mergeSort(array,start,mid);

            mergeSort(array,mid+1,end);

            //然後將已經排序好的,兩兩歸併排序再進行合併處理

            merge(array,start,mid,mid+1,end);

        }

    }

    //二路歸併兩個部分的時候進行排序

    public static void merge(int[] array,int start1,int end1,int start2,int end2)

    {

        int i=start1;//左路起始索引

        int j=start2;//右路起始索引

        int k=0;

        //歸併的時候,會將兩個數組數據按照大小輸入到一個臨時數組中

        //建立臨時長度爲兩個子列表長度的數組

        int[] temp=new int[end2-start1+1];

        //循環遍歷,按順序找出兩個表中的最小數據依次放入臨時表中

        //注意此時左路和右路已經是有序的了。

        //當一路有一個小的,則會索引加1,繼續喝另外一路的上次索引進行比較

        while(i<=end1&&j<=end2)

        {

            //這裏確定歸併的次序大小

            if(array[i]>array[j])

                temp[k++]=array[j++];

            else

                temp[k++]=array[i++];

        }

        //把剩下的元素放入臨時數組中,只有一路的

        while(i<=end1)

            temp[k++]=array[i++];

        while(j<=end2)

            temp[k++]=array[j++];

        k=start1;

        for(int item:temp)

            array[k++]=item;

    }

}



十 各種排序總結:

時間複雜度:巧記“快些以nlog2n歸隊”,快代表快速排序,些代表希爾排序,歸代表歸併排序,隊代表堆排序

算法穩定性:巧記“心情不穩定,快些選一堆人吧”,快代表快速排序,些代表希爾排序,選代表選擇排序,隊代表堆排序

從一大堆中選擇最大的幾個或者最小的幾個數,直接用堆排序

原始序列有序號,直接用插入排序

經過一趟排序能使一個元素達到它最終位置的是交換類排序(冒泡,快速)和選擇類排序(簡單選擇,堆)。

排序方法元素比較次數與原始序列無關---簡單選擇排序,折半插入排序

排序方法的排序趟數和原始隊列無有關--交換類排序


本文出自 “在雲端的追夢” 博客,請務必保留此出處http://computerdragon.blog.51cto.com/6235984/1161859



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