代码随想录算法训练营第三天 | 链表基础系列1-- 链表理论基础-移除链表元素-设计链表-反转链表(203、707、206)

news2024/11/24 12:47:23

链表基础系列1

  • 链表基础
  • 移除链表元素
  • 203 移除链表元素
    • 代码随想录的代码
  • 707 设计链表
    • 我的代码(错误太多,一致debug,没有用虚拟头,不想写了,是未通过的代码)
    • 代码随想录的代码
    • 小记:双链表好复杂,要仔细看。
  • 206 反转链表
    • 代码随想录的思路解答
    • 没什么思路,直接看的答案
    • 代码随想录的代码
    • 感悟
  • 参考

链表基础

1、链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。链表的入口节点称为链表的头结点也就是head。

2、链表类型:单链表,双链表,循环链表。

双链表:单链表中的指针域只能指向节点的下一个节点。双链表的每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。双链表既可以向前查询也可以向后查询。

循环链表:链表首尾相连。循环链表可以用来解决约瑟夫环问题。

3、链表的存储方式:数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。链表是通过指针域的指针链接在内存中各个节点。所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

4、链表的定义:链表节点的定义,很多同学在面试的时候都写不好。这是因为平时在刷leetcode的时候,链表的节点都默认定义好了,直接用就行了,所以同学们都没有注意到链表的节点是如何定义的。而在面试的时候,一旦要自己手写链表,就写的错漏百出。

5、链表与数组的对比。
在这里插入图片描述
6、Python自定义链表:

class ListNode:
    def __init__(self, val, next=None):
        self.val = val
        self.next = next

7、Java和Python语言都有自己的自动回收内存机制,所以在对链表进行操作时,不需要手动释放内存。

移除链表元素

移除操作要注意的就是删除头结点的情况。要单独写一段逻辑来处理移除头结点的情况。

这里就涉及如下链表操作的两种方式:
1、直接使用原来的链表来进行删除操作。
2、设置一个虚拟头结点在进行删除操作。

1:移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。

2:可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。来看看如何设置一个虚拟头。依然还是在这个链表中,移除元素1。最后在题目中,return 头结点的时候,别忘了 return dummyNode->next;, 这才是新的头结点

203 移除链表元素

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

示例1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例2:
输入:head = [], val = 1
输出:[]

示例3:
输入:head = [7,7,7,7], val = 7
输出:[]

小记:移除元素的逻辑是很简单的,但是这是我第一次在Python中用到链表,相关的语句语法不是很熟悉,第一题就直接看代码随想录给出的代码了。

代码随想录的代码

# Definition for singly-linked list.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        dummyHead = ListNode(0, head)
        cur = dummyHead
        while cur.next != None:
            if cur.next.val == val:
                cur.next = cur.next.next
            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

我的代码(错误太多,一致debug,没有用虚拟头,不想写了,是未通过的代码)

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class MyLinkedList:

    def __init__(self):
        self.head = ListNode(None,None)

    def get(self, index: int) -> int:
        cur = self.head
        count = 0
        if index == 0:
            return cur.val
        else:
            while cur.next != None:
                if count == index :
                    return cur.val
                cur = cur.next
                count = count + 1
            if count == index :
                return cur.val
            return -1


    def addAtHead(self, val: int) -> None:
        if self.head.val == None:
            self.head = ListNode(val,None)
        else :
            add_head = ListNode(val,self.head)
            self.head = add_head


    def addAtTail(self, val: int) -> None:
        if self.head.val == None:
            self.head = ListNode(val,None)
        else :
            cur = self.head
            while cur.next != None:
                cur = cur.next
            add_tail = ListNode(val,None)
            cur.next = add_tail

    def addAtIndex(self, index: int, val: int) -> None:
        add_n = ListNode(val,None)
        cur = self.head
        if index == 0:
            add_n.next = self.head
            self.head = add_n
            return 
        count = 0
        while cur.next != None:
            if count == index-1 :
                temp = cur.next
                cur.next = add_n
                add_n.next = temp
            cur = cur.next
            count = count + 1
        if count == index-1:
            cur.next = add_n

    def deleteAtIndex(self, index: int) -> None:
        cur = self.head
        count = 0
        while cur.next != None:
            if index == 0 :
                self.head = self.head.next
                break
            elif count == index-1 :    
                cur.next = cur.next.next
                break
            cur = cur.next
            count = count + 1

代码随想录的代码

(版本一)单链表法
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
        
class MyLinkedList:
    def __init__(self):
        self.dummy_head = ListNode()
        self.size = 0

    def get(self, index: int) -> int:
        if index < 0 or index >= self.size:
            return -1
        
        current = self.dummy_head.next
        for i in range(index):
            current = current.next
            
        return current.val

    def addAtHead(self, val: int) -> None:
        self.dummy_head.next = ListNode(val, self.dummy_head.next)
        self.size += 1

    def addAtTail(self, val: int) -> None:
        current = self.dummy_head
        while current.next:
            current = current.next
        current.next = ListNode(val)
        self.size += 1

    def addAtIndex(self, index: int, val: int) -> None:
        if index < 0 or index > self.size:
            return
        
        current = self.dummy_head
        for i in range(index):
            current = current.next
        current.next = ListNode(val, current.next)
        self.size += 1

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size:
            return
        
        current = self.dummy_head
        for i in range(index):
            current = current.next
        current.next = current.next.next
        self.size -= 1


# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
(版本二)双链表法
class ListNode:
    def __init__(self, val=0, prev=None, next=None):
        self.val = val
        self.prev = prev
        self.next = next

class MyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0

    def get(self, index: int) -> int:
        if index < 0 or index >= self.size:
            return -1
        
        if index < self.size // 2:
            current = self.head
            for i in range(index):
                current = current.next
        else:
            current = self.tail
            for i in range(self.size - index - 1):
                current = current.prev
                
        return current.val

    def addAtHead(self, val: int) -> None:
        new_node = ListNode(val, None, self.head)
        if self.head:
            self.head.prev = new_node
        else:
            self.tail = new_node
        self.head = new_node
        self.size += 1

    def addAtTail(self, val: int) -> None:
        new_node = ListNode(val, self.tail, None)
        if self.tail:
            self.tail.next = new_node
        else:
            self.head = new_node
        self.tail = new_node
        self.size += 1

    def addAtIndex(self, index: int, val: int) -> None:
        if index < 0 or index > self.size:
            return
        
        if index == 0:
            self.addAtHead(val)
        elif index == self.size:
            self.addAtTail(val)
        else:
            if index < self.size // 2:
                current = self.head
                for i in range(index - 1):
                    current = current.next
            else:
                current = self.tail
                for i in range(self.size - index):
                    current = current.prev
            new_node = ListNode(val, current, current.next)
            current.next.prev = new_node
            current.next = new_node
            self.size += 1

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size:
            return
        
        if index == 0:
            self.head = self.head.next
            if self.head:
                self.head.prev = None
            else:
                self.tail = None
        elif index == self.size - 1:
            self.tail = self.tail.prev
            if self.tail:
                self.tail.next = None
            else:
                self.head = None
        else:
            if index < self.size // 2:
                current = self.head
                for i in range(index):
                    current = current.next
            else:
                current = self.tail
                for i in range(self.size - index - 1):
                    current = current.prev
            current.prev.next = current.next
            current.next.prev = current.prev
        self.size -= 1



# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)

小记:双链表好复杂,要仔细看。

206 反转链表

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

代码随想录的思路解答

1、双指针法:首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。

然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。

为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。

接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。

最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。

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

2、递归法
代码思路和双指针法一致,只不过写法变了。
时间复杂度: O(n), 要递归处理链表的每个节点
空间复杂度: O(n), 递归调用了 n 层栈空间

没什么思路,直接看的答案

代码随想录的代码

(版本一)双指针法
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        cur = head   
        pre = None
        while cur:
            temp = cur.next # 保存一下 cur的下一个节点,因为接下来要改变cur->next
            cur.next = pre #反转
            #更新pre、cur指针
            pre = cur
            cur = temp
        return pre
(版本二)递归法
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        return self.reverse(head, None)
    def reverse(self, cur: ListNode, pre: ListNode) -> ListNode:
        if cur == None:
            return pre
        temp = cur.next
        cur.next = pre
        return self.reverse(temp, cur)

感悟

此题,反转链表,没有思路的原因:为什么要一直盯着链表的尾结点去看呢?总想着要先把尾结点放在头结点前,这样就涉及到O(n^2)的复杂度了!所以一直没有思路!

直接从前向后,反转next不就好了吗,最后只要return最后的结点就好了!不要被从左向右的主观方向迷惑了!

参考

以上部分内容来自:力扣官网,代码随想录

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

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

相关文章

【狂神】MySQL - Delete 和 Truncate 的区别

1. DELETE 命令 语法 &#xff1a; delete from 表名 [where 条件] -- 删除数据 (避免这样写, 会全部删除) DELETE FROM student;-- 删除指定数据 DELETE FROM student WHERE id 1; 2. TRUNCATE 命令 作用 : 完全清空一个数据库表, 表的结构和索引约束不会变. -- 清空 stu…

【Linux】LVS+Keepalived高可用负载均衡群集

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 LVSKeepalived高可用负载均衡群集 一、Keepalived实现原理1.Keepalived案例分析2.Keepalived工具介绍3&#xff0c;Keepalived实现原理剖析4.Keepalived案例讲解5.Keepalived…

自媒体新手如何从零开始做自媒体?有哪些步骤流程?

自媒体已经成为了一种非常流行的个人创业方式&#xff0c;相比于传统的创业方式&#xff0c;自媒体的投入成本较低&#xff0c;且门槛较低。许多人都有一个梦想&#xff0c;希望成为一个自媒体人&#xff0c;成为自己的老板。但是&#xff0c;对于很多自媒体小白来说&#xff0…

基于Springboot+mybatis+mysql+vue实现企业注册模块功能

基于Springbootmybatismysqlvue实现企业注册模块功能 一、系统介绍二、功能展示1.主页面2.注册成功 三、数据库四、代码展示四、其他系统实现五、获取源码 一、系统介绍 该系统实现简单的企业信息注册&#xff0c;保存后&#xff0c;提示注册成功。 运行环境&#xff1a;idea…

IP 协议(网络层协议)

IP协议 IP 协议作用地址管理动态分配 IP 地址NAT 机制IPv6IP 地址的组成 路由选择 IP 协议作用 主要有两点 : 地址管理 为每个上网的设备分配一个唯一地址. 路由选择 两台主机间的信息交互, 具体走哪条线路. 地址管理 先来看看 IP协议 报文格式 : IP 协议最主要就是 32 位的…

2.9寸NFC卡片

应用广泛 无需电池 可挂、可横向/纵向摆放&#xff0c;适合多种场所 使用2.9寸电子纸墨水屏&#xff0c;持续显示不耗电 本产品无电池&#xff0c;节能环保&#xff0c;助力实现碳中和 ​ 基于电子纸墨水屏作为显示屏&#xff0c;符合当下节能环保、护眼的需求。质地轻薄、大…

Flameshot (火焰截图)截图无法插入汉字

前不久&#xff0c;Debian11升级至Debian12后&#xff0c;发现fcitx5无法用了&#xff0c;好似包也被删除了。于是重新安装了fcitx5,但发现了一个问题&#xff0c;利用Flameshot&#xff08;火焰截图&#xff09;截取图片时&#xff0c;无法对图片进行文字注释。如下图所示&…

HTML常用标签

1、HTML HTML Hyper Text Markup Language 超文本标记语言 Markup Language 标记语言 XML Extensible Markup Language 可扩展标记语言 HTML2HTML3HTML4XHTML1XHTML2HTML5 2 、HTML基本结构 3 、网页 header header 文档的开始部分 网页加载时&#xff0c;首先加载header…

Spring Cloud Config: 了解、原理和使用

Spring Cloud Config: 了解、原理和使用 Spring Cloud Config 是 Spring Cloud 生态系统中的一个重要组件&#xff0c;它提供了一种分布式配置管理的解决方案&#xff0c;能够集中管理应用程序的配置&#xff0c;支持多种后端存储&#xff0c;如 Git、SVN、本地文件系统、Vaul…

ADG环境下统计每天的归档

现场项目经理反馈&#xff0c;使用日常的归档查询sql看到每天的归档量都快2T了&#xff0c;截图出来确实 很大 查看每天的归档文件总量比当天的归档量少了一半左右&#xff0c;百度了很多案例&#xff0c;最后发现问题该环境是ADG一主一备&#xff0c;每天的归档量也传输到备库…

JavaScript(JS)的引入方法

内部脚本 JS代码必须位于<script></script>标签之间在HTML文档中&#xff0c;可以在任意地方&#xff0c;放置任意数量的<script>一般会把脚本置于<body>元素的底部&#xff0c;可以改善显示速度 外部脚本&#xff1a;将JS代码定义在外部JS文件中&…

0基础学习VR全景平台篇 第55篇:专业版功能-数据统计

使用蛙色VR平台数据统计功能&#xff0c;可以统计分析整个账号下【所有作品】的访问数据&#xff1b; 亦可分析单个作品中【每个场景】的访问数据。 账号数据统计功能位置 单作品数据统计功能位置 一、本功能将用在哪里&#xff1f; 数据统计功能&#xff0c;可实现对作品总访…

自发二元行为预测人际神经同步(INS)的出现

导读 人际神经同步(INS)正在成为预测多人协调、沟通和合作成功等社会互动的有力标志。由于对INS的起源知之甚少&#xff0c;本研究测试了INS是否以及如何从自发的二元行为中产生。要求一对参与者在不说话或做出共同语言手势的情况下互相看着对方&#xff0c;并记录他们的神经活…

网络分层模型以及通信流程

2.1OSI模型和tcp/ip模型 Tcp/ip模型早于ISO的OSI模型 2.2网络为什么要分层&#xff1f; 将一个大的问题进行拆分&#xff0c;分而治之&#xff0c;专门的层处理专门的事情。而且那层出现问题只需对该层进行处理&#xff0c;不会影响到其他层。就相当于做菜的过程&#xff0c;…

满足数字化转型对无线网络性能需求,锐捷全场景 Wi-Fi 7 方案

数字化转型深入了各行业&#xff0c;对于算力、数据、网络的需求也水涨船高。其中&#xff0c;无线网络对于生产办公等等场景数据传输的保障&#xff0c;愈加重要。 例如生产场景里&#xff0c;工厂增设各类自动化、智能化的传感器&#xff0c;都需要以无线&#xff08;甚至全部…

我的《CSDN铁粉宝典》

完成一篇如何获得铁粉&#xff0c;或者相关的文章且质量分达到80分以上即可 一 什么是铁粉&#xff1f; 顾名思义&#xff0c;就是你的铁杆粉丝&#xff0c;但是这个只是过通俗的解释&#xff0c;那么在CSDN规则中&#xff0c;什么是铁粉呢&#xff1f;官方给了一系列解释 “为…

新发布的 DBeaver 23.1.1 版本正式支持时序数据库 TDengine

众所周知&#xff0c;DBeaver 是一个流行的开源数据库管理和 SQL 客户端工具&#xff0c;为管理和使用各种类型的数据库&#xff08;包括多个时序数据库&#xff09;提供强大而灵活的平台。为了让大家在应用上更加便捷&#xff0c;我们与 DBeaver 达成合作&#xff0c;新发布的…

蓝桥杯专题-试题版含答案-【字母统计】【计算球体积】【16进制的简单运算】【C小加随机数】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

子元素比父元素 z-index高低的问题

一.大坑---设置父relative z-index:0 .parent {position: relative;z-index:0; } .child {position: absolute;z-index:9999; }子元素居然比父元素的兄弟元素低一个层级&#xff01; 原因&#xff1a; 当你将父元素的 position 属性设置为 relative 并且 z-index 属性设置为…

美团落子泛娱乐

配图来自Canva可画 据工信部发布的《泛娱乐产业白皮书》显示&#xff0c;中国泛娱乐核心产业&#xff0c;已成为数字经济的重要支柱和新经济发展的重要引擎。因此&#xff0c;构建一个多产业联动的泛娱乐生态体系&#xff0c;便成为了企业发展的共同目标。比如&#xff0c;腾讯…