C++数据结构与算法——链表

news2024/11/17 21:23:25

C++第二阶段——数据结构和算法,之前学过一点点数据结构,当时是基于Python来学习的,现在基于C++查漏补缺,尤其是树的部分。这一部分计划一个月,主要利用代码随想录来学习,刷题使用力扣网站,不定时更新,欢迎关注!

文章目录

  • 一、移除链表元素(力扣203)
  • 二、设计链表(力扣707)
  • 三、翻转链表(力扣206)
  • 四、两两交换链表中的节点 (力扣24)
  • 五、删除链表的倒数第 N 个结点(力扣19)
  • 六、链表相交(力扣面试题02.07链表相交)
  • 七、环形链表Ⅱ(力扣142)

一、移除链表元素(力扣203)

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点
在这里插入图片描述
头结点单独处理

/**
 * 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* removeElements(ListNode* head, int val) {
        // 先判断头结点是不是val,如果是的话,head就后移
        while(head!=NULL && head->val==val){
            ListNode * temp ;
            temp = head;
            head = head->next;
            delete temp;
        }
        // 定义一个指针指向中间过程的结点
        ListNode * cur = head;
        while(cur!=NULL&&cur->next!=NULL){
            if(cur->next->val==val){
                // 删除下一个结点
                ListNode *temp = cur->next;
                cur->next = cur->next->next;
                delete temp;
            }
            else{
                cur = cur->next;
            }
        }
        return head;
    }
};

在这里插入图片描述

虚拟头节点

/**
 * 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* removeElements(ListNode* head, int val) {
        // 定义一个虚拟头结点
        ListNode * dummyHead = new ListNode(0); 
        dummyHead->next = head;
        // 定义一个遍历结点
        ListNode *cur = dummyHead;
        while(cur!=NULL&&cur->next!=NULL){
            if(cur->next->val==val){
                ListNode * temp = cur->next;
                cur->next = cur->next->next;
                delete temp;
            }
            else{
                cur = cur->next;
            }

        }
        return dummyHead->next;
    }
};

在这里插入图片描述

二、设计链表(力扣707)

你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList 类:
MyLinkedList() 初始化 MyLinkedList 对象。
int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
示例:
输入
[“MyLinkedList”, “addAtHead”, “addAtTail”, “addAtIndex”, “get”, “deleteAtIndex”, “get”]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]
解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 3
提示:
0 <= index, val <= 1000
请不要使用内置的 LinkedList 库。
调用 get、addAtHead、addAtTail、addAtIndex 和 deleteAtIndex 的次数不超过 2000 。

// 创建一个结点
class LinkNode{
public:
    LinkNode(int inputVal){
        this->val = inputVal;
    }
    int val;
    LinkNode *next=NULL;
};

class MyLinkedList {
public:// 实现一个单链表
    MyLinkedList() {
    dummyHead= new LinkNode(0);
    size=0; // 获取链表长度
    }
    int get(int index) {
        if(index<0||index>(this->size-1)){
            return -1;
        }
        LinkNode * cur = dummyHead->next;
        while(index--){
            cur = cur->next;
        }
        return cur->val;

    }
    
    void addAtHead(int val) {
        LinkNode * insertNode = new LinkNode(val);
        insertNode->next = dummyHead->next;
        dummyHead->next =insertNode;
        size++;

    }
    
    void addAtTail(int val) {
        // 找到尾部
        LinkNode * cur = dummyHead;
        while(cur!=NULL&&cur->next!=NULL){
            cur = cur->next;
        }
        // 插入
        LinkNode * insertNode = new LinkNode(val);
        cur->next = insertNode;
        insertNode->next = NULL;
        size++;

    }
    
    void addAtIndex(int index, int val) {
        if(index>=this->size+1){
            showLink();
            return;
        }
        else if(index == this->size){
            addAtTail(val);

            return;
        }
        else if(index<=0){
            addAtHead(val);

            return;
        }
        LinkNode * cur = dummyHead;
        while(index--){
            cur = cur->next;
        }
        // 添加
        LinkNode * insertNode = new LinkNode(val);
        insertNode->next = cur->next;
        cur->next = insertNode;
        size++;

    }
    
    void deleteAtIndex(int index) {
        if(index<0||index>(this->size-1)){
            return ;
        }
        LinkNode * cur = dummyHead;
        while(index--){
            cur = cur->next;
        }
        // 删除
        LinkNode * temp = cur->next;
        cur->next = cur->next->next;
        delete temp;
        temp = NULL;
        size--;
    }
    LinkNode * dummyHead= new LinkNode(0);
    int size=0; // 获取链表长度
    void showLink(){
        LinkNode * cur = dummyHead;
        while(cur!=NULL&&cur->next!=NULL){
            cout<<cur->next->val<<" ";
            cur = cur->next;
        }
        cout<<endl;
    }
};

// 6,7,2,0,4,
// 4

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

三、翻转链表(力扣206)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
在这里插入图片描述

双指针,注意先移动pre再移动cur。

/**
 * 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) {
        if(head==NULL||head->next==NULL){
            // 只有一个结点
            return head;
        }
        // 定义两个指针,一个指前面的,一指后面的
        ListNode *pre = NULL;
        ListNode *cur = head;
        while(cur!=NULL){
            // 用一个值去接收cur
            ListNode *temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }

};

在这里插入图片描述
另外一种通过递归的方式反转链表,等学到递归再补上

四、两两交换链表中的节点 (力扣24)

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
在这里插入图片描述

每两个交换一次,需要找到交换之前的节点。注意交换实的逻辑,对于不变量需要提前保存信息。

/**
 * 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* swapPairs(ListNode* head) {
        if(head==NULL||head->next==NULL){
            return head;
        }
        // 创建一个虚拟头结点
        ListNode * dummyNode = new ListNode(0);
        dummyNode->next =head;
        ListNode * cur = dummyNode;
        while(cur->next!=NULL&&cur->next->next!=NULL){
            ListNode * a = cur->next;
            ListNode * b= cur->next->next->next;
            cur->next = cur->next->next;
            cur->next->next = a;
            a->next = b;
            cur = cur->next->next;
        }
        return dummyNode->next;
    }
};

在这里插入图片描述

五、删除链表的倒数第 N 个结点(力扣19)

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
在这里插入图片描述

双指针的经典题目,快指针先走n个,快慢指针再同时走,当快指针下一个为NULL时,慢指针下一个即为要删除的元素

/**
 * 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* removeNthFromEnd(ListNode* head, int n) {
        if (head==NULL){
            return head;
        }

        ListNode * dummyNode = new ListNode(0);
        dummyNode->next = head;
        // 使用快慢指针
        ListNode* fast = dummyNode;
        ListNode* slow = dummyNode;
        while(n--){
            // 快指针先走n个
            fast = fast->next;
        }
        while(fast!=NULL&&fast->next!=NULL){
            fast = fast->next;
            slow = slow->next;
        }
        // 删除元素
        ListNode * temp = slow->next;
        slow->next = slow->next->next;
        delete temp; // 删除
        return dummyNode->next;
    }
};

在这里插入图片描述

六、链表相交(力扣面试题02.07链表相交)

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        // 实际上是找相等的结点,不是值相等,而是 是同一个结点
        // 将两个链表右对齐
        // 求两个链表的长度
        if(headA==NULL||headB==NULL){
            return NULL;
        }
        int lenA =1;
        int lenB =1;
        ListNode * curA = headA;
        ListNode * curB = headB;
        while(curA->next!=NULL){
            lenA++;
            curA = curA->next;
        }
        while(curB->next!=NULL){
            lenB++;
            curB = curB->next;
        }
        // 找最短的链表
        int minLen =min(lenA,lenB);
        // AB需要移动的长度
        int needA= lenA-minLen;
        int needB = lenB - minLen;
        // 右对齐两个节点
        while(needA--){
            headA = headA->next;
        }
        while(needB--){
            headB = headB->next;
        }
        // 比较之后的链表是否相同
        while(headA!=NULL||headB!=NULL){
            if(headA== headB){
                return headA;
            }
            else{
                headA = headA->next;
                headB = headB ->next;
            }
        }
        return NULL;
    }
};

七、环形链表Ⅱ(力扣142)

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // 用一个map记录访问过的地址,不断将地址放入map,如果某次map的长度没有发生变化,说明入环了
        map<ListNode *,ListNode *> addmap;
        if(head==NULL){
            return NULL;
        }
        // 创建虚拟头结点
        ListNode * dummyHead = new ListNode(0);
        dummyHead->next = head;
        // 遍历
        ListNode * cur = dummyHead;
        while(cur!=NULL){
            // 记录之前的size
            int firstSize = addmap.size();
            addmap.insert(make_pair(cur->next,cur->next));
            int endSize = addmap.size();
            if(endSize==firstSize){
                // 没有发生变化,说明已经入环
                return addmap[cur->next];
            }
            else{
                cur = cur->next;
            }
            
        }
        return NULL;

    }
};

在这里插入图片描述
使用map存储每个结点的next,如果map存完之后长度没有变,那么说明进入环了,map中存储的值即为要返回的索引值。
使用双指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode * first = head;
        ListNode * slow = head;
        while(first!=NULL&&first->next!=NULL){
            first = first->next->next;
            slow = slow->next;
            if(first==slow){
                // 此时相遇
                ListNode * index1 = first;
                ListNode * index2 = head;
                // 找入环口
                while(index1!=index2){
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return NULL;

    }
};

在这里插入图片描述

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

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

相关文章

LeetCode 834. 树中距离之和

简单换根DP 其实就是看好变化量&#xff0c;然后让父亲更新儿子就好了&#xff5e; 上图2当根节点的时候&#xff0c;ans[2] ans[0] -sz[2]n-sz[2]; class Solution { public:vector<int> sumOfDistancesInTree(int n, vector<vector<int>>& edges) {v…

OCP NVME SSD规范解读-8.SMART日志要求-2

SMART-7&#xff1a; 软错误ECC计数可能是记录了被第一级ECC&#xff08;比如LDPC Hard Decode&#xff09;成功纠正过的读取错误次数。这意味着数据恢复成功&#xff0c;但依然表明存储介质出现了某种程度上的可靠性下降。 LDPC码是一种基于稀疏矩阵的纠错码&#xff0c;它由…

鸿蒙会取代Android吗?听风就是雨

现在说取代还谈不上&#xff0c;毕竟这需要时间。安卓作为全球第一的手机操作系统&#xff0c;短时间内还无法取代。持平iOS甚至超过iOS有很大可能&#xff0c;最终会呈现“三足鼎立”有望超过安卓基数。 作为全新的鸿蒙操作系统&#xff0c;其现在已经是全栈自研底座。按照鸿…

【前端工程化】环境搭建 nodejs npm

文章目录 前端工程化是什么&#xff1f;前端工程化实现技术栈前端工程化环境搭建 &#xff1a;什么是Nodejs如何安装nodejsnpm 配置和使用npm 介绍npm 安装和配置npm 常用命令 总结 前端工程化是什么&#xff1f; 前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块…

模拟电路之运放

滞回比较器&#xff1a; 小幅度波动时候不受影响&#xff0c;除非超过一点范围 当输入信号慢慢增加到UT&#xff0c;就变成负电压 当输入信号慢慢减压到—UT&#xff0c;就变成正电压 电路反向接信号 正反馈&#xff0c;串联电阻接地 调整回差的方法 1.调整电阻的分压 2.…

python实现贪吃蛇小游戏(附源码)

文章目录 导入所需的模块坐标主游戏循环模块得分 贪吃蛇小游戏&#xff0c;那个曾经陪伴着00后和90后度过无数欢笑时光的熟悉身影&#xff0c;仿佛是一把打开时光之门的钥匙。它不仅是游戏世界的经典之一&#xff0c;更是我们童年岁月中不可或缺的一部分&#xff0c;一个承载回…

使用宝塔面板访问MySQL数据库

文章目录 前言一、安装访问工具二、查看数据库总结 前言 前面我们已经部署了前后端项目&#xff0c;但是却不能得到数据库的信息&#xff0c;看有谁再使用你的项目。例如员工、用户等等。本次博客进行讲解如何在宝塔面板里面访问MySQL数据库。 一、安装访问工具 1、打开软件商…

微信小程序(二十六)列表渲染基础核心

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.列表渲染基础写法 2.外部索引和自身索引 源码&#xff1a; index.wxml <view class"students"><view class"item"><text>序号</text><text>姓名</text…

C++-内存管理(1)

1. C/C内存分布 首先我们需要知道&#xff0c;在C中的内存分为5个区。 1. 栈 又叫堆栈 -- 非静态局部变量 / 函数参数 / 返回值等等&#xff0c;栈是向下增长的。 2. 内存映射段 是高效的 I/O 映射方式&#xff0c;用于装载一个共享的动态内存库。用户可使用系统接口 创建…

微调入门篇:大模型微调的理论学习

1、为什么大模型微调 之前在《大模型这块蛋糕,想吃吗》介绍了普通人如何搭上大模型这块列车, 其中有一个就是模型微调,这个也是未来很多IT公司需要发力的方向,以及在《垂直领域大模型的应用更亲民》中论述了为什么微调适合大家,以及微调有什么价值? 作为小程序猿在开始进行微…

C#,打印漂亮的贝尔三角形(Bell Triangle)的源程序

以贝尔数为基础&#xff0c;参考杨辉三角形&#xff0c;也可以生成贝尔三角形&#xff08;Bell triangle&#xff09;&#xff0c;也称为艾特肯阵列&#xff08;Aitkens Array&#xff09;&#xff0c;皮埃斯三角形&#xff08;Peirce Triangle&#xff09;。 贝尔三角形的构造…

常用抓包软件集合(Fiddler、Charles)

1. Fiddler 介绍&#xff1a;Fiddler是一个免费的HTTP和HTTPS调试工具&#xff0c;支持Windows平台。它可以捕获HTTP和HTTPS流量&#xff0c;并提供了丰富的调试和分析功能。优点&#xff1a;易于安装、易于使用、支持多种扩展、可以提高开发效率。缺点&#xff1a;只支持Wind…

Linux内核源码

记得看目录哦&#xff01; 1. 为什么要阅读Linux内核2. Linux0.01内核源码3. 阅读linux内核源码技巧4. linux升级内核5. linux的备份和恢复5.1 安装dump和restore5.2 使用dump完成备份5.3 使用restore完成恢复 1. 为什么要阅读Linux内核 2. Linux0.01内核源码 3. 阅读linux内核…

dvwa靶场xss储存型

xss储存型 xxs储存型lowmessage框插入恶意代码name栏插入恶意代码 medium绕过方法 high xxs储存型 攻击者事先将恶意代码上传或储存到漏洞服务器中&#xff0c;只要受害者浏览包含此恶意代码的页面就会执行恶意代码。产生层面:后端漏洞特征:持久性的、前端执行、储存在后端数据…

刨析数据结构(一)

&#x1f308;个人主页&#xff1a;小田爱学编程 &#x1f525; 系列专栏&#xff1a;数据结构————"带你无脑刨析" &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于数据结构的优质内容&#xff01;&#x1f3c6;&#x1f3c6; &#x1f600;欢迎…

【百度Apollo】轨迹绘制:探索路径规划和可视化技术的应用

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…

element -table,多行或列合并

需求&#xff1a;后端返回的表格数据&#xff0c;如果某列值一样&#xff0c;前端表格样式需要合并他们&#xff0c;需要合并的列的行数未知&#xff08;所以需要有数据后遍历后端数据对需要合并的属性进行计数&#xff09;即动态遍历表格合并 效果 - 重点方法&#xff1b;ta…

html+css+js 我的学校网页设计与制作实例(6个页面)

一、作品介绍 HTMLCSSJS网页设计与制作&#xff0c;我的学校网页设计与制作实例&#xff0c; 本实例适合于初学HTMLCSSJS的同学。该案例里面有divcss的样式布局设置&#xff0c;这个实例比较全面&#xff0c;有一级页、二级页、详情页、输入表单等&#xff0c;共6个页面。本文…

编写python脚本调用ordinals以及BRC20的接口

初始版本 #!/usr/bin/python3from flask import Flask, request, jsonify import subprocess import json import osapp Flask(__name__)ord_cmd ["/root/ord/target/release/ord", "--cookie-file/data/btcregtest/data/regtest/.cookie", "--rpc…

C#使用RabbitMQ-4_路由模式(直连交换机)

简介 RabbitMQ中的路由模式是一种根据Routing Key有条件地将消息筛选后发送给消费者的模式。在路由模式中&#xff0c;生产者向交换机发送消息时&#xff0c;会指定一个Routing Key。交换机接收生产者的消息后&#xff0c;根据消息的Routing Key将其路由到与Routing Key完全匹…