【代码随想录】Day34链表:力扣203,707,206,142,面试0207

news2024/11/24 13:02:36

目录

基本知识

概念、类型、存储方式:

定义

操作

性能分析

经典方法

虚拟头结点

思路

例题:力扣203

链表的基本操作

思路

例题:力扣707

反转链表

思路

例题:力扣206

删除倒数第N个结点

思路:

例题:力扣19

链表相交

思路:

例题:力扣面试题 02.07

环形链表

思路:

例题:力扣142

基本知识

概念、类型、存储方式:

1.通过指针串联起的线性结构,每个结点由数据域和指针域构成,最后一个结点的指针域指向null

2.双链表:两个指针域,指上一个结点和下一个结点,可以前查询和后查询

3.循环链表:链表首位相连,解决约瑟夫环问题

4.链表中的结点再内存中不是连续分布的,是散乱分布在内存中的某地址上

定义

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};
//也可以不定义构造函数,使用默认构造函数,但是这个构造函数不会初始化任何成员变量

//自己定义的构造函数
ListNode* head = new ListNode(5);

//默认构造函数
ListNode* head = new ListNode();
head->val = 5;

操作

1.删除结点

将B的next指向D就可以了

在C++中手动释放掉C结点,delete就行 

2.添加结点

B的next指向M,M的next指向C

性能分析

插入、删除查询使用场景
数组O(n)O(1)数据固定,频繁查询,较少增删
链表O(1)O(n)数据不固定,频繁增删,较少查询

经典方法

虚拟头结点

思路

设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除

例题:力扣203

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode*dummyhead=new ListNode(0);
        dummyhead->next=head; //虚拟头节点指向head结点
        ListNode*cur=dummyhead; //当前结点是dummyhead
        while(cur->next!=NULL){ //如果下一位不是空--没有到末尾 还在循环中判断
            if(cur->next->val==val){
                ListNode*temp=cur->next; //定义临时变量 方便回收节点
                cur->next=cur->next->next; //当前节点的next指向删除节点的下一位
                delete temp; //手动回首内存空间
            }
            else{
                cur=cur->next; //指向下一位
            }
        }
        head=dummyhead->next;//头节点是dummyhead的下一个
        delete dummyhead;//回收节点
        return head;//返回头节点
    }
};

链表的基本操作

思路

对链表进行增删改查的基本操作

例题:力扣707

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

class MyLinkedList {

public:
    //定义链表结构体
    struct LinkedNode{
        int val;
        LinkedNode*next;
        LinkedNode(int val):val(val),next(nullptr){}
    };
    //初始化链表
    MyLinkedList() {
        dummyhead = new LinkedNode(0);//这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
        size=0;//链表大小置为0
    }
    
    int get(int index) {
        LinkedNode*cur=dummyhead->next;//一个辅助变量 指向dummyhead的下一位
        if(index<0||index>size-1){
            return -1;//非法返回-1
        }
        //合法操作
        while(index--){
            cur=cur->next;
        }
        return cur->val;//返回cur的值
    }
    //在头位置添加
    void addAtHead(int val) {
        LinkedNode*newhead=new LinkedNode(val);
        newhead->next=dummyhead->next;//插入的结点为头节点
        dummyhead->next=newhead;
        size++;//数量要增加
    }
   //在尾巴插入 
    void addAtTail(int val) {
        LinkedNode*newtail=new LinkedNode(val);
        LinkedNode*cur=dummyhead;//辅助变量
        while(cur->next!=nullptr){
            cur=cur->next;
        }
        cur->next=newtail;
        size++;//数量要增加
    }
    //按照序列插入
    void addAtIndex(int index, int val) {
        if(index>size||index<0)
            return;
        LinkedNode*indexnode=new LinkedNode(val);
        LinkedNode*cur=dummyhead;
        while(index--){
            cur=cur->next;
        }
        indexnode->next=cur->next;
        cur->next=indexnode;
        size++;//数量增加
    }
    //删除结点
    void deleteAtIndex(int index) { //想不通cur是指向dummyhead还是指向dummyhead->next的时候 带入0试一下
        if (index >= size || index < 0) {
            return;
        }
        LinkedNode* cur = dummyhead;
        while(index--) {
            cur = cur ->next;
        }
        LinkedNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        size--;
    }
    //打印链表
    void printLinkedlist(){
        LinkedNode*cur=dummyhead;
        while(cur->next!=nullptr){
            cout<<cur->next->val<<" ";
            cur=cur->next;
        }
        cout<<endl;
    }
    private:
    int size;
    LinkedNode*dummyhead;
};

反转链表

思路

1.如果定义一个新的链表,实现链表元素的反转,是对内存空间的浪费

2.只需要改变链表的next指针的指向,直接将链表反转,不用重新定义一个新的链表

①首先定义一个cur指针,指向头结点,定义pre指针,初始化为null

②temp保存cur->next结点,改变cur->next的指向为pre,继续移动cur和pre

 ③cur指针指向null,循环结束,链表反转完毕,返回return pre即可,pre指向了新的头结点。

例题:力扣206

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

//如果再定义一个新的链表,实现链表元素的反转,其实这是对内存空间的浪费。
//只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表
class Solution {
public:
    //递归法
    ListNode* reverse(ListNode*cur,ListNode*pre){
        if(cur==NULL) return pre;
        ListNode*temp=cur->next;
        cur->next=pre;
        return reverse(temp,cur);
    }
    ListNode* reverseList(ListNode* head) {
        //双指针写法
       /* ListNode*pre=NULL;//头的前一位 也是cur->next新的指向位置
        ListNode*cur=head;//从头开始
        ListNode*temp;//临时遍历 保存cur->next的值 防止丢失
        while(cur){
            temp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;*/
        return reverse(head,NULL);
    }
};

删除倒数第N个结点

思路:

双指针的经典应用——如果要删除倒数第n个结点,让fast移动n步,然后让fast和slow同时移动,知道fast指向链表末尾。删掉slow所指向的结点就可以了。

①设置虚拟头结点dummyhead

②定义fast和slow指针,初始值为虚拟头结点

③fast首先走n+1步,slow和fast同时移动,直到fast指向末尾。

④删除slow所指向的下一个结点

例题:力扣19

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode*dummyhead=new ListNode(0);
        ListNode*fast;
        ListNode*slow;
        dummyhead->next=head;
        fast=dummyhead;
        slow=dummyhead;
        //让fast指针走n+1步 找到快指针的位置
        n=n+1;
        while(n--&&fast!=NULL){
            fast=fast->next;//挪动指针
        }
        //让slow和fast同时出发,快指针指向null,慢指针所指的位置就是倒数第n个结点
        while(fast!=NULL){
            fast=fast->next;
            slow=slow->next;
        }
        slow->next=slow->next->next;//执行删除操作

        return dummyhead->next; //返回头结点了
    }
};

链表相交

思路:

求两个链表交点节点的指针。**交点不是元素数值相等,而是指针相等

①curA指向链表A的头结点,curB指向链表B的头结点

②分别求出两个链表的长度,求出两个链表长度的差值,curA移动到和curB末尾对齐的位置

③比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA==curB,则找到交点;否则循环退出返回空指针。

例题:力扣面试题 02.07

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode*curA=headA;
        ListNode*curB=headB;
        int lenA=0;
        int lenB=0;
        //分别求A和B的长度
        while(curA!=NULL){
            lenA++; //A的长度
            curA=curA->next;
        }
        while(curB!=NULL){
            lenB++; //B的长度
            curB=curB->next;
        }
        //让他们分别为表头,这样才能从头开始移动
        curA=headA;
        curB=headB;
         // 让curA为最长链表的头,lenA为其长度
        if(lenB>lenA){
            swap(lenA,lenB);
            swap(curA,curB);
        }
        //挪动A链表的curA
        int gapp=lenA-lenB;
        while(gapp--&&curA!=NULL){
            curA=curA->next;
        }
        //遍历curA 和 curB,遇到相同则直接返回
        while(curA!=NULL){
            if(curA==curB){
                return curA;
            }
            curA=curA->next;
            curB=curB->next;
        }
        return NULL;

    }
};

环形链表

思路:

1.判断链表是否有环:快慢指针法

分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

2.如果有环,如何找到环的入口

假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。

①从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

②在相遇节点处,定义一个指针index1,在头结点处定一个指针index2

③让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

例题:力扣142

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

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

不允许修改 链表。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode*fast=head;//从头开始走
        ListNode*slow=head;//从头开始
        //先走index1,让index1找到
        while(fast!=NULL&&fast->next!=NULL){
            fast=fast->next->next; //快指针走2步
            slow=slow->next;//慢指针走1步
            //--找环
            if(slow==fast){//快&慢指针相遇了
                ListNode*index1=fast;//index1指向快慢指针相遇的位置,从相遇位置开始出发
                ListNode*index2=head;//index2指向头位置,从头开始出发
                //--找环的入口
                while(index1!=index2){//还没碰见呢--循环
                    index1=index1->next;//移动
                    index2=index2->next;
                }
                return index1;//返回刚碰见
            }
        }
        return NULL;//没有环 返回空
    }
};

链表是数据结构中我最不熟练认为最难的一部分。。。这次从头到尾捋了一遍,感觉更清晰了,但是还需要再练习一遍。。

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

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

相关文章

jsp+ssm计算机毕业设计-东湖社区志愿者管理平台【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

[附源码]计算机毕业设计Python的在线作业批改系统(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

tomcat 服务突然停止、日志排查以及解决方案

文章目录一、服务停止调研1. jvm排查2. 日志排查3. 推测与ssh会话有关二、ssh会话强制退出验证2.1. 手动强制关闭进程12.2. 手动强制关闭进程22.3. 总结归纳与解决方案一、服务停止调研 1. jvm排查 有可能是jvm配置参数导致的&#xff0c;然后在/var/log和/app/apache-tomcat…

2022 软件测试大题【太原理工大学】

大题应该是有两道&#xff0c;每道10分&#xff0c;具体是不是我也不知道&#xff0c;老师也不确定。① 白盒测试 —— 控制流图&#xff0c;给出一段代码&#xff0c;画出控制流图&#xff0c;根据公式求程序段的环形复杂度&#xff0c;求程序基本路径集合中的独立路径&#x…

永磁同步电机(PMSM)磁场定向控制(FOC)电流环PI调节器参数整定

文章目录前言一、调节器的工程设计方法二、电流环PI调节器的参数整定2.1.电流环的结构框图2.2.典型I型系统2.3.电流环PI参数整定计算公式三、电流环PI调节器设计实例3.1.永磁同步电机磁场定向的电流闭环控制3.2.电流环PI参数计算3.3.仿真分析总结前言 本章节采用工程设计的方法…

CommaFeed:仿Google Readerd的RSS阅读器

最近老苏身边中招的人也开始多起来了&#xff0c;大家要保重~ 本文开始于 9 月下旬&#xff0c;完成于 10 月下旬&#xff0c;目前正式版本还是老苏打包时用的 2.6.0&#xff0c;不过现在已经有了 3.0.0 RC1 什么是 CommaFeed &#xff1f; CommaFeed 是受 Google Reader 启发而…

CS144-Lab0解析

讲在开头 cs144建议我们使用Modern C来完成所有的lab&#xff0c;关于modern c的全面的用法可以在(http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines)获取。 以下是一些代码规范&#xff1a; 不要使用malloc()和free()不要使用new和delete在不得不使用指针时应…

如何自动估算项目开发成本及报价,提高估算效率?

项目估算需要有科学专业的估算方法&#xff0c;需要有明确的量化指标&#xff0c;那么如何自动估算项目开发成本及报价&#xff1f; 第一步&#xff1a;功能点复杂程度的估算 CoCode需求分析工具&#xff0c;根据用户需求&#xff0c;使用COSMIC和IFPUG项目规模估算法&#xff…

数据结构C语言版 —— 队列+循环队列实现

文章目录队列1.概念2. 生活中队列应用3. 队列的实现初始化队列入队列出队列获取队头元素获取队尾元素获取队列中元素个数判断队列是否为空销毁队列2. 循环队列队列 1.概念 和栈相反&#xff0c;队列(queue)是一种先进先出的线性表&#xff0c;它只允许在一端进行插入&#xf…

C#-winform调用COM组件(COM组件由Qt开发)

一、场景介绍 在项目开发中,需要Qt与C#进行混合编程,完成项目开发。C#这边作为主框架,Qt负责编写插件,将功能模块通过COM组件的形式封装注册,再由C#调用、交互完成最终的项目。 程序开发环境: win10 64位 编译器: VS2017 Qt版本: Qt5.12.6 二、Qt封装COM组件 2.1 环境…

android flutter 安装

下载 flutter官网下载安装&#xff1a;https://flutter.dev/docs/development/tools/sdk/releases 将下载下来的zip安装包解压到想安装Flutter SDK的路径。注意&#xff0c;不要将flutter安装到需要一些高权限的路径&#xff0c;比如C:\Program Files\ 配置环境变量 添加fl…

案例教学 | 如何确定ADAMS简化模型的准确性,以及简化模型精度不够怎么办?

仿真建模过程中不可避免地对各种复杂元素进行简化处理。这种建模思路的终极目标是不牺牲仿真精度、还提升仿真效率。在Adams仿真建模过程中也有一些常见的简化方式&#xff0c;如非线性元素按线性建模、不考虑摩擦力、通过耦合约束等效传动关系等等。应用简化建模之前&#xff…

蓝桥杯有必要参赛吗?

昨天和群里的小伙伴在群里聊&#xff0c;有的小伙伴竟然说蓝桥杯一等奖没有含量&#xff0c;我也是醉了&#xff01; 就像去年看了一个号主写的&#xff1a;研究生遍地都是! 放眼全国14亿人口&#xff0c;别说研究生了&#xff0c;本科生占比有多少? “蓝桥杯是我人生中得到…

多态性:中的向下转型,instanceof 操作符的使用

多态性&#xff1a;中的向下转型&#xff0c;instanceof 操作符的使用 每博一文案 都说树叶不是一天变黄的&#xff0c;人心也不是一天变凉的&#xff0c;每一个现在的自己&#xff0c;其实都是过去的自己拼凑的。 如今我们的气质里都藏着过去走过的路&#xff0c;看过的书和爱…

混合模式和预乘原理的理解

首先说到混合模式&#xff0c;简单理解&#xff0c;混合模式就是同一像素上有两个颜色需要混合成一个使用的模式。 这里的两个像素点&#xff0c;我们把原先已经存在的&#xff0c;也就是下面的像素点颜色定义为目标颜色。把新加上来的&#xff0c;也就是上面的像素点颜色定义为…

【Selenium IDE录制脚本】三分钟教会你安装Selenium IDE的安装及使用

目录 1、安装Selenium IDE 1.1、安装Firefox浏览器 1.2、安装selenium IDE 2、selenium的脚本录制 1、安装Selenium IDE 1.1、安装Firefox浏览器 因为selenium的不同版本对Firefox的支持不同&#xff0c;所以我们安装了Firefox之后&#xff0c;需要关闭他的自动更新 搜索&…

Python-Tinydb数据库详解

目录 数据库 Tinydb Tinydb 使用 安装 导入 创建数据库 创建 table 增 删 查 改 其他函数 示例 最后 数据库 数据库就是存储数据的的地方&#xff0c;现在我们生活中几乎每时每刻做的事可能都有它的作用。今天来介绍 Tinydb 数据库&#xff0c;它适合初学者&am…

设计模式概述之建造者模式(五)

常说的设计模式是23种设计模式&#xff0c;分为3大类&#xff1a; 创建型模式5种&#xff1a;工厂方法、抽象工厂、单例、建造者、原型 结构型模式7种&#xff1a;适配器、代理、桥接、装饰者、外观、享元、组合 行为型模式11种&#xff1a;模板方法、解释器、策略、观察者、…

【如意如意顺遂我意快快显灵】

文章目录 ● 【猿如意安装】 &#xff08;基于Windows环境安装&#xff09; ● 【猿如意首页】 ● 【猿如意效率工具】 ● 【猿如意开发工具】 ● 【猿如意教程文档】 ● 【猿如意一行代码】 ● 【猿如意ChatGPT】 ● 【Markdown笔记】 猿如意官网&#xff1a;猿…

【码极客精讲】二叉树

二叉树&#xff08;Binary tree&#xff09;是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式&#xff0c;即使是一般的树也能简单地转换为二叉树&#xff0c;而且二叉树的存储结构及其算法都较为简单&#xff0c;因此二叉树显得特别重要。二叉树特点…