Leetcode 題解 - 排序

快速選擇

用於求解 Kth Element 問題,使用快速排序的 partition() 進行實現。

需要先打亂數組,否則最壞情況下時間複雜度爲 O(N2)。

堆排序

用於求解 TopK Elements 問題,通過維護一個大小爲 K 的堆,堆中的元素就是 TopK Elements。

堆排序也可以用於求解 Kth Element 問題,堆頂元素就是 Kth Element。

快速選擇也可以求解 TopK Elements 問題,因爲找到 Kth Element 之後,再遍歷一次數組,所有小於等於 Kth Element 的元素都是 TopK Elements。

可以看到,快速選擇和堆排序都可以求解 Kth Element 和 TopK Elements 問題。

Kth Element

215. Kth Largest Element in an Array (Medium)

題目描述:找到第 k 大的元素。

排序 :時間複雜度 O(NlogN),空間複雜度 O(1)

public int findKthLargest(int[] nums, int k) {
    Arrays.sort(nums);
    return nums[nums.length - k];
}

堆排序 :時間複雜度 O(NlogK),空間複雜度 O(K)。

public int findKthLargest(int[] nums, int k) {
    PriorityQueue<Integer> pq = new PriorityQueue<>(); // 小頂堆
    for (int val : nums) {
        pq.add(val);
        if (pq.size() > k)  // 維護堆的大小爲 K
            pq.poll();
    }
    return pq.peek();
}

快速選擇 :時間複雜度 O(N),空間複雜度 O(1)

public int findKthLargest(int[] nums, int k) {
    k = nums.length - k;
    int l = 0, h = nums.length - 1;
    while (l < h) {
        int j = partition(nums, l, h);
        if (j == k) {
            break;
        } else if (j < k) {
            l = j + 1;
        } else {
            h = j - 1;
        }
    }
    return nums[k];
}

private int partition(int[] a, int l, int h) {
    int i = l, j = h + 1;
    while (true) {
        while (a[++i] < a[l] && i < h) ;
        while (a[--j] > a[l] && j > l) ;
        if (i >= j) {
            break;
        }
        swap(a, i, j);
    }
    swap(a, l, j);
    return j;
}

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

桶排序

出現頻率最多的 k 個數

347. Top K Frequent Elements (Medium)

Given [1,1,1,2,2,3] and k = 2, return [1,2].

設置若干個桶,每個桶存儲出現頻率相同的數,並且桶的下標代表桶中數出現的頻率,即第 i 個桶中存儲的數出現的頻率爲 i。

把數都放到桶之後,從後向前遍歷桶,最先得到的 k 個數就是出現頻率最多的的 k 個數。

public List<Integer> topKFrequent(int[] nums, int k) {
    Map<Integer, Integer> frequencyForNum = new HashMap<>();
    for (int num : nums) {
        frequencyForNum.put(num, frequencyForNum.getOrDefault(num, 0) + 1);
    }
    List<Integer>[] buckets = new ArrayList[nums.length + 1];
    for (int key : frequencyForNum.keySet()) {
        int frequency = frequencyForNum.get(key);
        if (buckets[frequency] == null) {
            buckets[frequency] = new ArrayList<>();
        }
        buckets[frequency].add(key);
    }
    List<Integer> topK = new ArrayList<>();
    for (int i = buckets.length - 1; i >= 0 && topK.size() < k; i--) {
        if (buckets[i] == null) {
            continue;
        }
        if (buckets[i].size() <= (k - topK.size())) {
            topK.addAll(buckets[i]);
        } else {
            topK.addAll(buckets[i].subList(0, k - topK.size()));
        }
    }
    return topK;
}

按照字符出現次數對字符串排序

451. Sort Characters By Frequency (Medium)

Input:
"tree"

Output:
"eert"

Explanation:
'e' appears twice while 'r' and 't' both appear once.
So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
public String frequencySort(String s) {
    Map<Character, Integer> frequencyForNum = new HashMap<>();
    for (char c : s.toCharArray())
        frequencyForNum.put(c, frequencyForNum.getOrDefault(c, 0) + 1);

    List<Character>[] frequencyBucket = new ArrayList[s.length() + 1];
    for (char c : frequencyForNum.keySet()) {
        int f = frequencyForNum.get(c);
        if (frequencyBucket[f] == null) {
            frequencyBucket[f] = new ArrayList<>();
        }
        frequencyBucket[f].add(c);
    }
    StringBuilder str = new StringBuilder();
    for (int i = frequencyBucket.length - 1; i >= 0; i--) {
        if (frequencyBucket[i] == null) {
            continue;
        }
        for (char c : frequencyBucket[i]) {
            for (int j = 0; j < i; j++) {
                str.append(c);
            }
        }
    }
    return str.toString();
}

荷蘭國旗問題

荷蘭國旗包含三種顏色:紅、白、藍。

有三種顏色的球,算法的目標是將這三種球按顏色順序正確地排列。

它其實是三向切分快速排序的一種變種,在三向切分快速排序中,每次切分都將數組分成三個區間:小於切分元素、等於切分元素、大於切分元素,而該算法是將數組分成三個區間:等於紅色、等於白色、等於藍色。

按顏色進行排序

75. Sort Colors (Medium)

Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]

題目描述:只有 0/1/2 三種顏色。

public void sortColors(int[] nums) {
    int zero = -1, one = 0, two = nums.length;
    while (one < two) {
        if (nums[one] == 0) {
            swap(nums, ++zero, one++);
        } else if (nums[one] == 2) {
            swap(nums, --two, one);
        } else {
            ++one;
        }
    }
}

private void swap(int[] nums, int i, int j) {
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章