1. 程式人生 > >演算法初級02——荷蘭國旗問題、隨機快速排序、堆排序、桶排序、相鄰兩數的最大差值問題、工程中的綜合排序演算法

演算法初級02——荷蘭國旗問題、隨機快速排序、堆排序、桶排序、相鄰兩數的最大差值問題、工程中的綜合排序演算法

主要討論:荷蘭國旗問題、隨機快速排序、堆排序、穩定性、比較器、桶排序、相鄰兩數的最大差值問題和簡單介紹工程中的綜合排序演算法

 

題目一

給定一個數組arr,和一個數num,請把小於等於num的數放在陣列的左邊,大於num的數放在陣列的右邊。

要求額外空間複雜度O(1),時間複雜度O(N)

參考下面的程式碼即可


問題二(荷蘭國旗問題)

給定一個數組arr,和一個數num,請把小於num的數放在陣列的左邊,等於num的數放在陣列的中間,大於num的數放在陣列的右邊。

要求額外空間複雜度O(1),時間複雜度O(N)

    public static int[] partition(int
[] arr, int l, int r, int p) { int less = l - 1; int more = r + 1; while (l < more) { if (arr[l] < p) { swap(arr, ++less, l++); } else if (arr[l] > p) { swap(arr, --more, l); } else { l
++; } } return new int[] { less + 1, more - 1 }; } // for test public static void swap(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }

 

題目二

隨機快速排序的細節和複雜度分析

可以用荷蘭國旗問題來改進快速排序

時間複雜度O(N*logN),額外空間複雜度O(logN)

    public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        quickSort(arr, 0, arr.length - 1);
    }

    public static void quickSort(int[] arr, int l, int r) {
        if (l < r) {
            swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
            int[] p = partition(arr, l, r);
            quickSort(arr, l, p[0] - 1);
            quickSort(arr, p[1] + 1, r);
        }
    }

    public static int[] partition(int[] arr, int l, int r) {
        int less = l - 1;
        int more = r;
        while (l < more) {
            if (arr[l] < arr[r]) {
                swap(arr, ++less, l++);
            } else if (arr[l] > arr[r]) {
                swap(arr, --more, l);
            } else {
                l++;
            }
        }
        swap(arr, more, r);
        return new int[] { less + 1, more };
    }

    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

 

 

題目三

堆排序的細節和複雜度分析

時間複雜度O(N*logN),額外空間複雜度O(1)


堆結構非常重要


1,堆結構的heapInsert與heapify

2,堆結構的增大和減少

3,如果只是建立堆的過程,時間複雜度為O(N)

4,優先順序佇列結構,就是堆結構

    public static void heapSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }
        int size = arr.length;
        swap(arr, 0, --size);
        while (size > 0) {
            heapify(arr, 0, size);
            swap(arr, 0, --size);
        }
    }

    public static void heapInsert(int[] arr, int index) {
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

    public static void heapify(int[] arr, int index, int size) {
        int left = index * 2 + 1;
        while (left < size) {
            int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }

    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

 

題目四

排序演算法的穩定性及其彙總

冒泡、插入、歸併穩定。

選擇、快速、堆排序不穩定。

 

穩定性的含義?

值相等的情況下,相對次序得到保持。

 

為什麼要穩定性?

由現實的例子決定,例如預設序列是按照身高排序的,再按照年齡排序的時候,希望可以保留原始身高的資訊,再進行年齡排序。

 

題目五

有關排序問題的補充:

1,歸併排序的額外空間複雜度可以變成O(1),但是非常難,不需要掌握,可以搜“歸併排序 內部快取法”

2,快速排序可以做到穩定性問題,但是非常難,不需要掌握,可以搜“01 stable sort”

3,有一道題目,是奇數放在陣列左邊,偶數放在陣列右邊,還要求原始的相對次序不變,碰到這個問題,可以懟面試官。面試官非良人。


題目六

認識比較器的使用

 1 public class Code_09_Comparator {
 2 
 3     public static class Student {
 4         public String name;
 5         public int id;
 6         public int age;
 7 
 8         public Student(String name, int id, int age) {
 9             this.name = name;
10             this.id = id;
11             this.age = age;
12         }
13     }
14 
15     public static class IdAscendingComparator implements Comparator<Student> {
16 
17         @Override
18         public int compare(Student o1, Student o2) {
19             return o1.id - o2.id;
20         }
21 
22     }
23 
24     public static class IdDescendingComparator implements Comparator<Student> {
25 
26         @Override
27         public int compare(Student o1, Student o2) {
28             return o2.id - o1.id;
29         }
30 
31     }
32 
33     public static class AgeAscendingComparator implements Comparator<Student> {
34 
35         @Override
36         public int compare(Student o1, Student o2) {
37             return o1.age - o2.age;
38         }
39 
40     }
41 
42     public static class AgeDescendingComparator implements Comparator<Student> {
43 
44         @Override
45         public int compare(Student o1, Student o2) {
46             return o2.age - o1.age;
47         }
48 
49     }
50 
51     public static void printStudents(Student[] students) {
52         for (Student student : students) {
53             System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
54         }
55         System.out.println("===========================");
56     }
57 
58     public static void main(String[] args) {
59         Student student1 = new Student("A", 1, 23);
60         Student student2 = new Student("B", 2, 21);
61         Student student3 = new Student("C", 3, 22);
62 
63         Student[] students = new Student[] { student3, student2, student1 };
64         printStudents(students);
65 
66         Arrays.sort(students, new IdAscendingComparator());
67         printStudents(students);
68 
69         Arrays.sort(students, new IdDescendingComparator());
70         printStudents(students);
71 
72         Arrays.sort(students, new AgeAscendingComparator());
73         printStudents(students);
74 
75         Arrays.sort(students, new AgeDescendingComparator());
76         printStudents(students);
77 
78     }
79 
80 }

 

題目七

桶排序、計數排序、基數排序的介紹

1,非基於比較的排序,與被排序的樣本的實際資料狀況很有關係,所以實際中並不經常使用

2,時間複雜度O(N),額外空間複雜度O(N)

3,穩定的排序

    public static void bucketSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
        }
        int[] bucket = new int[max + 1];
        for (int i = 0; i < arr.length; i++) {
            bucket[arr[i]]++;
        }
        int i = 0;
        for (int j = 0; j < bucket.length; j++) {
            while (bucket[j]-- > 0) {
                arr[i++] = j;
            }
        }
    }

 

 

題目八

補充問題

給定一個數組,求如果排序之後,相鄰兩數的最大差值,要求時間複雜度O(N),且要求不能用非基於比較的排序。

 

解題思路:

1、準備N+1個桶,遍歷陣列找到最小最大值

2、Min放在第一個桶,max放在最大的桶,然後把最小到最大的這個範圍等分為N+1份

 

3、那麼兩邊放入最大和最小,必然中間會有一個空桶。桶內部的差值肯定沒有桶範圍的大。

 

4、所以流程為:N個數準備N+1個桶,0~N,當一個數進入桶,每個桶只記錄出現的最小值和最大值,還有一個bool記錄這個桶有沒有進入過數。

5、遍歷完成後,計算費控桶直接的差值,用min和max計算,如果是空桶就跳過。

6、設定一個空桶的原因是,用於否定最大差值一定不來自一個桶內部,因為有空桶的存在,那空桶兩側的非空桶差值,肯定大於或者等於任意桶內容最大和最小值的差值。

 

7、上面的式子就是右邊先計算出,每個桶放多少個數,再把num-min為分子,可以看當前數佔用在哪個位置上。例如有10個數,最大最小範圍為1~100那就是說10/100=1/10,當前數要在放以10為分母的中,去計算他該去哪個桶。

 

題目九

介紹一下工程中的綜合排序演算法

少於60個數的直接用插入排序。(前期用遞迴分一半一半的,變小了再按照其他排序方法來排序,綜合利用各排序的優點)

基礎型別用快速排序,因為基礎型別相同分數無差異。

自定義型別用歸併排序,因為關乎多類資料,需要保持原始排序。