代码随想录算法训练营第五十七天 | LeetCode 739. 每日温度、496. 下一个更大元素 I

简介: 代码随想录算法训练营第五十七天 | LeetCode 739. 每日温度、496. 下一个更大元素 I

代码随想录算法训练营第五十七天 | LeetCode 739. 每日温度、496. 下一个更大元素 I

1. LeetCode 739. 每日温度

1.1 思路

  1. 本题按照正常的暴力解法大概率过不了,是一个 n^2 的时间复杂度,本题用到一个单调栈的解法。
  2. 单调栈适用场景:求当前元素左边或者右边第一个比当前元素大或者小的元素。而本题求的就是当前位置右边第一个比其大的元素,两个位置下标一相减就是了
  3. 什么是单调栈:就是一个栈,但栈里的元素要保证是递增或者递减的。一般编程语言没有实现这种栈的,只能自己模拟实现。本题中我们放入栈的不应该是元素本身,因为本题元素有重复,无法锁定唯一下标,而且我们要找的是下标相减的值,因此我们应该直接存下标,我们知道下标了直接用数组映射就是对应的元素的大小。
  4. 从栈顶到栈底的顺序:递增还是递减?如果是递增,求的就是左边或者右边第一个比当前位置大的元素,如果加入的元素大于栈顶的元素,才找到栈顶的元素在这个数组里所对应的右边第一个元素的位置;如果是递减,求的就是左边或者右边第一个比当前位置小的元素。注意这里的递增递减是从栈顶到栈底的方向
  5. 单调栈的作用:存放遍历过的元素。时间复杂度是 O(n)的,用一个 for 循环遍历的,我们是不知道当前元素和之前遍历过的元素之间是大是小,因此用一个数据结构记录遍历过的元素,这个单调栈就是记录遍历过的元素,然后和当前元素比较。那如何存放呢?因为当前遍历的元素就是和栈顶的元素作比较,要么大,要么等于,要么小于,然后做对应的操作。当前元素是 T[i],栈顶元素是 T[st.peek()],因为单调栈存的是下标
  6. 过程:定义个 result 数组存结果。先存第一个元素的下标,如果这个下标对应的数值比当前遍历的元素大,就下标之差,然后存入 result 中,含义就是第一个比 0 下标大的位置。然后如果栈里元素是计算过的就弹出,然后接着把 1 下标的元素加入栈中。后续如果出现当前遍历的元素小于等于栈顶元素的话就把这元素入栈,并且结果先不用记录,一直这样,这个栈从栈顶到栈底就是一个递增的关系。直到出现当前遍历的元素比栈顶大了,才作差然后存入 result 中,注意,这里比较完栈顶出栈,但是当前遍历的元素要接着和栈顶比较,然后作差存入 result 中,直到当前遍历的元素小于等于栈顶了,然后才把当前遍历元素存入栈顶。一直这样进行下去到最后可能会有元素在栈里没出来,说明右边没有比它大的元素了,那就默认为 0 即可
  7. 代码实现:Stack stack;int[] result=new int[T.length];stack.push(0)第一个元素存的是下标 0;for(int i=1;i<T.length;i++)从 1 开始是因为 0 下标已经存入栈了。如果当前元素小于等于栈顶元素就直接入栈,记住存的是下标。如果大于,while(!stack.empty()&&T[i]>T[stack.peek()])栈不能为空,用 while 是因为当前元素一直比栈顶大就要一直比较下去,result[stack.peek()]=i-stack.peek(),然后出栈 stack.pop()。最终 return result 即可。

1.2 代码

class Solution {
  // 版本 1
    public int[] dailyTemperatures(int[] temperatures) {

        int lens=temperatures.length;
        int []res=new int[lens];

        /**
        如果当前遍历的元素 大于栈顶元素,表示 栈顶元素的 右边的最大的元素就是 当前遍历的元素,
          所以弹出 栈顶元素,并记录
          如果栈不空的话,还要考虑新的栈顶与当前元素的大小关系
        否则的话,可以直接入栈。
        注意,单调栈里 加入的元素是 下标。
        */
        Deque<Integer> stack=new LinkedList<>();
        stack.push(0);
        for(int i=1;i<lens;i++){

            if(temperatures[i]<=temperatures[stack.peek()]){
                stack.push(i);
            }else{
                while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
                    res[stack.peek()]=i-stack.peek();
                    stack.pop();
                }
                stack.push(i);
            }
        }

        return  res;
    }
}

2. LeetCode 496. 下一个更大元素 I

2.1 思路

  1. 本题给了两个数组,nums1 是 nums2 的子集,各自没有重复的元素。在 nums2 中找出 nums1 中元素 x 第一个比其大的元素,存入新数组中,如果没有就是存入-1,这个新数组的大小和 nums1 一样。本质就是求在 nums2 中比当前元素 x 的右边第一个比其大的数,注意是从同一个元素的右边出发,而不是从下标 0 出发。在739. 每日温度就是一道直接的单调栈的题目,本题绕了一下。
  2. 首先定义个 result 数组,大小和 nums1 是一样的。在739. 每日温度中默认值都是 0,但在本题中找不到右边第一个比 x 大元素的话应该存的是-1。
  3. 单调栈应该遍历哪个数组呢?因为要求的是 nums2 里某个元素右边第一个比其大的元素是多少,因此遍历的是 nums2。单调栈里存入的是遍历过的元素
  4. 两个数组的联系是什么?比如在 nums2 中找到了元素 2 右边第一个比 2 大的元素是 3,那么就根据 2 找到 nums1 中有没有 2,如果有要找到 2 的下标,然后在 2 的下标的位置存入 3,因此要建立一个哈希映射。需要在 nums2 中的元素找到在 nums1 中是否出现过,如果出现过就找到这个元素的下标,因此用一个 map<k,v>。
  5. 单调栈顶到栈底的顺序是递增还是递减?因为求的是右边第一个比当前元素大的,因此用递增,并且存的是下标。然后还是当前元素和栈顶元素的比较,大于等于小于,栈顶元素是 nums2[stack.top()],当前元素是 nums2[i]。
  6. 代码实现:定义个栈、数组,数组和 nums1 大小一样,并且默认值为-1。如果 nums1 为 null 就直接 return null。要做一个映射的逻辑,要先把 nums1 元素映射到 nums2 的下标,因为遍历 nums2 的时候要找到对应 nums1 的下标。HashMap<Interger,Integer>,遍历 nums1,正常的 for 循环,map[nums[i]]=i,k 是值,v 是下标。然后 stack.push(0),先放入下标 0。然后是 for(int i=1;i<nums2.length;i++)从 1 开始是因为 0 下标已经存入栈了,然后就是单调栈的过程,如果当前元素如果小于等于栈顶元素,那就加入栈,如果大于,while(!stack.empty()&&nums2[i]>nums2[stack.peek()])用 while 是因为如果一直大于栈顶就要一直比较下去,然后 if(map.containsKey(nums2[stack.peek()))用哈希表看一下栈顶元素是否在 nums1 存在,int index=map.get(nums2[stack.peek()])找到对应元素在 nums1 的下标,然后 result[index]=nums2[i],这里不管哈希表是否存在,最后栈顶都要出栈。while 循环结束后把比较的元素放入栈中。最后 return result

2.2 代码

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        Stack<Integer> temp = new Stack<>();
        int[] res = new int[nums1.length];
        Arrays.fill(res,-1);
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        for (int i = 0 ; i< nums1.length ; i++){
            hashMap.put(nums1[i],i);
        }
        temp.add(0);
        for (int i = 1; i < nums2.length; i++) {
            if (nums2[i] <= nums2[temp.peek()]) {
                temp.add(i);
            } else {
                while (!temp.isEmpty() && nums2[temp.peek()] < nums2[i]) {
                    if (hashMap.containsKey(nums2[temp.peek()])){
                        Integer index = hashMap.get(nums2[temp.peek()]);
                        res[index] = nums2[i];
                    }
                    temp.pop();
                }
                temp.add(i);
            }
        }

        return res;
    }
}


相关文章
|
1月前
|
算法
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
【自然语言处理】TF-IDF算法在人工智能方面的应用,附带代码
TF-IDF算法在人工智能领域,特别是自然语言处理(NLP)和信息检索中,被广泛用于特征提取和文本表示。以下是一个使用Python的scikit-learn库实现TF-IDF算法的简单示例,并展示如何将其应用于文本数据。
208 65
|
20天前
|
机器学习/深度学习 存储 算法
经典算法代码
这段代码展示了多个经典算法,包括:穷举法解决“百钱买百鸡”问题;递推法计算“猴子吃桃”问题;迭代法求解斐波那契数列及折纸高度超越珠峰的问题。同时,还提供了希尔排序算法实现及披萨票务订购系统和汉诺塔问题的链表存储解决方案。每部分通过具体案例解释了算法的应用场景与实现方法。
21 3
|
1月前
|
机器学习/深度学习 人工智能 算法
【人工智能】传统语音识别算法概述,应用场景,项目实践及案例分析,附带代码示例
传统语音识别算法是将语音信号转化为文本形式的技术,它主要基于模式识别理论和数学统计学方法。以下是传统语音识别算法的基本概述
51 2
|
1月前
|
搜索推荐 算法 Java
|
1月前
|
人工智能 算法 Java
LeetCode经典算法题:井字游戏+优势洗牌+Dota2参议院java解法
LeetCode经典算法题:井字游戏+优势洗牌+Dota2参议院java解法
39 1
|
1月前
|
人工智能 算法 数据可视化
DBSCAN密度聚类算法(理论+图解+python代码)
DBSCAN密度聚类算法(理论+图解+python代码)
|
1月前
|
数据采集 搜索推荐 算法
【高手进阶】Java排序算法:从零到精通——揭秘冒泡、快速、归并排序的原理与实战应用,让你的代码效率飙升!
【8月更文挑战第21天】Java排序算法是编程基础的重要部分,在算法设计与分析及实际开发中不可或缺。本文介绍内部排序算法,包括简单的冒泡排序及其逐步优化至高效的快速排序和稳定的归并排序,并提供了每种算法的Java实现示例。此外,还探讨了排序算法在电子商务、搜索引擎和数据分析等领域的广泛应用,帮助读者更好地理解和应用这些算法。
24 0
|
1月前
|
搜索推荐 算法 Java
插入排序算法(Java代码实现)
这篇文章通过Java代码示例详细解释了插入排序算法的实现过程,包括算法的基本思想、核心代码、辅助函数以及测试结果,展示了如何通过插入排序对数组进行升序排列。
|
1月前
|
机器学习/深度学习 算法 Python
python与朴素贝叶斯算法(附示例和代码)
朴素贝叶斯算法以其高效性和优良的分类性能,成为文本处理领域一项受欢迎的方法。提供的代码示例证明了其在Python语言中的易用性和实用性。尽管算法假设了特征之间的独立性,但在实际应用中,它仍然能够提供强大的分类能力。通过调整参数和优化模型,你可以进一步提升朴素贝叶斯分类器的性能。
45 0