leetcode-链表

链表是一个用指针串联起来的线性结构,每个结点由数据域和指针域构成,指针域存放的是指向下一个节点的指针,最后一个节点指向NULL,第一个结点称为头节点head。

常见的链表有单链表、双向链表、循环链表。双向链表就是多了一个pre指针,头节点的pre指向NULL。循环链表就是尾节点的next指向了头节点,可以用来解决约瑟夫问题。

链表内存为节点间不连续,节点内连续。适用于解决数据长度不固定,不经常查找,经常增删的问题。

要学会自己定义struct ListNode,并且要知道构造函数自己写完怎么用(使用ListNode *node = new ListNode(3))这样就可以new一个ListNode出来让node指向了。

1.移除元素 Leetcode203. 分为虚拟节点和不使用虚拟节点

在这一题里我终于体会到了tmp=nullptr的用处。

代码随想录里明明只使用了delete tmp,但是我没有用tmp = nullptr 还是报了内存的错误,下面这样写才通过,但是看起来明明不对。

/**
 * 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) {
    // 2. 使用虚拟头节点
        while(head!=nullptr && head->val ==val)
        {
            ListNode* tmp = head;//内存~
            head = head->next;
            delete tmp;//内存~
        }

        ListNode* cur = head;
        while(cur != nullptr && cur->next != nullptr)
        {
            if(cur->next->val == val)
            {
                ListNode* tmp = cur->next;//~
                cur->next = cur->next->next;
                delete tmp;//~
            }
            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) {
    // 2. 使用虚拟头节点
        ListNode *dummynode = new ListNode();
        dummynode->next = head;
        ListNode *cur = dummynode;

        while(cur != nullptr && cur->next != nullptr)
        {
            if(cur->next->val == val)
            {
                ListNode* tmp = cur->next;//考虑一下内存,不考虑也没事,只是大一些
                cur->next = cur->next->next;
                delete tmp;//~同上
            }
            else
                cur = cur->next;
        }
        return dummynode->next;  
    }
};

这里要注意我的虚拟头节点也是一个指针类型的,因为我要使用到dummynode->next这种操作,仅仅是指向虚拟头结点的一个指针而已。我一开始用的是ListNode dummynode = new ListNode(),这是不对的。

[注意]通常情况下,在执行了 delete 操作之后,将指针置为 nullptr 是不必要的,因为你不应该在删除后继续使用已经释放的内存。这不是内存管理的原则之一。这里,delete之前要暂存一下删的东西地址,不然不知道删啥。

2.设计链表20230828

本题给了ListNode的节点定义,需要写MyLinkedList中的构造函数、get、addAtHead、addAtTail、addAtIndex、deleteAtIndex。

需要注意的是:第一,写错了什么东西--。我本来是index--,误写成tmp--,找了半天也没发现哪里错。

在这里插入图片描述

第二个写错的是这里:

在这里插入图片描述

这里写错了,其实index=0也可以删,就是把头节点删掉。这里错误导致我有三个用例不通过,都是delete头节点的。改成>=0就行了。

这一题熟悉了链表的操作,并且深刻体会了虚拟头节点的妙用。

3.反转链表LCR024 20230829

  • 双指针法,清晰易懂:
/**
 * 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 *cur = head;
        while(cur){
            ListNode *tmp = cur -> next; 
            cur -> next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
};

代码真是超简单,但是记得我们本来只有prev和current,是因为要存断裂开来的current才引入了temp。

并且,记得prev最终指向尾节点,cur最终指向尾节点后的nullptr,所以while的条件是cur!=nullptr。当等于空,直接可以返回,返回的头节点就是prev。

时间复杂度O(n),空间复杂度O(1)

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        return reverse(nullptr,head);
    }
    ListNode* reverse(ListNode *pre, ListNode *cur){
        if(cur == nullptr)
            return pre;
        else
        {
            ListNode *temp = cur->next; 
            cur -> next = pre;
            return reverse(cur, temp);
        }
    }
};

以上是递归写法,是双指针法的变体。

4. 两两交换链表中的节点Leetcode24. 20230901

在这里插入图片描述

/**
 * 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) {
        //定义虚拟头节点
        ListNode *dummynode = new ListNode();
        dummynode -> next = head;
        ListNode *cur = dummynode;
        while(cur -> next != nullptr && cur -> next -> next != nullptr)
        {
            ListNode *tmp = cur -> next;
            ListNode *tmp1 = cur -> next -> next -> next;
            cur -> next = cur -> next -> next;
            cur -> next -> next = tmp;
            tmp -> next = tmp1;
            cur = cur -> next -> next;
        }
        return dummynode -> next;
    }
};

这一题画图非常重要!

5. 删除链表的倒数第N个节点:删除某节点,指针需要跑到节点前面20230902

本题是快慢指针的经典使用!

/**
 * 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) {       
        ListNode *dummynode = new ListNode(); 
        dummynode->next = head;
        ListNode *fast = head;
        ListNode *slow = dummynode;

        while(n-- && fast != nullptr){
            fast = fast->next;
        }
        while(fast != nullptr){
            fast = fast->next;
            slow = slow->next;
        }
        ListNode *tmp = slow->next;
        slow->next = slow->next->next;
        delete tmp;
        return dummynode->next;
    }
};

时间复杂度:O(n),空间复杂度:O(1)

6. 链表相交 面试题 02.07.  重点在于尾部对齐思想和非值相等20230902

尾部对齐这个没啥好说的,跟上题快慢指针一样思想记住就行

非值相等……简单来说就是我做题的时候以为下面这个示例应该1的时候就是相交的,其实测试用例应该是真给了两个相交链表,导致值相等的时候不一定相交,反而是直接判断指针curA!= curB相等才对。

在这里插入图片描述

/**
 * 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) {
        // 尾部对齐
        int lengthA = 0, lengthB = 0;
        ListNode *curA = headA, *curB = headB;
        while (headA != nullptr){
            headA = headA->next;
            lengthA++;
        }
        while (headB != nullptr){
            headB = headB->next;
            lengthB++;
        }
        if(lengthA > lengthB){
            int tmp = lengthA - lengthB;
            while(tmp--){
                curA = curA->next;
            } 
        }
        else{
            int tmp = lengthB - lengthA;
            while(tmp--){
                curB = curB->next;
            } 
        }
        // 移动指针找交点
        while(curA != nullptr && curA!= curB)//本来写的是curA->val != curB->val
        {
            curA = curA->next;
            curB = curB->next;
        }
        return curA;
    }
};

7. 环形链表II Leetcode142. 20230903

本题主要是数学证明居多。

这一题是这两篇唯一让我觉得有些心虚的,因为这个数学证明我自己肯定不会去想出来,让我们来看一下本题的思路:

在这里插入图片描述

/**
 * 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 *fast = head;
        ListNode *slow = head;
        while(fast != nullptr  && fast->next !=nullptr){
            fast = fast->next ->next;
            slow = slow->next;
            if(fast == slow){
                ListNode *index1 = fast;
                ListNode *index2 = head;
                while(index1 != index2){
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return nullptr;
    }
};

首先是快慢双指针从头开始,快指针到空就肯定无环;然后快指针两倍速入环,慢指针跟着一倍速入,慢指针走不到一圈必被快指针追上(考虑最坏情形,慢指针刚入,快指针刚比他快一格;当慢指针走完1环,快指针肯定走完2环。所以,慢在走1环的中途肯定被追上。)

我们列出的数学式子是2(x+y)=x+y+n(y+z),注意这个2,就是因为快指针两格两格跨,又跟慢指针同时触发,所以是走了慢指针两倍距离。然后可以看到x=(n-1)(y+z)+y

那么如果我们要求入环点,就是求这个x。考虑被追上的这个情形下,正好有了y的尾部,那么走x与从y走最终就会在y头部相遇,这个从刚刚的式子可以几何理解,不论多走几圈,最终都是在y头部相遇,那么就得到了最后的点。[对于这一段真的是有点心虚,自己推不出来,笨笨again]

链表到此就结束了,说谢谢随想录。

8. 两数相加 leetcode2. 20231025

时隔一个月,在leetcode题单上看到这个第二题,是之前被我看过、提交过、放弃过的题,就在学会链表之后自己尝试了一下,调试了很多遍,好在最后通过了。但是,我并不知道这一题dummynode有什么用,也忘记了怎么写,并且忘记了如何优雅地循环新增node,所以只能抱着“节省空间”的想法,基于一个链表去进行求和,写出了如下丑陋冗余的代码:

/**
 * 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) {
        ListNode *node = l2;
        ListNode *result = l2;
        bool carry = 0;
        while(l1 != nullptr && l2 != nullptr){
            int temp = l1->val + l2->val + carry;
            carry = 0;
            l1 = l1->next;
            l2 = l2->next;
            if(temp > 9){
                carry = 1;
            }
            node->val = temp % 10;
            if(l2 != nullptr)
                node = node->next;
        }
        
        if(l1 == nullptr && l2 == nullptr && carry == 1){
            ListNode *tailnode = new ListNode();
            tailnode->val = 1;
            node->next = tailnode;             
        }
            
        while(l1 != nullptr){
            node -> next = l1;
            node = node->next;
            int temp = l1->val + carry;
            carry = 0;
            l1 = l1->next;
            if(temp > 9){
                carry = 1;
            }
            node->val = temp % 10;
            if(l1 == nullptr && carry == 1){
                ListNode *tailnode = new ListNode();
                tailnode->val = 1;
                node->next = tailnode;             
            }
        }

        while(l2 != nullptr){
            int temp = l2->val + carry;
            carry = 0;
            l2 = l2->next;
            if(temp > 9){
                carry = 1;
            }
            node->val = temp % 10;
            if(l2 == nullptr && carry == 1){
                ListNode *tailnode = new ListNode();
                tailnode->val = 1;
                node->next = tailnode;             
            }
            node = node->next;
        }

        return result; 
    }

};

后来,看到了官方题解。

官方题解中,使用了

int n1 = l1 ? l1->val: 0;

也就是说,l1=nullptr的时候,自动就是0值,可以直接用;

然后,又用了

if (!head) {
head = tail = new ListNode(sum % 10);
} else {
tail->next = new ListNode(sum % 10);
tail = tail->next;
}

意思是说,tail往后增长是直接用next去new一个,这样的话就可以实现我想实现的循环新增;

然后,如何让两个链表一起停止呢?只要对l1和l2分别判断是不是为空,如果为空就不next,反之继续next

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ITSource 分享 第5期【校园信息墙系统】

项目介绍 本期给大家介绍一个 校园信息墙 系统,可以发布信息,表白墙,分享墙,校园二手买卖,咨询分享等墙信息。整个项目还是比较系统的,分为服务端,管理后台,用户Web端,小…

ELASTICO-A Secure Sharding Protocol For Open Blockchains

INTRO 在中本聪共识中,通过POW机制来公平的选举leader,不仅非常消耗power,并且拓展性也不好。现在比特币中是7 TPS,和其他的支付系统相比效率相差甚远。 当前的许多拜占庭共识协议,并不支持在一个开放的环境中使用&a…

C语言实现输入一个字符串,递归将其逆序输出

完整代码&#xff1a; // 输入一个字符串&#xff0c;递归将其逆序输出。如输入 LIGHT&#xff0c;则输出 THGIL #include<stdio.h> #include<stdlib.h> //字符串的最大长度 #define N 20//逆序输出字符串 void func(char *str){if (*str\0){//结尾时直接退出递归…

Java SE 学习笔记(十七)—— 单元测试、反射

目录 1 单元测试1.1 单元测试概述1.2 单元测试快速入门1.3 JUnit 常用注解 2 反射2.1 反射概述2.2 获取类对象2.3 获取构造器对象2.4 获取成员变量对象2.5 获取常用方法对象2.6 反射的作用2.6.1 绕过编译阶段为集合添加数据2.6.2 通用框架的底层原理 1 单元测试 1.1 单元测试概…

基于单片机的太阳跟踪系统的设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、硬件电路设计2.1跟踪控制方案的选择2.1.1跟踪系统坐标系的选择2.2系统总体设计及相关硬件介绍…

服务熔断保护实践--Hystrix

概述 微服务有很多互相调用的服务&#xff0c;构成一系列的调用链路&#xff0c;如果调用链路中某个服务失效或者网络堵塞等问题&#xff0c;而有较多请求都需要调用有问题的服务时&#xff0c;这是就会造成多个服务的大面积失效&#xff0c;造成服务“雪崩”效应。 服务“雪…

十九、类型信息(2)

本章概要 Class 对象 类字面常量泛化的 Class 引用cast() 方法 Class 对象 要理解 RTTI 在 Java 中的工作原理&#xff0c;首先必须知道类型信息在运行时是如何表示的。这项工作是由称为 **Class**对象 的特殊对象完成的&#xff0c;它包含了与类有关的信息。实际上&#x…

JVM第二十三讲:Java动态调试技术原理

Java动态调试技术原理 本文是JVM第二十三讲&#xff0c;Java动态调试技术原理。转载自 美团技术团队胡健的Java 动态调试技术原理及实践&#xff0c;通过学习java agent方式进行动态调试&#xff0c;了解目前很多大厂开源的一些基于此的调试工具 (例如来自阿里开源的Arthas)。 …

微信小程序设计之主体文件app-wxss/less

一、新建一个项目 首先&#xff0c;下载微信小程序开发工具&#xff0c;具体下载方式可以参考文章《微信小程序开发者工具下载》。 然后&#xff0c;注册小程序账号&#xff0c;具体注册方法&#xff0c;可以参考文章《微信小程序个人账号申请和配置详细教程》。 在得到了测…

elementUI 特定分辨率(如1920*1080)下el-row未超出一行却换行

在1920*1080分辨率下&#xff0c; el-col 内容未超出 el-col 宽度&#xff0c;el-col 不足以占据一行&#xff0c;el-row 却自动换行了&#xff08;其他分辨率没有这个问题&#xff09;。 截图&#xff1a; 排查&#xff1a; el-col 内容没有溢出&#xff1b;没有多余的 pad…

拜耳阵列(Bayer Pattern)和解马赛克简介

拜尔阵列 典型的图像传感器&#xff08;例如我们在数码相机中使用的图像传感器&#xff0c;主要有CCD, CMOS&#xff09;由许多单独的光电传感器组成&#xff0c;所有这些传感器都会捕获光线。这些光电传感器本身能够捕获光的强度&#xff0c;但不能捕获其波长&#xff08;颜色…

CTF-Web(3)文件上传漏洞

笔记目录 CTF-Web(2)SQL注入CTF-Web(3)文件上传漏洞 1.WebShell介绍 (1)一句话木马定义 一种网页后门&#xff0c;以asp、php、jsp等网页文件形式存在的一种命令执行环境&#xff0c;而 一句话木马往往只有一行WebShell代码。 作用&#xff1a; 攻击获得网站控制权限 查看、修改…

如何防范AI等技术带来的诈骗风险?从技术、法律、教育等多方面入手

文章目录 前言什么是AI诈骗案例案例一案例二 AI诈骗的特点如何预防和应对AI诈骗建议后记 前言 互联网是一把双刃剑&#xff0c;这是我们常说的一个问题。 随着人工智能技术的快速发展&#xff0c;AI诈骗成为当今社会面临的新兴威胁。不法分子利用人工智能技术&#xff0c;以更…

Qt之实现支持多选的QCombobox

一.效果 1.点击下拉列表的复选框区域 2.点击下拉列表的非复选框区域 二.实现 QHCustomComboBox.h #ifndef QHCUSTOMCOMBOBOX_H #define QHCUSTOMCOMBOBOX_H#include <QLineEdit> #include <QListWidget> #include <QCheckBox> #include <QComboBox>…

面试算法43:在完全二叉树中添加节点

题目 在完全二叉树中&#xff0c;除最后一层之外其他层的节点都是满的&#xff08;第n层有2n-1个节点&#xff09;。最后一层的节点可能不满&#xff0c;该层所有的节点尽可能向左边靠拢。例如&#xff0c;图7.3中的4棵二叉树均为完全二叉树。实现数据结构CBTInserter有如下3种…

Vue 3 响应式对象:ref 和 reactive 的使用和区别

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是尘缘&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f449;点击这里&#xff0c;就可以查看我的主页啦&#xff01;&#x1f447;&#x…

Flink CDC 2.0 主要是借鉴 DBLog 算法

DBLog 算法原理 DBLog 这个算法的原理分成两个部分&#xff0c;第一部分是分 chunk&#xff0c;第二部分是读 chunk。分 chunk 就是把一张表分为多个 chunk&#xff08;桶/片&#xff09;。我可以把这些 chunk 分发给不同的并发的 task 去做。例如&#xff1a;有 reader1 和 re…

二叉树的最近公共祖先

题目&#xff1a; 样例&#xff1a; 输入 6 1 4 2 5 -1 -1 1 4 -1 -1 -1 -1 -1 3 输出 2 思路&#xff1a; 由题意&#xff0c;最近公共祖先就是&#xff0c;找出给出的两个结点的父结点 是谁。 这里有两种情况 1、给定的两个结点都是孩子结点 2、给定的两个结点&#xff…

【送书福利-第二十二期】《Vue.js 3企业级项目开发实战(微课视频版)》

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&#xff1a;程序员洲洲。 &#x1f388; 本文专栏&#xff1a;本文…

Table-GPT:让大语言模型理解表格数据

llm对文本指令非常有用&#xff0c;但是如果我们尝试向模型提供某种文本格式的表格数据和该表格上的问题&#xff0c;LLM更有可能产生不准确的响应。 在这篇文章中&#xff0c;我们将介绍微软发表的一篇研究论文&#xff0c;“Table-GPT: Table- tuning GPT for Diverse Table…