【LeetCode 热题 HOT 100】题解笔记 —— Day02

news2024/11/25 20:52:48

❤ 作者主页:欢迎来到我的技术博客😎
❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得关注点赞收藏评论⭐️⭐️⭐️
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉

文章目录

  • 一、有效的括号
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 二、合并两个有序列表
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 三、括号生成
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 四、合并K个升序链表
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 五、下一个排序
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 六、最长有效括号
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 七、搜索旋转排序数组
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 八、在排序数组中查找元素的第一个和最后一个位置
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 九、组合总和
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现
  • 十、接雨水
    • 1. 题目描述
    • 2. 思路分析
    • 3. 代码实现

一、有效的括号

1. 题目描述

在这里插入图片描述


2. 思路分析

从前往后枚举每个字符

  1. 当遇到左括号,则将元素压进栈中;
  2. 当遇到右括号
  • 如果栈为空,return false
  • 如果栈顶元素是对应的左括号,说明这是匹配的字符,将栈顶元素pop 出即可。
    否则,匹配不成功,return false.
  1. 最后,若栈是空栈,表示所有的字符都已经匹配好了,若不是空栈,表示还存在未能匹配好的字符。

注意: 由于'{'' }'以及'('')'他们的字符值只相差1, 而'[''']的字符值只相差2,所以可以通过这个来判断字符是否匹配。


3. 代码实现

class Solution {
public:
    bool isValid(string s) {
        stack<char> stk;

        for (auto c : s) {
            if (c == '(' || c == '{' || c == '[') stk.push(c);
            else {
                if (stk.size() && abs(stk.top() - c) <= 2) stk.pop();
                else return false;
            }
        }
        return stk.empty();
    }
};

二、合并两个有序列表

1. 题目描述

在这里插入图片描述


2. 思路分析

(线性合并) O(n)

  1. 新建头部的保护结点dummy,设置cur 指针指向dummy
  2. 若当前 l 1 l_1 l1指针指向的结点的值val l 2 l_2 l2指针指向的结点的值val小 ,则令curnext指针指向 l 1 l_1 l1,且 l 1 l_1 l1后移;否则指向 l 2 l_2 l2,且 l 2 l_2 l2后移。
  3. 然后cur指针按照上一部设置好的位置后移。
  4. 循环以上步骤直到 l 1 l_1 l1 l 2 l_2 l2为空。
  5. 将剩余的 l 1 l_1 l1 l 2 l_2 l2接到cur指针后边。

3. 代码实现

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        auto dummy = new ListNode(-1), cur = dummy;

        while (l1 && l2) {
            if (l1->val < l2->val) {
                cur = cur->next = l1;
                l1 = l1->next;
            } else {
                cur = cur->next = l2;
                l2 = l2->next;
            }
        }
        if (l1) cur->next = l1;
        if (l2) cur->next = l2;
        return dummy->next;
    }
};

三、括号生成

1. 题目描述

在这里插入图片描述


2. 思路分析

(直接生成合法发括号序列)

  1. 使用dfs
  2. 每次可以放置左括号的条件是当前左括号的数目不超过 n n n
  3. 每次可以放置右括号的条件是当前右括号的数目不超过 n n n 并且不超过左括号的数目。

3. 代码实现

class Solution {
public:
    vector<string> ans;

    vector<string> generateParenthesis(int n) {
        dfs(n, 0, 0, "");
        return ans;
    }

    void dfs(int n, int lc, int rc, string path) {
        if (lc == n && rc == n) ans.push_back(path);
        else {
            if (lc < n) dfs(n, lc + 1, rc, path + '(');
            if (rc < n && lc > rc) dfs(n, lc, rc + 1, path + ')');
        }
    }
};

四、合并K个升序链表

1. 题目描述

在这里插入图片描述


2. 思路分析

(优先队列)

  1. 一开始先用小根堆存储 k k k 个排序链表的头指针,每次操作后用小根堆维护 k k k 个链表当前最小的指针,并以指针对应的值进行排序。
  2. 操作过程中,当小根堆不为空时,堆顶元素即当前 k k k 个排序链表当前最小的元素的指针 t t t,将该值加入到 d u m m y dummy dummy 链表的后面,并把 t t t 指针往后移动一位,使得 t t t 指针指向的值变大,再加入到小根堆中。

3. 代码实现

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:

    struct Cmp {
        bool operator() (ListNode* a, ListNode* b) {
            return a->val > b->val;
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, Cmp> heap;

        auto dummy = new ListNode(-1), tail = dummy;
        for (auto l : lists) if (l) heap.push(l);

        while (heap.size()) {
            auto t = heap.top();
            heap.pop();

            tail =  tail->next = t;
            if (t->next) heap.push(t->next);
        }
        return dummy->next;
    }
};

五、下一个排序

1. 题目描述

在这里插入图片描述


2. 思路分析

(找规律) O ( n ) O(n) O(n)

找下一个序列就是从后往前找第一个出现降序的地方,把这个地方的前一个数字与后边某个比它大的数字交换,再把该位置之后整理为升序。

  1. 从数组末尾往前找,找到第一个位置 k k k, 使得 nums[k] > nums[k - 1]
  2. 如果不存在这样的 j j j,说明数组是递减的,则直接将数组进行翻转即可;
  3. 如果存在这样的 j j j,则从末尾找到第一个位置 t t t,使得 nums[t] > nums[k];
  4. 交换 nums[t - 1]nums[k - 1],然后将数组从 k + 1 k + 1 k+1 到末尾部分继续逆转。

3. 代码实现

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int k = nums.size() - 1;
        while (k > 0 && nums[k - 1] >= nums[k]) k --;
        if (k <= 0) {
            reverse(nums.begin(), nums.end());
        } else {
            int t = k;
            while (t < nums.size() && nums[t] > nums[k - 1]) t ++;
            swap(nums[t - 1], nums[k - 1]);
            reverse(nums.begin() + k, nums.end());
        }
    }
};

六、最长有效括号

1. 题目描述

在这里插入图片描述


2. 思路分析

已知每个有限的括号字符串必须是连续的,那么可以用栈来寻找以某个字符结尾最长的有限长度。

具体做法是我们始终保持栈底元素为当前已经遍历过的元素中 最后一个没有被匹配的右括号的下标,这样的做法主要是考虑了边界条件的处理,栈里其他元素维护左括号的下标

  • 对于遇到的每个 ‘(’ ,我们将它的下标放入栈中
  • 对于遇到的每个 ‘)’ ,我们先弹出栈顶元素表示匹配了当前右括号:
    • 如果栈为空,说明当前的右括号为没有被匹配的右括号,我们将其下标放入栈中来更新我们之前提到的 最后一个没有被匹配的右括号的下标;
    • 如果栈不为空,当前 右括号的下标减去栈顶元素 即为 以该右括号为结尾的最长有效括号的长度;

我们从前往后遍历字符串并更新答案即可。

注意: 如果一开始栈为空,第一个字符为左括号的时候我们会将其放入栈中,这样就不满足提及的 最后一个没有被匹配的右括号的下标,为了保持统一,我们在一开始的时候往栈中放入一个值为 − 1 −1 1 的元素。


3. 代码实现

class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int> stk;
        int res = 0;
        stk.push(-1);

        for (int i = 0; i < s.size(); i ++) {
            if (s[i] == '(') stk.push(i);
            else {
                stk.pop();
                if (stk.empty()) stk.push(i);
                else res = max(res, i - stk.top());
            }
        }
        return res;
    }
};

七、搜索旋转排序数组

1. 题目描述

在这里插入图片描述


2. 思路分析

  1. 首先二分找出哪个点是将数组分割成两段的;
  2. 确定 t a r g e t target target 是在哪一段的,划分条件是 t a r g e t target target n u m s [ 0 ] nums[0] nums[0] 的大小关系;
  3. 最后在确定的分段中二分查找 t a r g e t target target 即可。

3. 代码实现

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if (nums.empty()) return -1;

        // 二分找分割点, 这里用性质>=nums[0], 所以当找到边界时, 它将是最后一个比nums[0]大的数
        int l = 0, r = nums.size() - 1;
        while(l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid] >= nums[0]) l = mid;
            else r = mid - 1; 
        }

        //确定target在哪一段
        if (target >= nums[0]) l = 0;
        else l = r + 1, r = nums.size() - 1;

        //二分查找目标值
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        }

        // 这里写成nums[r], 当数组只有一个元素时, 两个二分查找代码都没有走, 而l在上面被+1, 这时会越界, 而r是length-1还是0, 不会产生越界
        if (nums[r] == target) return r;
        else return -1;
    }
};

八、在排序数组中查找元素的第一个和最后一个位置

1. 题目描述

在这里插入图片描述


2. 思路分析

直接进行二分查找即可找到目标值的第一个位置以及最后一个位置。


3. 代码实现

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if (nums.empty()) return {-1, -1};

        int l = 0, r = nums.size() - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        if (nums[l] != target) return {-1, -1};

        int L = r;

        l = 0, r = nums.size() - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid] <= target) l = mid;
            else r = mid - 1;
        }
        return {L, r};
    }
};

九、组合总和

1. 题目描述

在这里插入图片描述


2. 思路分析

直接进行深度优先搜索即可。
在这里插入图片描述


3. 代码实现

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;

    vector<vector<int>> combinationSum(vector<int>& c, int target) {
        dfs(c, 0, target);
        return ans;
    }

    void dfs(vector<int>& c, int u, int target) {
        if (target == 0) {
            ans.push_back(path);
            return;
        }
        if (u == c.size()) return;

        //枚举一下当前的数可以选几个,可以选0个,或者选i个总和不超过target的个数
        for (int i = 0; c[u] * i <= target; i ++ ) {
            //第一次选了0个,也就是什么都没选,不需要记录此次的路径,所以直接dfs下一个
            dfs(c, u + 1, target - c[u] * i);
            path.push_back(c[u]);
        }

        // 回溯,恢复现场
        for (int i = 0; c[u] * i <= target; i ++ ) {
            path.pop_back();
        }
    }
};

十、接雨水

1. 题目描述

在这里插入图片描述


2. 思路分析

单调栈是本文想要重点说明的一个方法。

因为本题是一道典型的单调栈的应用题。

简单来说就是当前柱子如果小于等于栈顶元素,说明形不成凹槽,则将当前柱子入栈;反之若当前柱子大于栈顶元素,说明形成了凹槽,于是将栈中小于当前柱子的元素pop出来,将凹槽的大小累加到结果中。

3. 代码实现

class Solution {
    public int trap(int[] height) {
        Stack<Integer> stack = new Stack<>();
        int res = 0;
        // 遍历每个柱体
        for (int i = 0; i < height.length; i++) {
           while (!stack.isEmpty() && height[stack.peek()] < height[i]) {
                int bottomIdx = stack.pop();
                // 如果栈顶元素一直相等,那么全都pop出去,只留第一个。
                while (!stack.isEmpty() && height[stack.peek()] == height[bottomIdx]) {
                    stack.pop();
                }
                if (!stack.isEmpty()) {
                    // stack.peek()是此次接住的雨水的左边界的位置,右边界是当前的柱体,即i。
                    // Math.min(height[stack.peek()], height[i]) 是左右柱子高度的min,减去height[bottomIdx]就是雨水的高度。
                    // i - stack.peek() - 1 是雨水的宽度。
                    res += (Math.min(height[stack.peek()], height[i]) - height[bottomIdx]) * (i - stack.peek() - 1);
                }
            }
            stack.push(i);
        }
        return res;
    }
}

 
非常感谢您阅读到这里,如果这篇文章对您有帮助,希望能留下您的点赞👍 关注💖 分享👥 留言💬thanks!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1257866.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python基础教程:强大的Pandas数据分析库

Pandas是一个基于 NumPy 的非常强大的开源数据处理库&#xff0c;它提供了高效、灵活和丰富的数据结构和数据分析工具&#xff0c;当涉及到数据分析和处理时&#xff0c;使得数据清洗、转换、分析和可视化变得更加简单和高效。本文中&#xff0c;我们将学习如何使用Pandas来处理…

外贸CRM客户管理系统是什么?外贸CRM作用?

外贸CRM客户管理系统有哪些&#xff1f;海洋建站如何选外贸软件&#xff1f; 企业的外贸业务面临着日益激烈的竞争。为了更好地管理客户关系、提高运营效率&#xff0c;越来越多的企业开始采用外贸CRM客户管理系统。那么&#xff0c;海洋建站来介绍一下&#xff0c;外贸CRM客户…

爆肝整理! Python 网络爬虫 + 数据分析 + 机器学习教程来了

前段时间&#xff0c;有小伙伴多次在后台留言询问 Python 爬虫教程的问题。经过这两个多月以来的收集与整理&#xff0c;汇集了多个高校以及公开课视频教程&#xff0c;包括 python 爬虫的入门、进阶与实践&#xff0c;共 9G 左右。爬虫作为机器学习语料库构建的主要方式&#…

快速上手Banana Pi BPI-R4 MediaTek MT7988A 开源路由器开发板

基础开发 准备开发 * 准备8G以上TF卡、USB转串口线、Ubuntu系统* 使用 USB 串行电缆&#xff08;3.3V TTL&#xff0c;波特115200&#xff09;连接到 BPI-R4 上的调试控制台G接地&#xff1b;RXBPI-R4输入&#xff1b;TXBPI-R4输出* BPI-R4 引导程序和设备选择跳线设置* 例子…

抖音开新店,十个设置,实操满满全是干货

抖店开店的第一天手足无措不知从哪入手&#xff1f;我给大家分享一个新手开店必须完成的十个设置&#xff0c;实操满满全是干货&#xff0c;每一项都很重要&#xff01;特别是新手一定要认真看&#xff0c;因为这个直接关系到你店铺能否正常经营&#xff0c;跟着我一起来实操一…

minio客户端基本操作

minio客户端基本操作 桶 创建桶 如果要创建新的桶 输入名称&#xff0c;点击创建即可&#xff0c;默认权限就行 删除桶 点击要删除的桶 点击删除 修改桶 如果哪天需要修改桶的权限或者其他信息&#xff0c;还是先点击这个桶进入详情 然后点击要修改的属性&#xff0c;选择…

力扣日记11.27-【二叉树篇】二叉树的最大深度

力扣日记&#xff1a;【二叉树篇】二叉树的最大深度 日期&#xff1a;2023.11.27 参考&#xff1a;代码随想录、力扣 104. 二叉树的最大深度 题目描述 难度&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最…

SparkDesk知识库 + ChuanhuChatGPT前端 = 实现轻量化知识库问答

上一篇 讯飞星火知识库文档问答Web API的使用&#xff08;二&#xff09; 把星火知识库搞明白了&#xff1b; 然后又花了时间学习了一下gradio的一些基础内容: 在Gradio实现两个下拉框进行联动案例解读&#xff1a;change/click/input实践&#xff08;三&#xff09; 在Gradio实…

P8A002-CIA安全模型-配置Linux描述网络安全CIA模型之可用性案例

【预备知识】 可用性(Availability) 数据可用性是一种以使用者为中心的设计概念,易用性设计的重点在于让产品的设计能够符合使用者的习惯与需求。以互联网网站的设计为例,希望让使用者在浏览的过程中不会产生压力或感到挫折,并能让使用者在使用网站功能时,能用最少的努力…

PHP 双门双向门禁控制板实时监控源码

本示例使用设备&#xff1a; 实时网络双门双向门禁控制板可二次编程控制网络继电器远程开关-淘宝网 (taobao.com) <?PHPheader("content-type:text/html;charsetGBK");$ThisIpget_local_ip(); //获取电脑IP地址 $server udp://.$ThisIp.:39192; $sock…

前置任务之安装jdk

已经安装过很多次了&#xff0c;但是每次安装都要搜好几次才能找到正确的&#xff0c;离大谱。 1.打开 oracle官网 https://www.oracle.com 然后切换到Java archive 下载192版本的&#xff0c;页面搜索ctrlF&#xff0c;【Java SE Development Kit】或者【jdk-8u192-windows-…

低代码开发平台:构建企业数字化生态系统的关键利器

近年来&#xff0c;随着人工智能、大数据分析技术、云计算等领域的迅速发展&#xff0c;企业数字化转型已成为不可避免的趋势。政策层面也对这一转型给予了大力支持&#xff0c;通过多项举措推动技术创新、信息化应用和数字化产业的发展。然而&#xff0c;我们要清醒认识到&…

不会提问不打紧,不敢提问才要命

最近在星球里回答了球友提出来的一些问题&#xff0c;我都给了回复&#xff0c;不经过在明确问题、探索问题的过程&#xff0c;对我启发挺大&#xff0c;特此来记录下感受和感悟。 缘起 最近新加入球友提的问题&#xff0c;有几次&#xff0c;我第一时间没看懂&#xff0c;甚…

Oracle Linux 9.3 发布

导读Oracle Linux 9 系列发布了第 3 个版本更新&#xff0c;支持 64 位 Intel 和 AMD (x86_64) 以及 64 位 Arm (aarch64) 平台。与所有的 Oracle Linux 版本一样&#xff0c;此版本与相应 RHEL 版本 100% 应用二进制兼容。 对于 x86_64 和 aarch64 架构&#xff0c;Oracle Li…

关于高斯核是实现尺度空间变换的唯一性思考

受到自己的启发&#xff0c;唯一性证明有了思路&#xff1a; 谁的一阶导数是自己&#xff0c;exp&#xff08;x&#xff09;&#xff0c;只有是自己&#xff0c;才能保持自己在其中。 为什么不能是exp&#xff08;x&#xff09;呢&#xff1f;不变导致图像不会模糊&#xff0…

微软发布了Orca 2,一对小型语言模型,它们的性能超越了体积更大的同类产品

尽管全球目睹了OpenAI的权力斗争和大规模辞职&#xff0c;但作为AI领域的长期支持者&#xff0c;微软并没有放慢自己的人工智能努力。今天&#xff0c;由萨提亚纳德拉领导的公司研究部门发布了Orca 2&#xff0c;这是一对小型语言模型&#xff0c;它们在零样本设置下对复杂推理…

基于PLC的物料分拣控制传送带控制系统设计

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;物料分拣 获取完整论文报告PLC梯形图工程源文件 传送带在先进制造领域中扮演着极其重要的角色。它可以搬运货物、分拣物品、代替人的繁重劳动。可以实现生产的机械化和自动化&#xff0c;能在有害环境下操作以保护人身安全…

条形码格式

条形码格式 简述EAN码EAN-13EAN-8 UPC码UPC-AUPC-E 简述 EAN码 EAN码&#xff08;European Article Number&#xff09;是国际物品编码协会制定的一种全球通用的商用条码。EAN码分为&#xff1a;标准版&#xff08;EAN-13&#xff09; 和 缩短版&#xff08;EAN-8&#xff09…

Eolink 通过多项信创环境适配认证,信创生态兼容更全面!

近日&#xff0c;Eolink 通过海光、龙芯、统信、达梦以及 TiDB 数据库等多个兼容性认证&#xff0c;信创生态适配能力全面提升。 信息技术应用创新的自主可控是国家实现科技自立自强的一项重要发展战略。作为国内 API 全生命周期管理理念的先行者&#xff0c;Eolink 积极响应国…

Java学习路线第一篇:Java基础(2)

这篇则分享Java学习路线第一part&#xff1a;Java基础&#xff08;2&#xff09; 从看到这篇内容开始&#xff0c;你就是被选定的天命骚年&#xff0c;将承担起学完Java基础的使命&#xff0c;本使命为单向契约&#xff0c;你可选择YES或者选择YES。 具体路线安排&#xff1a…