LeetCode高频算法刷题记录1

news2024/11/25 15:25:47

文章目录

  • 1. 无重复字符的最长子串【中等】
    • 1.1 题目描述
    • 1.2 解题思路
    • 1.3 代码实现
  • 2. 反转链表【简单】
    • 2.1 题目描述
    • 2.2 解题思路
    • 2.3 代码实现
  • 3. LRU 缓存【中等】
    • 3.1 题目描述
    • 3.2 解题思路
    • 3.3 代码实现
  • 4. 数组中的第K个最大元素【中等】
    • 4.1 题目描述
    • 4.2 解题思路
    • 4.3 代码实现
  • 5. 三数之和【中等】
    • 5.1 题目描述
    • 5.2 解题思路
    • 5.3 代码实现

1. 无重复字符的最长子串【中等】

题目链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/
参考题解:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-by-leetc-2/

1.1 题目描述

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例1:

输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

示例2:

输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。

示例3:

输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

1.2 解题思路

依次考察以字符串中每个字符 s[i] 为起始的最长子串,一个关键的性质是,随着 i 的增加,其最长子串的结束位置也在不断增加。具体来说,使用一个哈希表 repeat 来存不重复的字符,每次往 repeat 中去掉子串最左边的字符,然后再不断试探能否往右继续把子串加长,最后比较当前子串是否是历史最长子串。

1.3 代码实现

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char> repeat;
        int r = -1;
        int ans = 0;
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                repeat.erase(s[i - 1]);
            }
            while (r + 1 < n && !repeat.count(s[r + 1])) {
                repeat.insert(s[r + 1]);
                ++r;
            }
            ans = max(ans, r - i + 1);
        }
        return ans;
    }
};

2. 反转链表【简单】

题目链接:https://leetcode.cn/problems/reverse-linked-list/
参考题解:https://leetcode.cn/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-by-leetcode-solution-d1k2/

2.1 题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例1:
在这里插入图片描述

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例2:
在这里插入图片描述

输入:head = [1,2]
输出:[2,1]

示例3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

2.2 解题思路

可以模拟一下反转的过程,捋清楚各个操作的先后顺序。为了实现某个节点 current 的反转,需要记录其前置节点,而在 current 反转后,其后继节点便丢失了,因此需要用一个临时节点 tmp 把 current 的后继节点暂时记录下来。

2.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* reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* current = head;
        while (current != nullptr) {
            ListNode* tmp = current -> next;
            current -> next = pre;
            pre = current;
            current = tmp;
        }
        return pre;
    }
};

3. LRU 缓存【中等】

题目链接:https://leetcode.cn/problems/lru-cache/
参考题解:https://leetcode.cn/problems/lru-cache/solution/lruhuan-cun-ji-zhi-by-leetcode-solution/

3.1 题目描述

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例:

输入
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4

提示:

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 105
  • 最多调用 2 * 105 次 get 和 put

3.2 解题思路

用双向链表模拟缓存,越靠近头部表示越近使用过,越靠近尾部表示越久未使用。为了快速找到缓存中的元素,需要用一个哈希表来存储各个节点的位置。

3.3 代码实现

官方题解代码:

// class LRUCache {
// public:
//     LRUCache(int capacity) {

//     }
    
//     int get(int key) {

//     }
    
//     void put(int key, int value) {

//     }
// };

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */
 struct DLinkedNode {
    int key, value;
    DLinkedNode* prev;
    DLinkedNode* next;
    DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
    DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

class LRUCache {
private:
    unordered_map<int, DLinkedNode*> cache;
    DLinkedNode* head;
    DLinkedNode* tail;
    int size;
    int capacity;

public:
    LRUCache(int _capacity): capacity(_capacity), size(0) {
        // 使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head->next = tail;
        tail->prev = head;
    }
    
    int get(int key) {
        if (!cache.count(key)) {
            return -1;
        }
        // 如果 key 存在,先通过哈希表定位,再移到头部
        DLinkedNode* node = cache[key];
        moveToHead(node);
        return node->value;
    }
    
    void put(int key, int value) {
        if (!cache.count(key)) {
            // 如果 key 不存在,创建一个新的节点
            DLinkedNode* node = new DLinkedNode(key, value);
            // 添加进哈希表
            cache[key] = node;
            // 添加至双向链表的头部
            addToHead(node);
            ++size;
            if (size > capacity) {
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode* removed = removeTail();
                // 删除哈希表中对应的项
                cache.erase(removed->key);
                // 防止内存泄漏
                delete removed;
                --size;
            }
        }
        else {
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            DLinkedNode* node = cache[key];
            node->value = value;
            moveToHead(node);
        }
    }

    void addToHead(DLinkedNode* node) {
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }
    
    void removeNode(DLinkedNode* node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    void moveToHead(DLinkedNode* node) {
        removeNode(node);
        addToHead(node);
    }

    DLinkedNode* removeTail() {
        DLinkedNode* node = tail->prev;
        removeNode(node);
        return node;
    }
};

4. 数组中的第K个最大元素【中等】

题目链接:https://leetcode.cn/problems/kth-largest-element-in-an-array/
参考题解:https://leetcode.cn/problems/kth-largest-element-in-an-array/solution/partitionfen-er-zhi-zhi-you-xian-dui-lie-java-dai-/

4.1 题目描述

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:

  • 1 <= k <= nums.length <= 105
  • -104 <= nums[i] <= 104

4.2 解题思路

最简单的思路就是先把数组从小到大排序,返回倒数第 k 个元素即为所求。而根据快速排序每次都会确定一个元素最终位置的这一特点,可以使求解更加快速,即如果当前确定的元素位置恰好就是倒数第 k 个,那么直接返回就可以了。本题的难点是实现快速排序,处理边界情况。

4.3 代码实现

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        srand(time(0));
        int n = nums.size();
        int target = n - k;
        int left = 0;
        int right = n - 1;
        while (true) {
            int pivot = partition(nums, left, right);
            if (pivot == target) {
                return nums[pivot];
            }
            else if (pivot < target) {
                left = pivot + 1;
            }
            else {
                right = pivot - 1;
            }
        }
    }

    int partition(vector<int>& nums, int left, int right) {
        int tmp = rand() % (right - left + 1) + left;
        swap(nums[tmp], nums[left]);
        int pivot = nums[left];
        int j = left;
        for (int i = left + 1; i <= right; ++i) {
            if (nums[i] <= pivot) {
                ++j;
                swap(nums[i], nums[j]);
            }
        }
        swap(nums[left], nums[j]);
        return j;
    }
};

5. 三数之和【中等】

题目链接:https://leetcode.cn/problems/3sum/
参考题解:https://leetcode.cn/problems/3sum/solution/san-shu-zhi-he-by-leetcode-solution/

5.1 题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意: 答案中不可以包含重复的三元组。

示例1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

5.2 解题思路

首先把数组从小到大排序,这样可以减少循环次数。要满足 a + b + c = 0,如果当前遍历的是 a ,那么只需要判断 b + c 是不是等于 -a 就行了,对于从左往右遍历的每一个 b ,从右往左遍历 c ,这样 b + c 会从大不断变小,直到小于等于 -a ,并且 b 增大的话, c 只能不断变小往左走,否则 b + c 还会变大。所以 b 和 c 可以放到同一个循环中遍历。

5.3 代码实现

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<vector<int>> ans;
        for (int a = 0; a < n; ++a) {
            if (a > 0 && nums[a] == nums[a - 1]) {
                continue;
            }
            int target = -nums[a];
            int c = n - 1;
            for (int b = a + 1; b < n; ++b) {
                if (b > a + 1 && nums[b] == nums[b - 1]) {
                    continue;
                }
                while (b < c && nums[b] + nums[c] > target) {
                    --c;
                }
                if (b == c) {
                    break;
                }
                if (nums[a] + nums[b] + nums[c] == 0) {
                    ans.push_back({nums[a], nums[b], nums[c]});
                }
            }
        }
        return ans;
    }
};

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

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

相关文章

吴恩达OpenAI最新课程:prompt-engineering-for-developers读书笔记

文章目录 一、前言二、Prompt编写原则2.1 环境配置2.2 编写清晰、具体的指令2.2.1 使用分隔符2.2.2 结构化输出&#xff08;JSON、HTML等&#xff09;2.2.3 要求模型检查条件是否满足2.2.4 提供少量示例&#xff08;Few-shot Prompting&#xff09; 2.3 指导模型思考2.3.1 指定…

未来工业维护:探索数据分析与机器学习的融合之路

随着工业领域相关技术的不断发展&#xff0c;预测性维护作为一种先进的维护策略&#xff0c;正日益受到企业的重视。预测性维护的核心目标是通过准确预测设备故障的发生时间&#xff0c;实现及时维护和优化生产效率。而在实现这一目标的过程中&#xff0c;数据分析和机器学习的…

FreeRTOS:任务状态和信息查询

目录 一、任务相关 API函数预览二、任务相关API函数详解2.1uxTaskPriorityGet()2.2vTaskPrioritySet()2.3uxTaskGetSystemState() ※※※※※2.4vTaskGetInfo() ※※※※※2.5xTaskGetApplicationTaskTag()2.6xTaskGetCurrentTaskHandle()2.7xTaskGetHandle()2.8xTaskGetIdleTa…

教你用JMeter做接口测试的几个简单实例

目录 前言 1、登录&#xff08;POST&#xff09; 登录 2、获取学生信息&#xff08;GET&#xff09; 获取学生信息 3、添加学生信息&#xff08;POST&#xff0c;JSON&#xff09; 添加学生信息 4、学生充值金币&#xff08;POST&#xff0c;Cookie&#xff09; 学生金…

【Linux】11. 进程控制

小实验(谨慎测试) 1. 进程退出码的引出 2. 进程码的使用 3. 进程退出 3.1 进程退出情况 进程退出分三种情况&#xff1a; 1.代码运行完毕&#xff0c;结果正确 – return 0; 2.代码运行完毕&#xff0c;结果不正确 – 根据退出码判断错误情况 3.代码没有运行完毕&#xff0c;…

如何0基础自学黑客(网络安全)技术,万字长文教你如何学习黑客(网络安全)

一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员&#xff08;以编程为基础的学习&#xff09;再开始学习 我在之前的回答中&#xff0c;我都一再强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而…

【数字化转型-06】数字化转型咨询项目中如何做好高层访谈

咨询项目中少不了至关重要的一步&#xff0c;那就是高层访谈&#xff0c;做好高层访谈&#xff0c;对于咨询项目至关重要&#xff0c;我们接触的维度越高&#xff0c;就会越能把控项目的真实意图&#xff0c;有的放矢&#xff0c;不会让下面的人带偏&#xff1b;另一方面我们也…

Vue3 使用 Ts 泛型 封装本地存储

前期回顾 NVM —— 你把我玩明白_彩色之外的博客-CSDN博客本文将学习 使用 NVM 管理node版本https://blog.csdn.net/m0_57904695/article/details/130670262?spm1001.2014.3001.5501 目录 新建 \src\utils\storage-utils.ts 使用 泛型示例 泛型交换变量 泛型 strin…

在Linux系统中用vim编写第一个C语言之gcc编译器

文章目录 在Linux系统中用vim编写第一个C语言HelloWorld第一步 创建第二步 编写第三步&#xff0c;编译第四步 运行 gcc四步骤gcc常用选项 在Linux系统中用vim编写第一个C语言HelloWorld 第一步 创建 vim HelloWorld.c第二步 编写 #include<stdio.h>int main(){printf(…

Android Jsoup爬取网页数据及其局限性,接口爬取数据的思路

1.Jsoup jsoup 是一款Java 的HTML解析器&#xff0c;可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API&#xff0c;可通过DOM&#xff0c;CSS以及类似于jQuery的操作方法来取出和操作数据。 需求是需要获取某个网站上的排行榜数据&#xff0c;用作App展示&am…

【axios】后端未收到前端post传参

今天遇到过问题&#xff0c;前端post请求参数明明已经传过去了&#xff0c;可是后端说没收到&#xff0c;不知道后端大哥是不是故意搞我。 代码前端图如下↓ 代码 import axios from axios //对象形式 const val {pass:123,user:name}axios.post(/api/login, val).then(res>…

Vue3-黑马(十三)

目录&#xff1a; &#xff08;1&#xff09;vue3-router-动态路由3 &#xff08;2&#xff09;vue3-进阶router-动态菜单 &#xff08;3&#xff09;vue3-进阶-router-令牌-获取用户信息 &#xff08;1&#xff09;vue3-router-动态路由3 登录页面后&#xff0c;如果点击了…

Android技术探索与实践:从新功能体验到故障调试的全方位探索

目录 Android技术探索与实践&#xff1a;从新功能体验到故障调试的全方位探索 第一章&#xff1a;技术解析 Android平台的架构和工作原理 应用组件的生命周期和交互方式 Android开发中常用的设计模式和技术框架解析 第二章:产品新功能体验测评 深入了解最新发布的Androi…

【安卓源码】Binder机制5 -- Java层Framework Binder机制和 AIDL

图中红色代表整个framework层 binder架构相关组件&#xff1b; Binder类代表Server端&#xff0c;BinderProxy类代码Client端&#xff1b;图中蓝色代表Native层Binder架构相关组件&#xff1b;上层framework层的Binder逻辑是建立在Native层架构基础之上的&#xff0c;核心逻辑都…

shell编程:概述、脚本入门、变量、运算符、条件判断、流程控制、读取控制台、函数、正则表达式、文本处理工具、综合案例

第 1 章 Shell 概述 1&#xff09;Linux 提供的 Shell 解析器有 [atguiguhadoop101 ~]$ cat /etc/shells /bin/sh /bin/bash /usr/bin/sh /usr/bin/bash /bin/tcsh /bin/csh2&#xff09;bash 和 sh 的关系 sh&#xff1a;比较基础bash&#xff1a;功能更加强大&#xff0c;默…

三十四、Hybrid 接口用法解析

文章目录 前言交换机接口类型有哪些Hybrid 端口使用场景什么时候必须使用 Hybrid 一、Hybrid 特点二、Hybrid 当做 access和trunk使用三、Hybrid 特殊用法 前言 交换机接口类型有哪些 Access、trunk、Hybrid、qinq Hybrid 端口使用场景 接pc、服务器、接交换机、接路由器&a…

Linux守护进程

"忍耐的灵魂啊&#xff0c;安静地运转吧~" 我们先来看看这个场景。这是一个常见的基于TCP套接字的网络服务器&#xff0c;服务端接收客户端发送的消息&#xff0c;收到后并向echo回响给客户端。 对于Linux而言&#xff0c;终端只能有一个前台进程&#xff0c;这也是为…

行业唯一丨冠珠出席“中国企业社会责任高峰论坛”,并荣获人民日报社“ESG年度案例”

践行社会责任&#xff0c;推动品牌高质量发展。5月11日&#xff0c;由人民日报社指导、人民日报社经济社会部主办的“中国企业社会责任高峰论坛”在上海盛大举行。 本次论坛围绕乡村振兴、共同富裕、绿色低碳等重点议题进行深入研讨&#xff0c;邀请国家发展和改革委员会、商务…

教程硬货|微信小程序开发之基于vue的微信开发工具JS文件解读(一)

文章目录 1 前言2 前期准备3 微信开发者工具3.1 创建项目3.2 页面介绍 4 读懂Pages4.1 index.wxss4.2 index.wxml4.3 index.json4.4 index.js 5 logs6 小程序的主要文件6.1 app.js6.2 app.json 7 讨论 1 前言 鉴于前段时间出的第一篇记录安装Nodejs和HBuilderX搭建、部署微信小…

【C++】内存分区模型

目录 1、缘起 2、内存分区模型 2.1、程序运行前 2.2、程序运行后 3、总结 1、缘起 前几天学习完了 C 的 基础语法 知识点&#xff0c;现在终于要踏上学习 C 核心编程 的旅程了&#xff0c;期待沿途中所遇到的风景。 2、内存分区模型 C 程序在执行时&#xff0c;将内存大…