2018年力扣高頻算法面試題8排序與檢索

1、尋找峯值

峯值元素是指其值大於左右相鄰值的元素。
給定一個輸入數組 nums,其中 nums[i] ≠ nums[i+1],找到峯值元素並返回其索引。
數組可能包含多個峯值,在這種情況下,返回任何一個峯值所在位置即可。
你可以假設 nums[-1] = nums[n] = -∞。
要求:你的解法應該是O(logN)時間複雜度的。
參考思路:
方法一:線性掃描:時間複雜度O(n),空間複雜度O(1)。
方法二:二分查找:O(logN)時間複雜度。

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int right=nums.size()-1, left=0,m;
        while(left<right)
        {
            m=(right+left)/2;
            if(nums[m+1]<nums[m])right=m;
            else left=m+1;
        }
        return left;
    }
};

2、尋找重複數

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。
要求:不能更改原數組(假設數組是隻讀的)。只能使用額外的 O(1) 的空間。時間複雜度小於 O(n2) 。數組中只有一個重複的數字,但它可能不止重複出現一次。
解答:
鴿子洞原理/抽屜原理:有n只鴿子和m個鴿洞,所有鴿子都住在鴿洞裏,如果n>m,那麼至少有2只鴿子必須住在同一鴿洞裏。
https://leetcode-cn.com/problems/find-the-duplicate-number/solution/287xun-zhao-zhong-fu-shu-by-kirsche/

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int slow=0,fast=0;
        slow=nums[slow];
        fast=nums[nums[fast]];
        while(slow!=fast)
        {
            slow=nums[slow];
            fast=nums[nums[fast]];
        }
        int pre1=0,pre2=slow;
        while(pre1!=pre2)
        {
            pre1=nums[pre1];
            pre2=nums[pre2];
        }
        return pre1;
    }
};

3、計算右側小於當前元素的個數

給定一個整數數組nums,按要求返回一個新數組counts。數組counts有該性質:counts[i]的值是nums[i]右側小於nums[i]的元素的數量。
解答:歸併排序 + 索引數組
https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/solution/gui-bing-pai-xu-suo-yin-shu-zu-python-dai-ma-java-/

class Solution {
    vector<int>index;
    vector<int>helper;
    vector<int>count;
public:
    void merge(const vector<int> &nums,int lo,int mi,int hi)
    {
        if(lo==mi)return ;
        if(mi==hi)return ;
        // 先merge兩個小的
        merge(nums, lo, (lo + mi) >> 1, mi);
        merge(nums, mi, (mi + hi) >> 1, hi);
        
        // 把需要用的index拷貝到helper裏面,一會兒直接往index裏面寫就好了
        for (int k = lo; k < hi; k++) helper[k] = index[k];
        
        // 合併兩個有序數組,並計算向右移動的位數(即右邊有幾個比當前元素小)
        int p1 = lo, p2 = mi, p = lo;
        while (p1 != mi or p2 != hi) {
            if (p1 == mi) {
                index[p++] = helper[p2++];
                
            } else if (p2 == hi) {
                index[p++] = helper[p1++];
                count[index[p - 1]] += (p2 - mi);  // p2 - mi即右邊出了多少個
                
            } else if (nums[helper[p2]] < nums[helper[p1]]) {
                index[p++] = helper[p2++];
                
            } else {
                index[p++] = helper[p1++];
                count[index[p - 1]] += (p2 - mi);  // p2 - mi即右邊出了多少個
            }
        }
    }
    
    vector<int> countSmaller(vector<int>& nums) {
        int len=nums.size();
        if(len==0)return {};
        if(len==1)return {0};
        
        index.resize(len);
        helper.resize(len);
        count.resize(len);
        
        for(int i=0;i<len;i++)
        {
            index[i]=i;
            count[i]=0;
        }
        
        //merge
        merge(nums,0,len/2,len);
        
        return count;
    }
};

4、擺動排序 II

給定一個無序的數組 nums,將它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]… 的順序。
要求:用 O(n) 時間複雜度和 / 或原地 O(1) 額外空間來實現。
解答:快速選擇 + 3-way-partition + 虛地址
https://leetcode-cn.com/problems/wiggle-sort-ii/solution/yi-bu-yi-bu-jiang-shi-jian-fu-za-du-cong-onlognjia/

class Solution {
public:
    void wiggleSort(vector<int>& nums) {
        int n = nums.size();

        // Find a median.
        auto midptr = nums.begin() + n / 2;
        nth_element(nums.begin(), midptr, nums.end());
        int mid = *midptr;

        // Index-rewiring.
        #define A(i) nums[(1+2*(i)) % (n|1)]

        // 3-way-partition-to-wiggly in O(n) time with O(1) space.
        int i = 0, j = 0, k = n - 1;
        while (j <= k) {
            if (A(j) > mid)
                swap(A(i++), A(j++));
            else if (A(j) < mid)
                swap(A(j), A(k--));
            else
                j++;
        }
    }
};

5、最大數

給定一組非負整數,重新排列它們的順序使之組成一個最大的整數。輸出結果可能非常大,所以你需要返回一個字符串而不是整數。
解答:下面是在題解裏找到的兩個很厲害的答案。
方法一:一套STL走天下,tql

class Solution {
public:
    string largestNumber(vector<int>& nums) 
    {
        if (all_of(nums.begin(), nums.end(), [](int x) { return x == 0; })) {
            return string("0");
        }
        vector<string> strNums(nums.size());
        std::transform(nums.begin(), nums.end(), strNums.begin(), [](int x) {
            return std::to_string(x);
        });

        std::sort(strNums.begin(), strNums.end(), [](const string& x, const string& y) {
            /* x爲後面元素,y爲前面元素,return true則將x移動到前面 */
            return x + y > y + x;
        });

        return std::accumulate(strNums.begin(), strNums.end(), string());
    }
};

方法二:實現一個自定義排序。
將輸入的數組按照某個順序排序,然後按順序組合就是最大數。
這個順序的目的是實現組合後較大的數字在前面。
所以排序的比較算法應該是對組合後的數字進行比較。
通過計算進行比較的兩個數字的位數,交叉相乘再相加(a * b的位數 + b),實現將兩個數字組合到一起。
鏈接:https://leetcode-cn.com/problems/largest-number/solution/179-by-ikaruga/

class Solution {
public:
    string largestNumber(vector<int>& nums) 
    {
        auto f_sort = [](const int &a, const int &b)
        {
            long long n_a = 10;
            while (a / n_a) n_a *= 10;
            long long n_b = 10;
            while (b / n_b) n_b *= 10;
            long long r_a = (long long)a * n_b + (long long)b;
            long long r_b = (long long)b * n_a + (long long)a;
            return r_a < r_b;
        };

        sort(nums.rbegin(), nums.rend(), f_sort);

        string ans;
        for (auto n : nums)
        {
            ans = (ans == "0") ? to_string(n) : ans + to_string(n);
        }
        return ans;
    }

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