面试经典150题 -- 链表 (总结)

news2024/11/24 13:55:03

总的地址 : 

面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

c++链表总结 : 

链表总结 -- 《数据结构》-- c/c++-CSDN博客

141 . 环形链表

详细题解参考 : 

141 . 环形链表-CSDN博客

这里给出慢双指针的代码 : 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head == nullptr || head->next == nullptr) return false;
        ListNode* slow = head;
        ListNode* fast = head->next;
        while(slow != fast){
            if(fast == nullptr || fast->next == nullptr){
                return false;
            }
            slow = slow->next;
            fast = fast->next->next;
        } 
        return true;
    }
};

2 . 两数相加

法1

递归 , 这里是具有子问题的性质的 , 然后模拟乘法 ;

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        return addTwo(l1, l2, 0);
    }

    // l1 和 l2 为当前遍历的节点,carry 为进位
    private ListNode addTwo(ListNode l1, ListNode l2, int carry) {
        if (l1 == null && l2 == null) // 递归边界:l1 和 l2 都是空节点
            return carry != 0 ? new ListNode(carry) : null; // 如果进位了,就额外创建一个节点
        if (l1 == null) { // 如果 l1 是空的,那么此时 l2 一定不是空节点
            l1 = l2;
            l2 = null; // 交换 l1 与 l2,保证 l1 非空,从而简化代码
        }
        carry += l1.val + (l2 != null ? l2.val : 0); // 节点值和进位加在一起
        l1.val = carry % 10; // 每个节点保存一个数位
        l1.next = addTwo(l1.next, (l2 != null ? l2.next : null), carry / 10); // 进位
        return l1;
    }
}

法二 : 

迭代 , 模拟这个加法的过程 ;

/**
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        auto dmy = new ListNode() ;// 哨兵结点
        auto cur = dmy ;
        ListNode* tmp ;
        int cay = 0 ;
        while(l1 || l2 || cay){
            cay += (l1 ? l1->val : 0) + (l2 ? l2->val : 0) ;
            tmp = new ListNode(cay % 10);
            cur -> next = tmp ; 
            cur = cur -> next ;
            cay /= 10 ;
            if(l1) l1 = l1 -> next ;
            if(l2) l2 = l2 -> next ;
        }
        return dmy -> next ;
    }
};

21 . 合并两个有序链表

递归 

/**
 * 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) {
        if(l1 == nullptr) return l2;
        else if(l2 == nullptr) return l1;
        else if(l1->val < l2->val){
            l1->next = mergeTwoLists(l1->next,l2);
            return l1;
        } else {
            l2->next = mergeTwoLists(l1,l2->next);
            return l2;
        }
    }
};

双指针迭代

/**
 * 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) {
        ListNode* ans = new ListNode(-1);

        ListNode* tmp = ans;
        while(l1!=nullptr && l2!=nullptr){
            if(l1->val <= l2->val){
                tmp->next = l1;
                l1 = l1->next;
            }else {
                tmp->next = l2;
                l2 = l2->next;
            }
            tmp = tmp->next;
        }
        tmp->next = l1==nullptr ? l2 : l1;
        return ans->next;
    }
};

138 . 随机链表的复制

题意

这一题的题意可能难以理解 ; 

下面给出在lc评论区的一段话,可能帮助理解 : 

 题目要求我们给定一个链表,每个节点除了包含一个指向下一个节点的指针(next),还包含一个随机指针(random),该随机指针可以指向链表中的任何节点或空节点。我们需要构造一个深拷贝的链表,使得新链表与原链表具有相同的结构和值,但是新链表中的节点均为全新的节点。

换句话说,我们需要创建一个与原链表结构相同的链表,其中每个节点的值与对应原节点的值相同,并且每个节点的next指针和random指针都指向新链表中对应的节点。

例如,原链表为:A -> B -> C,其中A.random指向C,B.random指向A,C.random指向B。那么深拷贝后的链表为:A' -> B' -> C',其中A'.random指向C',B'.random指向A',C'.random指向B'。

(难点就是创建链表时random指的可能还没创建出来,要解决的就是这个)

思路 : 

哈希表

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == nullptr) return nullptr;
        Node* cur = head;
        unordered_map<Node*, Node*> map;
        // 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
        while(cur != nullptr) {
            map[cur] = new Node(cur->val);
            cur = cur->next;
        }
        cur = head;
        // 4. 构建新链表的 next 和 random 指向
        while(cur != nullptr) {
            map[cur]->next = map[cur->next];
            map[cur]->random = map[cur->random];
            cur = cur->next;
        }
        // 5. 返回新链表的头节点
        return map[head];
    }
};

92 . 反转链表II

图文题解见 : 

反转链表【基础算法精讲 06】-CSDN博客

这一题只需要反转[l,r]的部分结点

将反转链表的前一个结点成为p0 ;

然后和上一题一样反转链表 ;

也就是 : 

把p0的next指针指向cur,p0指向pre

有一个特殊的情况,当l = 1 的时候 , 没有p0 , 可以在前面加上一个哨兵结点为p0 ;

代码如下 : 

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        ListNode* dmy = new ListNode(0,head) ;
        ListNode* p0 = dmy ;
        for(int i=0;i<left-1;i++){
            p0 = p0 -> next ;
        }
        ListNode* pre = nullptr ;
        ListNode* cur = p0->next ;
        for(int i=1;i<=right-left+1;i++){
            ListNode* nxt = cur->next ;
            cur->next = pre ;
            pre = cur ;
            cur = nxt ;
        }
        p0->next->next = cur ;
        p0->next = pre ;
        return dmy->next ;
    }
};

25 . k个一组反转链表

和上题类似 ,每逢k个反转依次一次就好了 ;

/**
 * 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* reverseKGroup(ListNode* head, int k) {
        int n = 0 ;
        ListNode* cur = head ;
        while(cur!=nullptr){ // 拿到链表的长度 
            n++;
            cur = cur->next ;
        }
        ListNode* dmy = new ListNode(0,head) ;
        ListNode* p0 = dmy ;
        while(n>=k){
            n-=k;
            ListNode* pre = nullptr;
            ListNode* cur = p0->next ;
            for(int i=0;i<k;i++){
                ListNode* nxt = cur->next;
                cur->next = pre ;
                pre = cur ;
                cur = nxt ;
            }
            ListNode* tmp = p0->next ;
            p0->next->next = cur ;
            p0->next = pre ;
            p0 = tmp ;
        }
        return dmy -> next;
    }
};

19 . 删除链表的倒数第N个结点

快慢双指针!!!

快的先跑n个,然后快慢同时跑,快的跑到终点,慢的也就到了倒数第n+1个的位置!!!

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dmy = new ListNode(0,head) ;
        dmy->next = head ;
        ListNode* fast = head , *slow  = dmy ;
        // 快的先跑n个
        for(int i=0;i<n;i++) fast = fast -> next ;
        // 快的跑 len - n , 慢的也就跑到了倒数第n个
        while(fast){
            fast = fast -> next ;
            slow = slow -> next ;
        }
        slow -> next = slow -> next -> next ;
        ListNode* ans = dmy -> next ;
        delete dmy ;
        return ans ;
    }
};

82 . 删除排序链表中的重复元素 II

双指针解决!!!

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null){
            return head ;
        }
        ListNode dummy = new ListNode(0,head) ;

        ListNode cur = dummy ;

        while(cur.next != null && cur.next.next != null){
            if(cur.next.val == cur.next.next.val){
                int x = cur.next.val ;
                while(cur.next != null && cur.next.val == x){
                    cur.next = cur.next.next ;
                }
            }else{
                cur = cur.next ;
            }
        }
        return dummy.next ;
    }
}

61 . 旋转链表

先变成环 , 再剪开 ;

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (k == 0 || head == nullptr || head->next == nullptr) {
            return head;
        }
        int n = 1;
        ListNode* iter = head;
        while (iter->next != nullptr) {
            iter = iter->next;
            n++;
        }
        int add = n - k % n;
        if (add == n) {
            return head;
        }
        iter->next = head;
        while (add--) {
            iter = iter->next;
        }
        ListNode* ret = iter->next;
        iter->next = nullptr;
        return ret;
    }
};

86 . 分隔链表

直观来说我们只需维护两个链表 smalll 和 large即可,small链表按顺序存储所有小于 xxx 的节点,large 链表按顺序存储所有大于等于 x 的节点。遍历完原链表后,我们只要将 small 链表尾节点指向 large链表的头节点即能完成对链表的分隔。

代码如下 : 

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode* small = new ListNode(0);
        ListNode* smallHead = small;
        ListNode* large = new ListNode(0);
        ListNode* largeHead = large;
        while (head != nullptr) {
            if (head->val < x) {
                small->next = head;
                small = small->next;
            } else {
                large->next = head;
                large = large->next;
            }
            head = head->next;
        }
        large->next = nullptr;
        small->next = largeHead->next;
        return smallHead->next;
    }
};

146 . LRU缓存

双链表 + 哈希表

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;
    }
};

参考 : 

代码随想录

链表总结 -- 《数据结构》-- c/c++-CSDN博客

141 . 环形链表-CSDN博客

反转链表【基础算法精讲 06】-CSDN博客

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

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

相关文章

【JAVA WEB】JS的应用

目录 猜数字 预期效果 涉及接口预览 代码实现 表白墙 预期效果 代码实现 代办事项 预期效果 代码实现 猜数字 预期效果 涉及接口预览 //当我们要获得文本框上输入的内容&#xff0c;可以通过.value获取 let guess_text document.querySelector(.guess_num) guess_…

社交商业策略:揭秘Facebook Shops的成功之道

随着数字化时代的不断发展&#xff0c;社交媒体已经成为了商业活动的重要平台之一。在这个趋势下&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;不仅仅是人们交流互动的场所&#xff0c;更成为了商家开展电子商务的重要渠道。其中&#xff0c;Facebook Shops…

MySQL中SQL语句的执行流程(高频考点)

文章目录 前言SQL语句的执行流程查询语句的执行流程更新语句的执行流程 总结 前言 昨天跟大家讲了MySQL的基础架构&#xff08;链接&#xff1a;MySQL的基础架构&#xff09;&#xff0c;今天讲一讲我们的高频面试题MySQL中SQL语句的执行流程。 建议看完 MySQL的基础架构 再来…

flutter 文件上传组件和大文件分片上传

文件分片上传 资料 https://www.cnblogs.com/caijinglong/p/11558389.html 使用分段上传来上传和复制对象 - Amazon Simple Storage Service 因为公司使用的是亚马逊的s3桶 下面是查阅资料获得的 亚马逊s3桶的文件上传分片 分段上分为三个步骤&#xff1a;开始上传、上传对…

【漏洞复现-通达OA】通达OA WHERE_STR 存在前台SQL注入漏洞

一、漏洞简介 通达OA(Office Anywhere网络智能办公系统)是由北京通达信科科技有限公司自主研发的协同办公自动化软件,是与中国企业管理实践相结合形成的综合管理办公平台。通达OA WHERE_STR存在前台SQL注入漏洞,攻击者可通过该漏洞获取数据库敏感信息。 二、影响版本 ●…

全网最详细的从0到1的turbo pnpm monorepo的前端工程化项目[vitePress篇]

全网最详细的从0到1的turbo pnpm monorepo的前端工程化项目[vitePress篇] 前言选型为什么选择VitePress安装VitePress运行优化默认UI使用自定义UI编辑自定义布局编写home页面组件编写page页面组件 结语 前言 一个好的工程化项目&#xff0c;必然有一个好的文档管理&#xff0c;…

【Go-Zero】goctl生成model层后报错Unresolved reference ‘ErrNotFound‘解决方案

【Go-Zero】goctl生成model层后报错Unresolved reference ErrNotFound’解决方案 大家好 我是寸铁&#x1f44a; 总结了一篇goctl生成model层后报错Unresolved reference ErrNotFound’报错解决方案的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 问题背景 大家好&#xff…

Sora 的工作原理

原文&#xff1a;How Sora Works (And What It Means) 作者&#xff1a; DAN SHIPPER OpenAI 的新型文本到视频模型为电影制作开启了新篇章 DALL-E 提供的插图。 让我们先明确一点&#xff0c;我们不会急急忙忙慌乱。我们不会预测乌托邦或预言灾难。我们要保持冷静并... 你…

java面试多线程篇

文章说明 在文章中对所有的面试题都进行了难易程度和出现频率的等级说明 星数越多代表权重越大&#xff0c;最多五颗星&#xff08;☆☆☆☆☆&#xff09; 最少一颗星&#xff08;☆&#xff09; 1.线程的基础知识 1.1 线程和进程的区别&#xff1f; 难易程度&#xff1a;☆☆…

Filterajax

1.Filter概念 概念:表示过滤器,是JavaWeb三大组件(Servlet,Filter,Listener)之一;过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能.过滤器可以完成一些通用操作比如:登录添加购物车,视频广告,敏感字符处理等等... 2.Filter快速入门 3.Listener 4.Ajax学习 1.使用场…

移动通信相关知识学习笔记

一、移动通信架构简图 移动无线的接入网是专指各种基站设备。核心网就是各种交换机。 二、无线信号基本原理 无线网络中&#xff0c;使用AP设备和天线来实现有线和无线信号互相转换。如上图所示&#xff0c;有线网络侧的数据从AP设备的有线接口进入AP后&#xff0c;经AP处理为…

一.重新回炉Spring Framework: 理解Spring IoC

1. 写在前面的话 说实话&#xff0c;从事java开发工作时间也不短了&#xff0c;对于Spring Framework&#xff0c;也是天天用&#xff0c;这期间也碰到了很多问题&#xff0c;也解决了很多问题。可是&#xff0c;总感觉对Spring Framework还是一知半解&#xff0c;不能有个更加…

PCIe学习笔记(2)错误处理和AER/DPC功能

文章目录 PCIe ErrorAER (Advanced Error Reporting)DPC (Downstream Port Containment) 处理器上错误通常可分为detected和undetected error。Undetected errors可能变得良性(benign)&#xff0c;也可能导致系统故障如silent data corruptions (SDC)。Detected errors则又可分…

2024024期传足14场胜负前瞻

2024024期赛事由亚冠5场&#xff0c;欧冠4场、英超1场、英冠4场组成。售止时间为2月20日&#xff08;周二&#xff09;17点30分&#xff0c;敬请留意&#xff1a; 本期中深盘中等&#xff0c;1.5以下赔率5场&#xff0c;1.5-2.0赔率5场&#xff0c;其他场次是平半盘、平盘。本期…

Django后端开发——ORM

文章目录 参考资料ORM-基础字段及选项字段类型练习——添加模型类应用bookstore下的models.py数据库迁移——同步至mysqlmysql中查看效果 字段选项Meta类定义示例&#xff1a;改表名应用bookstore下的models.py终端效果 练习——改表名字段选项修改应用bookstore下的models.py终…

DVWA 靶场之 Brute Force-LowMedium(前期配置铺垫与渗透方法及源码分析)

首先登录 DVWA 靶场 DVWA 默认的用户有5个&#xff0c;用户名及密码如下&#xff1a; admin/passwordgordonb/abc1231337/charleypablo/letmeinsmithy/password 难度等级设置为 low &#xff0c;我们先从最简单的开始 来到 Brute Force&#xff08;暴力破解&#xff09; 我们可…

手写myscrapy(二)

我们看一下scrapy的系统架构设计方法和思路&#xff1a; 模块化设计&#xff1a; Scrapy采用模块化设计&#xff0c;将整个系统划分为多个独立的模块&#xff0c;包括引擎&#xff08;Engine&#xff09;、调度器&#xff08;Scheduler&#xff09;、下载器&#xff08;Downl…

目录IO 2月19日学习笔记

1. lseek off_t lseek(int fd, off_t offset, int whence); 功能: 重新设定文件描述符的偏移量 参数: fd:文件描述符 offset:偏移量 whence: SEEK_SET 文件开头 SEE…

C++ 浮点数二分 数的三次方根

给定一个浮点数 n &#xff0c;求它的三次方根。 输入格式 共一行&#xff0c;包含一个浮点数 n 。 输出格式 共一行&#xff0c;包含一个浮点数&#xff0c;表示问题的解。 注意&#xff0c;结果保留 6 位小数。 数据范围 −10000≤n≤10000 输入样例&#xff1a; 1000.00…