力扣406——根據身高重建隊列

這道題主要涉及的是找規律和快速排序,優化時需要考慮 Java 中數據結構的特性。

原題

假設有打亂順序的一羣人站成一個隊列。 每個人由一個整數對(h, k)表示,其中h是這個人的身高,k是排在這個人前面且身高大於或等於h的人數。 編寫一個算法來重建這個隊列。

注意:

總人數少於1100人。

示例

輸入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

輸出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

原題url:https://leetcode-cn.com/problems/queue-reconstruction-by-height/

解題

兩次快速排序

拿到這道題目,先想想規律。關注的重點應該在於這句話:k是排在這個人前面且身高大於或等於h的人數

所以一般肯定是 h 大的在前面,但也需要考慮 k,當 h 相同時,肯定是 k 越小,越在前面。

這樣也就涉及到了排序,排序中時間複雜度低的也就是快速排序歸併排序。本題並不需要考慮穩定性,因爲原始的數組並沒有規律,且並沒有涉及原始數組的順序,所以兩種排序用哪個都可以。但考慮到快速排序寫起來更簡單,因此就採用它。

我的思路是:

  • 先針對 h ,如果 h 越大,則越靠前(也就是降序),做一次快速排序。
  • 如果 h 相同時,再針對 k,如果 k 越小,則越靠前(也就是升序),再做一次快速排序。
  • 利用自定義的 TreeNode 結構,也就是單向鏈表,根據 k 進行插入。
  • 遍歷單向鏈表,輸出最終結果

接下來看看代碼:

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        if (people.length <= 1) {
            return people;
        }

        // 利用快排,針對people進行排序
        // h越大,越靠前,降序
        binarySort(people, 0, people.length - 1);
        // h相等時,k越小越靠前,升序
        sortByK(people);
        // 構造樹
        TreeNode root = new TreeNode();
        TreeNode temp = new TreeNode();
        temp.val = people[0];
        root.next = temp;
        for (int i = 1; i < people.length; i  ) {
            int[] person = people[i];
            // 根據k,往樹中放
            TreeNode preNode = root;
            for (int j = 0; j < person[1]; j  ) {
                preNode = preNode.next;
            }
            // 添加節點
            temp = new TreeNode();
            temp.val = person;
            temp.next = preNode.next;
            preNode.next = temp;
        }
        // 最終結果
        int[][] result = new int[people.length][2];
        int index = 0;
        root = root.next;
        // 遍歷並賦值
        while (root != null) {
            TreeNode node = root;
            result[index] = node.val;
            root = root.next;
            index  ;
        }
        return result;
    }

    public void sortByK(int[][] people) {
        int start = 0;
        int end = 1;
        int[] prePeople = people[0];
        // 遍歷,找出相等的結果
        while (end < people.length) {
            // 如果兩者不等
            if (prePeople[0] != people[end][0]) {
                if (end - start >= 2) {
                    binarySortByK(people, start, end - 1);
                }
                prePeople = people[end];
                start = end;
            }
            // 如果兩者相等,則什麼都不需要改變
            end  ;
        }
        if (end - start >= 2) {
            binarySortByK(people, start, end - 1);
        }
    }

    public void binarySortByK(
            int[][] people,
            int left,
            int right) {
        // 終止條件
        if (left >= right) {
            return;
        }
        // 標準值(待比較)
        int[] standard = new int[2];
        standard[0] = people[left][0];
        standard[1] = people[left][1];
        // 提前記錄
        int low = left;
        int high = right;

        while (left < right) {
            // 先從right找起,因爲left的值已經被重新存儲,可以被替換
            while (left < right && people[right][1] >= standard[1]) {
                right--;
            }
            people[left] = people[right];
            // 再替換right
            while (left < right && people[left][1] < standard[1]) {
                left  ;
            }
            people[right] = people[left];
        }
        // 最終left和right必然相等
        people[right] = standard;
        // 繼續
        binarySortByK(people, low, right - 1);
        binarySortByK(people, right   1, high);
    }

    public void binarySort(
            int[][] people,
            int left,
            int right) {
        // 終止條件
        if (left >= right) {
            return;
        }
        // 標準值(待比較)
        int[] standard = new int[2];
        standard[0] = people[left][0];
        standard[1] = people[left][1];
        // 提前記錄
        int low = left;
        int high = right;

        while (left < right) {
            // 先從right找起,因爲left的值已經被重新存儲,可以被替換
            while (left < right && people[right][0] < standard[0]) {
                right--;
            }
            people[left] = people[right];
            // 再替換right
            while (left < right && people[left][0] >= standard[0]) {
                left  ;
            }
            people[right] = people[left];
        }
        // 最終left和right必然相等
        people[right] = standard;
        // 繼續
        binarySort(people, low, right - 1);
        binarySort(people, right   1, high);
    }
}

class TreeNode {
    int[] val;
    TreeNode next;
}

提交OK,但執行時間較長,我們再優化優化。

優化

首先,針對快速排序,是否可以只用一次?答案是肯定的,我們只需要將比較條件特殊處理即可,也就是將上面兩次的排序判斷條件合併。

其次,針對最終結果的輸出,我之前考慮用單向鏈表,是因爲相比於數組每次插入時需要複製,鏈表的插入比較簡單,只需要將地址換掉即可。但鏈表在找元素過程中耗時較長,數組可以直接利用下標計算出目標位置,且 Java 中的 ArrayList 的 add(int index, E element),其複製方法是 native 類型,因此效率較高。所以我將最終的結果放入 ArrayList 進行處理。

接下來看看代碼:

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        if (people.length <= 1) {
            return people;
        }

        // 利用快排,針對people進行排序
        binarySort(people, 0, people.length - 1);

        List<int[]> list = new ArrayList<>(people.length);
        // 根據k向ArrayList中添加元素
        for (int[] person : people) {
            int k = person[1];
            list.add(k, person);
        }
        // 轉化爲數組
        return list.toArray(new int[people.length][2]);
    }

    public void binarySort(
            int[][] people,
            int left,
            int right) {
        // 終止條件
        if (left >= right) {
            return;
        }
        // 標準值(待比較)
        int[] standard = new int[2];
        standard[0] = people[left][0];
        standard[1] = people[left][1];
        // 提前記錄
        int low = left;
        int high = right;

        while (left < right) {
            // 先從right找起,因爲left的值已經被重新存儲,可以被替換
            while (left < right && !compare(people[right], standard)) {
                right--;
            }
            people[left] = people[right];
            // 再替換right
            while (left < right && compare(people[left], standard)) {
                left  ;
            }
            people[right] = people[left];
        }
        // 最終left和right必然相等
        people[right] = standard;
        // 繼續
        binarySort(people, low, right - 1);
        binarySort(people, right   1, high);
    }

    public boolean compare(int[] person1, int[] person2) {
        // h越大,越靠前,降序
        int height1 = person1[0];
        int height2 = person2[0];
        if (height1 > height2) {
            return true;
        }

        if (height1 < height2) {
            return false;
        }

        // 當h相等時,k越小越靠前,升序
        int k1 = person1[1];
        int k2 = person2[1];
        return k1 < k2;
    }
}

提交OK,這樣速度提升了至少一倍。

總結

以上就是這道題目我的解答過程了,不知道大家是否理解了。這道題主要涉及的是找規律和快速排序,優化時需要考慮 Java 中數據結構的特性。

有興趣的話可以訪問我的博客或者關注我的公衆號、頭條號,說不定會有意外的驚喜。

https://death00.github.io/

公衆號:健程之道

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