LeetCode Next Greater Element I & II && III

题还是要当成吃饭睡觉一样每天坚持做规律做和总结的呀。

496. Next Greater Element I

有两个集合num1和num2,其中num1是num2的子集。让你找出对于每个num1的元素,在num2中再其右边的比其大的第一个数。


由于集合最大不超过1000,因此O(n2)的遍历是一定能过的。但是,这种又和位置有关又和大小有关的要求,我想到了单调队列和单调栈。为什么呢?对于一个数,当从右向左开始遍历时,如果i < j 且 num[i] >= num[j],那么对于i左边的数来说num[j]一定是没有用了。基于此,我从右向左遍历时,维护一个单调递增堆栈。当遍历到i时,检查栈顶和num[i]的大小关系,如果栈顶top <= num[i],则出栈,直到top > num[i] 或 栈为空。然后将该数压入栈。这样,弹出去的那些数都是既在右边又小的数,在后面的遍历过程中就没有作用了。这样一次遍历就能完成啦。

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& findNums, vector<int>& nums) {
        stack<int> st;
        unordered_map<int, int> cnt;
        for (auto ite = nums.rbegin(); ite != nums.rend(); ite++) {
            while (!st.empty() && st.top() < *ite) st.pop();
            cnt[*ite] = st.empty() ? -1 : st.top();
            st.push(*ite);
        }
        for (int i = 0; i < findNums.size(); i++) {
            findNums[i] = cnt[findNums[i]];
        }
        return findNums;
    }
};

503. Next Greater Element II

针对问题1的改进,假设num2是可以头尾拼接一起无限接下去的,把num1换成num2,然后问的问题也是一样的。


由于num2是头尾拼接的,因此实际上我们只用考虑两个num2拼接在一起的数组即可。我也不需要新建一个数组把2个num2同时的copy过去,我只需要把指针i从2n-1开始循环,用num2[i%n]表示当前的数。那么剩下的方法都一样,从右端开始维护一个单调栈即可。

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int n = nums.size();
        if (n == 0) return vector<int>{}; 
        vector<int> ret(nums.size(), 0);
        stack<int> st;
        for (int i = 2*n-1; i >= 0; i--) {
            int num = nums[i%n];
            while (!st.empty() && num >= st.top()) st.pop();
            ret[i%n] = st.empty() ? -1 : st.top();
            st.push(num);
        }
        return ret;
    }
};

556 Next Greater Element III

给你一个数字表示的字符串,问你他的next Permutation是什么?


这题其实思路并不是很难,由于int最多才32位所以你暴力循环也绝对不会超时。但这题的重点在于细节的考虑与打磨,否则WA好多次。思路如下:

1.从右往左开始遍历,对于每一位i,找到其右边的大于i的数字的最小值,假设处于第j位。
2.交换i位与j位的值。
3.对于i位以后的数字,将他们从小到大排序。(Details)
class Solution {
public:
    int nextGreaterElement(int n) {
        vector<int> digits;
        //首先通过这种经典的方法得到数字n的每一位
        while (n) {
            digits.push_back(n%10);
            n /= 10;
        }
        reverse(digits.begin(), digits.end());
        int pos = -1;
        for (int i = digits.size()-1; i >= 0; i--) {
            int k = -1, Min = 10;
            for (int j = digits.size()-1; j > i; j--) {
                if (digits[j] > digits[i]) {
                    k = j;
                    Min = min(Min, digits[j]);
                }
            }
            if (k != -1) {
                pos = i;
                swap(digits[i], digits[k]);
                break;
            }
        }
        if (pos == -1) return -1;
        sort(digits.begin()+pos+1, digits.end());
        int res = 0;
        for (int i = 0; i < digits.size(); i++)
            res = 10*res + digits[i];

        return res;

    }
};

总结

  1. 单调队列与单调栈的应用。我对这个技巧也只是略懂皮毛,可以去各大oj上搜一些相关的题目,强化一下这个专题的应用。(嗯,又挖坑不知道填系列)

你可能感兴趣的:(LeetCode题解,算法与数据结构)