算法学习day58

算法学习day58

  • 1.力扣 739. 每日温度
    • 1.1 题目描述
    • 1.2 分析
    • 1.3代码
  • 2.力扣 496.下一个更大元素 I
    • 2.1 题目描述
    • 2.2 分析
    • 2.3 代码
  • 3.参考资料

1.力扣 739. 每日温度

1.1 题目描述

题目描述:

请根据每日气温列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后后不升高,请在该位置用0来代替。

例:给定一个列表temperatures=[73 , 74 , 75 , 71, 69 , 72 , 76 , 73] 输出:[1, 1 , 4 , 2 , 1, 1, 0, 0]。

分析:73时想要观测更高气温就是下一位,所以为1;74时想要观测更高气温就是下一位,所以为1;75时想要观测更高气温是76有4位,所以为4…以此例推。得:[1, 1 , 4 , 2 , 1, 1, 0, 0]。

1.2 分析

使用单调栈:

1.什么时候使用单调栈?

答:通常是一维数组,要寻找任意一个元素的右边或者左边第一个比自己的大或者小的元素的位置,此时可以考虑使用单调栈。时间复杂度为O(n)。

2.单调栈原理是什么?

答:使用一个栈来记录遍历过的元素,使用一个单调栈来记录遍历过得元素。这样在遍历元素的时候可以知道是不是之前遍历过一个更小的。

3.单调栈里面存的元素是什么?

答:单调栈里只需要存放元素的下标i即可,如果需要使用对应的元素,通过下标i,T[i]获取即可。

4.单调栈里元素是递增?还是递减?

答:使用递增循序。

使用单调栈递增,主要有三个判断条件

注意:栈内元素为元素的下标!

(1)当前遍历的元素T[i]大于栈顶元素T[st.top()]的时候

要满足单调栈递增。**说白了就是底部大,顶部小。**将小的(栈里面的元素弹出),大的(当前遍历元素)加入。在使用result[st.top()] = i - st.top(),记录。

(2)当前遍历的元素T[i]小于于栈顶元素T[st.top()]的时候

要满足单调栈递增,从栈头到栈底要从小到大。**说白了就是底部大,顶部小。**将小的(当前遍历元素)加入,大的(栈里元素)不变。在使用result[st.top()] = i - st.top(),记录。

(3)当前遍历的元素T[i]等于于栈顶元素T[st.top()]的时候

根据题意是要求大于本元素的位置,所以入栈。但是不用result记录距离。

总结:使用了单调栈,当遍历到一个新元素时,如果它比栈顶元素大,就需要把栈中所有小于它的元素都出栈,因为它们的下一个更大的元素就是当前的新元素。然后将新元素的下标入栈,表示该元素还没有找到下一个更大的元素。如果新元素比栈顶元素小,就将它的下标入栈,因为它的下一个更大的元素还没有找到。

1.3代码

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        // 递增栈,存储的是下标
        stack<int> st;
        // 存储结果的数组,初始值都为0
        vector<int> result(T.size(), 0);
        // 将第一个元素下标入栈
        st.push(0);
        // 遍历数组,从第二个元素开始
        for (int i = 1; i < T.size(); i++) {
        	// (2)当前遍历的元素T[i]**小于**于栈顶元素T[st.top()]的时候
            // 如果当前元素温度小于等于栈顶元素温度,入栈
            // 将小的(当前遍历元素)加入,大的(栈里元素)不变。小的加在大的后面
            if (T[i] <= T[st.top()]) {                       
                st.push(i);
            } 
            // (3)当前遍历的元素T[i]**等于**于栈顶元素T[st.top()]的时候
            // 如果当前元素温度等于栈顶元素温度,入栈
            else if (T[i] == T[st.top()]) {               
                st.push(i);
            } 
            // (1)当前遍历的元素T[i]**大于**栈顶元素T[st.top()]的时候
            // 如果当前元素温度大于栈顶元素温度,弹出栈顶元素
            // 并计算栈顶元素对应的结果值,一直循环直到栈顶元素大于等于当前元素温度
            else {
                while (!st.empty() && T[i] > T[st.top()]) { 
                    result[st.top()] = i - st.top();
                    st.pop();
                }
                // 将当前元素下标入栈
                st.push(i);
            }
        }
        return result;
    }
};

2.力扣 496.下一个更大元素 I

2.1 题目描述

题目描述:

给两个没有重复元素的数组nums1和nums2,其中nums1是nums2的子集。

请你找出nums1中每个元素在nums2中的下一个比其大的值。

nums1中数字x的下一个更大的元素是在x在nums2中对应位置的右边的第一个比x大的元素。如果不存在,对应位置输出-1.

例1:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].

输出:[-1,3,-1]

解释:

对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

例2:

输入:nums1 = [2,4], nums2 = [1,2,3,4].

输出:[3,-1]

解释:

对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出-1 。

2.2 分析

由题意可知最后是要求nums1的每一个元素在nums2中下一个比当前元素大的元素,需要定义一个和nums1一定大小的数组result来存放结果。

result数组初始化为多少呢?

由题意,不存在对应位值就输出-1,所以result数组如果某位置没有被赋值,就是-1,所以初始化为-1.

由题意,没有重复元素,可以使用map来做映射。根据数值快速找到下标,可以判断nums2[i]是否在nums1中出现过。

注:在cpp中当使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率最优。

预处理代码如下:

unordered_map<int, int> umap; // key下标元素,valuie下标
for(int i = 0 ; i < nums1.size(); i++){
    umap[nums1[i]] = i;
}

本题使用递增单调栈(底大头小)

1.当前遍历的元素T[i]小于栈顶元素T[st.top()]

此时满足底大头小,递增。直接入栈。

2.当前遍历的元素T[i]等于栈顶元素T[st.top()]

如果相等,直接入栈,要求右边第一个比自己大的元素,而不是大于等于。

3.当遍历元素T[i]大于栈顶元素T[st.top()]

此时如果入栈就不满足递增栈了,这也找到了右边第一个比自己大。

判断栈顶元素是否在nums1里出现过,栈里元素都是nums2的元素,如果出现过,开始记录结果。

while (!st.empty() && nums2[i] > nums2[st.top()]) {
    if (umap.count(nums2[st.top()]) > 0) { // 看map里是否存在这个元素
        int index = umap[nums2[st.top()]]; // 根据map找到nums2[st.top()] 在 nums1中的下标
        result[index] = nums2[i];
    }
    st.pop();
}
st.push(i);

总结:本题是要找到nums1中的每个元素在nums2中下一个比它大的元素,因此我们可以先用一个哈希表来保存nums1中每个元素的下标,然后遍历nums2,对于每个元素,如果它在哈希表中出现过,那么就更新结果数组中对应位置的值为当前元素(nums2)右边第一个比它大的元素。

为了方便起见,我们可以使用单调递增栈来找到当前元素的右边第一个比它大的元素。我们可以维护一个单调递增栈,遍历nums2中的每个元素,如果当前元素比栈顶元素小,那么就将当前元素入栈;如果当前元素比栈顶元素大,那么就弹出栈顶元素,并更新结果数组中对应位置的值为当前元素(nums2);最后将当前元素入栈即可。

时间复杂度:O(N),其中 N 是 nums2 的长度。

2.3 代码

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        stack<int> st;                                                                      // 定义一个栈,存储下标
        vector<int> result(nums1.size(), -1);                        // 初始化结果数组,全部设为-1
        if (nums1.size() == 0) return result;                          // 处理特殊情况,nums1为空

        unordered_map<int, int> umap;                              // 定义一个哈希表,存储nums1中元素和它在nums1中的下标
        for (int i = 0; i < nums1.size(); i++) {
            umap[nums1[i]] = i;
        }
        st.push(0);                                                                          // 将nums2的第一个元素下标0放入栈中
        for (int i = 1; i < nums2.size(); i++) {
            if (nums2[i] < nums2[st.top()]) {                           // 如果当前元素小于栈顶元素,入栈
                st.push(i);
            } else if (nums2[i] == nums2[st.top()]) {            // 如果当前元素等于栈顶元素,入栈
                st.push(i);
            } else {                                                                            // 如果当前元素大于栈顶元素,处理
                while (!st.empty() && nums2[i] > nums2[st.top()]) {
                    if (umap.count(nums2[st.top()]) > 0) {    // 如果栈顶元素在nums1中出现过
                        int index = umap[nums2[st.top()]];     // 根据map找到nums2[st.top()] 在 nums1中的下标
                        result[index] = nums2[i];                         // 更新结果数组
                    }
                    st.pop();                                                              // 弹出栈顶元素
                }
                st.push(i);                                  // 将当前元素下标入栈
            }
        }
        return result;                                       // 返回结果数组
    }
};

3.参考资料

[代码随想录]

你可能感兴趣的:(算法,数据结构,c++,动态规划)