經典排序算法總結

排序算法總結

情緒(穩定性)不穩定:快些選堆java

排序名稱 時間複雜度 空間複雜度 穩定性 備註
選擇排序 O(n^2) O(1) 不穩定 運行時間和輸入無關;數據移動是最少的
插入排序 O(n^2),徹底有序變成O(n) O(1) 穩定 排序時間取決於初始值(使用交換方式)
冒泡排序 O(n^2),徹底有序變成O(n) O(1) 穩定
歸併排序 O(nlogn),徹底有序變成O(n) O(n) 穩定 求解逆序對、小和、染色等問題
快速排序 O(nlogn),含有相同元素數組,三路快排O(n) O(1) 不穩定 求解topK等問題
堆排序 O(nlogn) O(1) 不穩定 實現優先隊列
希爾排序 O(nlogn)-O(n^2) O(1) 不穩定 分組思想

選擇排序

public class SelectionSort {
    private SelectionSort() {
    }

    // 默寫版
    public static void selectionSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                minIndex = arr[minIndex] < arr[j] ? minIndex : j;
            }
            swap(arr, i, minIndex);
        }
    }

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

    // 選擇排序1:從前到後選最小
    public static <E extends Comparable<E>> void selectionSort(E[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                // 循環不變量:minIndex永遠指向最小的元素
                minIndex = arr[minIndex].compareTo(arr[j]) < 0 ? minIndex : j;
            }
            swap(arr, i, minIndex);
        }
    }

    // 選擇排序2從後到前選最大
    public static <E extends Comparable<E>> void selectionSort1(E[] arr) {
        for (int i = arr.length - 1; i >= 0; i--) {
            int maxIndex = i;
            for (int j = i - 1; j >= 0; j--) {
                maxIndex = arr[j].compareTo(arr[maxIndex]) > 0 ? j : maxIndex;
            }
            swap(arr, i, maxIndex);
        }
    }


    private static <E> void swap(E[] arr, int i, int j) {
        E temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

插入排序

public class InsertionSort {
    private InsertionSort() {
    }


    // 默寫版
    private static void insertSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int temp = arr[i];
            int j;
            // 從後往前插
            for (j = i; j - 1 >= 0 && temp < arr[j - 1]; j--) {
                arr[j] = arr[j - 1];
            }
            arr[j] = temp;
        }
    }

    // 插入排序1:取出元素插入,後面希爾排序會用到這種方式
    public static <E extends Comparable<E>> void insertSort1(E[] data) {
        // 外層循環:遍歷每一個數組元素,i左邊已經排好序,右邊待排序
        for (int i = 0; i < data.length; i++) {
            // 取出當前遍歷第一個元素,將它插入到前面已經排好序的數組中,合適的位置
            E temp = data[i];
            // 內層循環:遍歷左邊已經排好序的數組
            int j;
            // &&右邊的條件不能下放!由於j--會對一個位移
            for (j = i; j - 1 >= 0 && temp.compareTo(data[j - 1]) < 0; j--) {
                data[j] = data[j - 1];
            }
            // 內層發生移動,空出的j位置賦值給temp
            // 內層沒有發生移動,temp原地賦值給本身
            data[j] = temp;
        }
    }


    // 插入排序2:使用交換,性能沒有上面好
    public static <E extends Comparable<E>> void insertSort2(E[] arr) {
        for (int i = 1; i < arr.length; i++) {
            for (int j = i - 1; j >= 0 && arr[j].compareTo(arr[j + 1]) > 0; j++) {
                swap(arr, j, j + 1);
            }
        }
    }

    private static <E> void swap(E[] arr, int j, int i) {
        E t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

}

練習題

LC147_對鏈表進行排序

public class Solution {
    public ListNode insertionSortList(ListNode head) {
        if (head == null) {
            return null;
        }
        // 初始化三個指針:dummyNode防止cur插入到head位置
        ListNode dummyNode = new ListNode(0);
        dummyNode.next = head;
        ListNode lastSorted = head;
        ListNode cur = head.next;
        // cur指針遍歷,循環結束條件是cur==null
        while (cur != null) {
            // lastSorted後移條件
            if (lastSorted.val <= cur.val) {
                lastSorted = lastSorted.next;
            } else {
                // 不然,從頭遍歷鏈表找最後<=cur.val的節點
                ListNode pre = dummyNode;
                while (pre.next.val <= cur.val) {
                    pre = pre.next;
                }
                // 將cur插入到pre後一位,改變三者指向
                lastSorted.next = cur.next;
                cur.next = pre.next;
                pre.next = cur;
            }
            // cur爲待插入結點=last後一位
            cur = lastSorted.next;
        }
        // 返回dummyNode下一位
        return dummyNode.next;
    }
}

冒泡排序

public class BubbleSort {
    private BubbleSort() {
    }

    // 默寫版
    private static void bubbleSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            boolean isSwap = false;
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    swap1(arr, j, j + 1);
                    isSwap = true;
                }
            }
            if (!isSwap) {
                break;
            }
        }
    }

    // 不使用額外空間,不考慮數字越界的交換
    private static void swap1(int[] arr, int i, int j) {
        arr[j] = arr[i] + arr[j];
        arr[i] = arr[j] - arr[i];
        arr[j] = arr[j] - arr[i];
    }


    // 冒泡排序常規寫法:從後往前比較,這種寫法比較好記
    public static <E extends Comparable<E>> void bubbleSort0(E[] arr) {
        // 只須要n-1層外部循環
        for (int i = arr.length - 1; i > 0; i--) {
            for (int j = 0; j < i; j++) {
                if (arr[j].compareTo(arr[j + 1]) > 0) {
                    swap(arr, j, j + 1);
                }
            }
        }
    }

    // 冒泡排序1:優化設置交換標誌位
    public static <E extends Comparable<E>> void bubbleSort1(E[] arr) {
        // 外層實現比較次數:n-1次
        for (int i = 0; i < arr.length - 1; i++) {
            // 優化:設置交換標誌位
            boolean isSwap = false;
            // 內層實現相鄰元素比較:每趟只比較j和j+1的元素
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j].compareTo(arr[j + 1]) > 0) {
                    swap(arr, j, j + 1);
                    isSwap = true;
                }
            }
            // 內層沒有發生交換,說明前面已經都排好序了,就不用再次比較
            if (!isSwap) {
                break;
            }
        }
    }

    // 冒泡排序2:優化設置最後交換位置,取消掉i++
    public static <E extends Comparable<E>> void bubbleSort2(E[] arr) {
        for (int i = 0; i < arr.length - 1; ) {
            int swapNextIndex = 0;
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j].compareTo(arr[j + 1]) > 0) {
                    swap(arr, j, j + 1);
                    // 最後交換位置確定是靠後的j+1
                    swapNextIndex = j + 1;
                }
            }
            i = arr.length - swapNextIndex;
        }
    }


    private static <E> void swap(E[] arr, int i, int j) {
        E temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

希爾排序

希爾排序是對插入排序的一種改進嘗試,每次基於必定間隔的的兩數進行排序,直到間隔爲1再進行插入排序,此時幾乎有序插入排序效率變高node

public class ShellSort {
    private ShellSort() {
    }

    /**
     * 希爾排序:對插入排序一種優化,又叫縮小增量排序,每次將數組一個元素與相同增量區間比較排序,直到增量爲1
     */


    // 希爾排序1:利用插入排序更改
    public static <E extends Comparable<E>> void shellSort1(E[] arr) {
        // 希爾的步長
        int step = arr.length / 2;
        while (step >= 1) {
            // 插入排序
            insertSort(arr, step);
            step = step / 2;
        }
    }

    private static <E extends Comparable<E>> void insertSort(E[] arr, int step) {
        for (int i = step; i < arr.length; i++) {
            E t = arr[i];
            int j;
            for (j = i; j - step >= 0 && t.compareTo(arr[j - step]) < 0; j -= step) {
                arr[j] = arr[j - step];
            }
            arr[j] = t;
        }
    }

    // 希爾排序2:三重循環
    public static <E extends Comparable<E>> void shellSort2(E[] arr) {
        // 希爾的步長
        int step = arr.length / 2;
        while (step >= 1) {
            // start是每一個子數組的起始位置
            for (int start = 0; start < step; start++) {
                // 如下是使用插入排序
                // 對每一個子數組進行插入排序:data[start+step,start+2h...data.length-1]
                for (int i = start + step; i < arr.length; i += step) {
                    E temp = arr[i];
                    int j;
                    // 前一個元素是j-step
                    for (j = i; j - step >= 0 && temp.compareTo(arr[j - step]) < 0; j -= step) {
                        arr[j] = arr[j - step];
                    }
                    arr[j] = temp;
                }
            }
            step = step / 2;
        }
    }


    // 希爾排序3:修改步長序列
    public static <E extends Comparable<E>> void shellSort3(E[] arr) {
        // 希爾的步長
        int step = 1;
        while (step < arr.length) {
            step = step * 3 + 1;
        }
        while (step >= 1) {
            // 對每一個子數組進行插入排序:data[start+h,start+2h...data.length-1]
            for (int i = step; i < arr.length; i++) {
                E temp = arr[i];
                int j;
                // 前一個元素是j-step
                for (j = i; j - step >= 0 && temp.compareTo(arr[j - step]) < 0; j -= step) {
                    arr[j] = arr[j - step];
                }
                arr[j] = temp;
            }
            step = step / 3;
        }
    }
}

練習題

LC506_相對名次

public class Solution {
    // 排序法
    public String[] findRelativeRanks1(int[] score) {
        String[] result = new String[score.length];
        // 複製原數組到排序數組
        int[] sorted = new int[score.length];
        System.arraycopy(score, 0, sorted, 0, score.length);
        Arrays.sort(sorted);
        for (int i = 0; i < score.length; i++) {
            int index = score.length - Arrays.binarySearch(sorted, score[i]);
            switch (index) {
                case 1:
                    result[i] = "Gold Medal";
                    break;
                case 2:
                    result[i] = "Silver Medal";
                    break;
                case 3:
                    result[i] = "Bronze Medal";
                    break;
                default:
                    result[i] = String.valueOf(index);
            }
        }
        return result;
    }

    // 計數排序法
    public String[] findRelativeRanks2(int[] score) {
        String[] result = new String[score.length];
        int max = Integer.MIN_VALUE;
        for (int num : score) {
            max = Math.max(max, num);
        }
        int[] array = new int[max + 1];
        // 初始化arr數組,下標爲score的值,arr[i]爲該元素的排名
        for (int i = 0; i < score.length; i++) {
            array[score[i]] = i + 1;
        }
        int rank = 1;
        for (int i = array.length - 1; i >= 0; i--) {
            if (array[i] != 0) {
                switch (rank) {
                    case 1:
                        result[array[i] - 1] = "Gold Medal";
                        break;
                    case 2:
                        result[array[i] - 1] = "Silver Medal";
                        break;
                    case 3:
                        result[array[i] - 1] = "Bronze Medal";
                        break;
                    default:
                        result[array[i] - 1] = String.valueOf(rank);
                }
                rank++;
            }
        }
        return result;
    }
}

歸併排序

第一種版本是進行優化後的,便於對理解數組中的逆序對問題!算法

public class MergeSort {
    private MergeSort() {

    }

    public static <E extends Comparable<E>> void mergerSort(E[] arr) {
        // 優化:只生成一次輔助數組,使得merge中沒有偏移量的操做了
        // Arrays.copyOf(目標數組,開區間)
        E[] temp = Arrays.copyOf(arr, arr.length);
        mergerSort(arr, 0, arr.length - 1, temp);
    }

    private static <E extends Comparable<E>> void mergerSort(E[] arr, int l, int r, E[] temp) {
        // 優化2:指定長度內,可以使用直接插入排序優化,但這種優化效果不明顯,因此這裏放棄使用
        if (l >= r) {
            return;
        }
        // 先遞歸分解
        int mid = l + (r - l) / 2;
        mergerSort(arr, l, mid, temp);
        mergerSort(arr, mid + 1, r, temp);
        // 優化1:[l,mid]已經排序好,[mid+1...]後的位置
        if (arr[mid].compareTo(arr[mid + 1]) > 0) {
            merge(arr, l, mid, r, temp);
        }
    }

    private static <E extends Comparable<E>> void merge(E[] arr, int l, int mid, int r, E[] temp) {
        // System.arraycopy(複製數組,複製數組起始位置,目標數組,目標數組起始位置,複製數組長度開區間)
        System.arraycopy(arr, l, temp, l, r - l + 1);
        // p遍歷temp[l,mid];q遍歷temp[mid+1,r]
        int p = l, q = mid + 1;
        // i遍歷arr[l,r]
        for (int i = l; i <= r; i++) {
            if (p > mid) {
                arr[i] = temp[q++];
            } else if (q > r) {
                arr[i] = temp[p++];
            } else if (temp[p].compareTo(temp[q]) <= 0) {
                arr[i] = temp[p++];
            } else {
                arr[i] = temp[q++];
            }
        }
    }
}

第二版是左程元上課時教的,我的認爲是最容易理解和背誦的版本,但不利於書寫數組逆序對問題shell

public class MergeSort1 {
    private MergeSort1() {
    }

    // 左神歸併排序寫法
    public static void mergeSort(int[] arr) {
        if (arr.length < 2) {
            return;
        }
        mergeSort(arr, 0, arr.length - 1);
    }

    public static void mergeSort(int[] arr, int l, int r) {
        if (l == r) {
            return;
        }
        int mid = l + ((r - l) >> 1);
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);
        merge(arr, l, mid, r);
    }

    public static void merge(int[] arr, int l, int m, int r) {
        int[] temp = new int[r - l + 1];
        int i = 0;
        int p1 = l;
        int p2 = m + 1;
        while (p1 <= m && p2 <= r) {
            temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        }
        while (p1 <= m) {
            temp[i++] = arr[p1++];
        }
        while (p2 <= r) {
            temp[i++] = arr[p2++];
        }
        for (i = 0; i < temp.length; i++) {
            arr[l + i] = temp[i];
        }
    }

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

}

練習題

LC88_合併兩個有序數組

很是高頻的一道題數組

public class Solution {

    /**
     * 合併兩個有序數組,num1長度大於m+n,m是num1長度,n是nums2長度
     */

    // 方法一:逆向雙指針,不使用額外數組
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        // 初始化爲各自-1的位置
        int p1 = m - 1, p2 = n - 1, tail = m + n - 1;
        // temp存較大的數,從後往前放
        int temp;
        while (p1 >= 0 || p2 >= 0) {
            // temp獲取較大值
            if (p1 == -1) {
                temp = nums2[p2--];
            } else if (p2 == -1) {
                temp = nums1[p1--];
            } else if (nums1[p1] < nums2[p2]) {
                temp = nums2[p2--];
            } else {
                temp = nums1[p1--];
            }
            // 利用尾指針存放temp
            nums1[tail--] = temp;
        }
    }

    // 方法二:正向雙指針,須要使用額外數組
    public void merge1(int[] nums1, int m, int[] nums2, int n) {
        // 初始化從0開始
        int p1 = 0, p2 = 0;
        // 輔助變量存比較後的值
        int temp;
        int[] help = new int[m + n];
        while (p1 < m || p2 < n) {
            if (p1 == m) {
                temp = nums2[p2++];
            } else if (p2 == n) {
                temp = nums1[p1++];
            } else if (nums1[p1] < nums2[p2]) {
                temp = nums1[p1++];
            } else {
                temp = nums2[p2++];
            }
            // 輔助數組放下temp
            help[p1 + p2 - 1] = temp;
        }
        // 從help數組的0位置開始複製m+n個元素到nums1數組中,nums1數組從0位置開始接受
        System.arraycopy(help, 0, nums1, 0, m + n);
    }
}

劍指25_合併兩個有序鏈表

public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // dummyNode便於返回頭結點
        ListNode dummyNode = new ListNode(0);
        // cur初始化指向dummyNode
        ListNode cur = dummyNode;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                cur.next = l1;
                l1 = l1.next;
            } else {
                cur.next = l2;
                l2 = l2.next;
            }
            // 每次循環,cur後移
            cur = cur.next;
        }
        cur.next = l1 != null ? l1 : l2;
        return dummyNode.next;
    }
}

劍指52_數組中的逆序對問題

這道題很是高頻,而且對於理解歸併排序的歸併操做有很大幫助,建議dubug多看幾回less

public class Solution {
    private int res;

    // 歸併排序法,若是左邊大,那麼左邊未歸併的部分就是造成逆序對的個數
    public int reversePairs(int[] nums) {
        int[] temp = new int[nums.length];
        res = 0;
        sort(nums, 0, nums.length - 1, temp);
        return res;
    }

    private void sort(int[] arr, int l, int r, int[] temp) {
        if (l >= r) {
            return;
        }
        int mid = l + (r - l) / 2;
        sort(arr, l, mid, temp);
        sort(arr, mid + 1, r, temp);
        // >保證了,若是j位置小於i位置的,左邊未排序中的確定是逆序對
        if (arr[mid] > arr[mid + 1]) {
            merge(arr, l, mid, r, temp);
        }
    }

    private void merge(int[] arr, int l, int mid, int r, int[] temp) {
        System.arraycopy(arr, l, temp, l, r - l + 1);
        int i = l, j = mid + 1;
        // 每輪循環爲 arr[k] 賦值
        for (int k = l; k <= r; k++) {
            if (i > mid) {
                arr[k] = temp[j++];
            } else if (j > r) {
                arr[k] = temp[i++];
            } else if (temp[i] <= temp[j]) {
                arr[k] = temp[i++];
            } else {
                // 判斷逆序對條件:temp[i]>temp[j]
                res += mid - i + 1;
                arr[k] = temp[j++];
            }
        }
    }
}

快速排序

單路快排

public class QuickSort {
    private QuickSort() {
    }

    // 單路快排默寫版
    public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        Random random = new Random();
        quickSort(arr, 0, arr.length - 1, random);
    }

    private static void quickSort(int[] arr, int l, int r, Random random) {
        if (l >= r) {
            return;
        }
        int p = partition(arr, l, r, random);
        quickSort(arr, l, p - 1, random);
        quickSort(arr, p + 1, r, random);
    }

    private static int partition(int[] arr, int l, int r, Random random) {
        swap(arr, l, l + random.nextInt(r - l + 1));
        int less = l;
        for (int i = l + 1; i <= r; i++) {
            if (arr[i] < arr[l]) {
                swap(arr, i, ++less);
            }
        }
        swap(arr, l, less);
        return less;
    }


    public static <E extends Comparable<E>> void quickSort1(E[] arr) {
        Random random = new Random();
        quickSort1(arr, 0, arr.length - 1, random);
    }

    private static <E extends Comparable<E>> void quickSort1(E[] arr, int l, int r, Random random) {
        // 不單單是遞歸結束條件,更是上輪l和r保證左小右大的條件
        if (l >= r) return;
        // 將數組分區,隨機得到一個基數,小於基數放左邊,大於基數放右邊,返回基數最終位置下標
        int p = partition1(arr, l, r, random);
        // 對上一輪基數最終位置的左邊,遞歸排序
        quickSort1(arr, l, p - 1, random);
        // 對上一輪基數最終位置的右邊,遞歸排序
        quickSort1(arr, p + 1, r, random);
    }

    // partition:數組分區,隨機得到一個基數,小於基數放左邊,大於基數放右邊,返回基數最終位置下標
    private static <E extends Comparable<E>> int partition1(E[] arr, int l, int r, Random random) {
        // 隨機交換l和p位置上的數,解決數組有序問題
        int p = l + random.nextInt(r - l + 1);
        swap1(arr, l, p);
        // j初始化l
        int j = l;
        // 遍歷l後續數組元素,j指向<arr[l]的最後一個數,
        for (int i = l + 1; i <= r; i++) {
            if (arr[i].compareTo(arr[l]) < 0) {
                j++;
                swap1(arr, i, j);
            }
        }
        // 循環跳出,交換基數與小於基數最後一個數
        swap1(arr, l, j);
        // 因爲上一步交換,此時arr[j]就是基數最終位置,返回j
        return j;
    }


    private static <E> void swap1(E[] arr, int i, int j) {
        E t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

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

雙路快排

public class QuickSort2Ways {
    private QuickSort2Ways() {
    }

    // 雙路快速排序
    public static <E extends Comparable<E>> void quickSort2ways(E[] arr) {
        Random random = new Random();
        quickSort2ways(arr, 0, arr.length - 1, random);
    }

    private static <E extends Comparable<E>> void quickSort2ways(E[] arr, int l, int r, Random random) {
        if (l >= r) return;
        int p = partition(arr, l, r, random);
        quickSort2ways(arr, l, p - 1, random);
        quickSort2ways(arr, p + 1, r, random);
    }

    private static <E extends Comparable<E>> int partition(E[] arr, int l, int r, Random random) {
        int p = l + random.nextInt(r - l + 1);
        swap(arr, l, p);
        // i指向<=的區間的下一個元素,j指向>=區間的前一個元素
        int i = l + 1, j = r;
        while (true) {
            while (i <= j && arr[i].compareTo(arr[l]) < 0) {
                i++;
            }
            while (i <= j && arr[j].compareTo(arr[l]) > 0) {
                j--;
            }
            // 循環條件:i<j;相反就是結束條件
            if (i >= j) {
                break;
            }
            // 循環未結束,此時arr[i]>=基數,arr[j]<=基數,交換使得前小後大
            swap(arr, i, j);
            // 移動指針
            i++;
            j--;
        }
        // 循環跳出條件i>=j,arr[j]指向<=arr[l]的最後一個數,交換(l,j)
        swap(arr, l, j);
        return j;
    }

    private static <E> void swap(E[] arr, int i, int j) {
        E t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

三路快排

public class QuickSort3Ways {
    private QuickSort3Ways() {
    }

    // 三路快排
    public static <E extends Comparable<E>> void quickSort3ways(E[] arr) {
        Random random = new Random();
        quickSort3ways(arr, 0, arr.length - 1, random);
    }

    private static <E extends Comparable<E>> void quickSort3ways(E[] arr, int l, int r, Random random) {
        if (l >= r) return;
        int p = l + random.nextInt(r - l + 1);
        swap(arr, l, p);
        // 核心:arr[l+1,lt]<v ; arr[lt+1,i-1]=v ; arr[gt,r]>v
        // less指向<的最後一個元素,i指針遍歷,more指向>的第一個元素
        int less = l, i = l + 1, more = r + 1;
        while (i < more) {
            if (arr[i].compareTo(arr[l]) < 0) {
                less++;
                swap(arr, i, less);
                i++;
            } else if (arr[i].compareTo(arr[l]) > 0) {
                more--;
                swap(arr, i, more);
                // 原先的gt--後的值沒有比較過,i繼續指向它,不用i++
            } else {
                // arr[i]==arr[l]
                i++;
            }
        }
        swap(arr, l, less);
        // 三路快排拋棄掉中間的部分,再也不遞歸
        quickSort3ways(arr, l, less - 1, random);
        quickSort3ways(arr, more, r, random);
    }


    private static <E> void swap(E[] arr, int i, int j) {
        E t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

練習題

LC69_多數元素

public class Solution {
    // 排序法
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length / 2];
    }

    // map法
    public int majorityElement1(int[] nums) {
        Map<Integer, Integer> map = countMap(nums);
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() > (nums.length) / 2) {
                return entry.getKey();
            }
        }
        throw new RuntimeException("not have num");
    }

    private Map<Integer, Integer> countMap(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            if (!map.containsKey(num)) {
                map.put(num, 1);
            } else {
                map.put(num, map.get(num) + 1);
            }
        }
        return map;
    }

    // 候選人投票法
    public int majorityElement2(int[] nums) {
        int res = nums[0], res_count = 1;
        for (int i = 1; i < nums.length; i++) {
            if (res == nums[i]) {
                res_count++;
            } else {
                res_count--;
                if (res_count == 0) {
                    // 候選人投票數爲0,重置候選人爲下一個數
                    res = nums[i];
                    res_count = 1;
                }
            }
        }
        return res;
    }
}

堆排序

public class HeapSort {
    private HeapSort() {

    }

    // 基礎知識:設根節點0開始爲i,左孩子2i+1,右孩子2i+2
    // 設左右孩子爲i,父親節點爲(i-1)/2

    // 堆排序:原地堆排序
    public static <E extends Comparable<E>> void heapSort(E[] arr) {
        if (arr.length <= 1) {
            return;
        }
        // 1.對數組最右子樹父節點依次上浮操做,循環結束arr[0]=數組max
        for (int i = (arr.length - 1 - 1) / 2; i >= 0; i--) {
            siftUp(arr, i, arr.length);
        }
        // 2.第一次arr[0]=max,與數組末尾交換,每次交換,都對[0,i)進行下沉操做使得arr[0]=max
        for (int i = arr.length - 1; i >= 0; i--) {
            swap(arr, 0, i);
            siftUp(arr, 0, i);
        }

    }

    // 對 arr[0,bound)所造成的最大堆中,索引爲parentIndex的父親和其左右孩子,進行最大值上浮操做
    private static <E extends Comparable<E>> void siftUp(E[] arr, int parentIndex, int bound) {
        while (2 * parentIndex + 1 < bound) {
            int left = 2 * parentIndex + 1;
            if (left + 1 < bound && arr[left + 1].compareTo(arr[left]) > 0) {
                // left指向最大孩子節點下標
                left++;
            }
            // 循環結束條件:父元素比孩子最大還大
            if (arr[parentIndex].compareTo(arr[left]) >= 0) {
                break;
            }
            // 不然:交換父元數和孩子最大值
            swap(arr, parentIndex, left);
            // 父指針指向孩子最大值下標
            parentIndex = left;
        }
    }


    private static <E> void swap(E[] arr, int i, int j) {
        E t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

練習題

LC23_合併K個升序數組

public class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }
        // 最小值堆:按照lists中node的值從小到大排列。相同的值按照原先順序排列
        PriorityQueue<ListNode> minQueue = new PriorityQueue<>(lists.length, (node1, node2) -> {
            if (node1.val < node2.val) {
                return -1;
            } else if (node1.val == node2.val) {
                return 0;
            } else {
                return 1;
            }
        });
        // 啞結點和遍歷指針
        ListNode dummyNode = new ListNode(0);
        ListNode cur = dummyNode;
        // 遍歷原lists,將全部結點放入最小值堆中
        for (ListNode node : lists) {
            if (node != null) {
                minQueue.add(node);
            }
        }
        // 遍歷最小值堆,每次讓cur指向出隊的元素,cur後移,再讓cur.next從新入隊,始終讓堆頂保持最小值
        while (!minQueue.isEmpty()) {
            cur.next = minQueue.poll();
            cur = cur.next;
            if (cur.next != null) {
                minQueue.add(cur.next);
            }
        }
        return dummyNode.next;
    }
}
相關文章
相關標籤/搜索