【代码随想录——链表】

news2025/1/13 11:37:35

1.链表

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

1.1 链表的类型

  • 单链表
    • 只有一个指向下一个节点的指针
    • 只能向后查询
  • 双链表
    • 每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。
    • 既可以向前查询也可以向后查询。
  • 循环链表 (就是链表首尾相连)
    • 可以用来解决约瑟夫环问题

1.2 链表的常见操作

  • 删除节点
    • 需要两个指针,一个指针p2指向要删除的节点,一个指针p1指向要删除的前一个节点。之后将p1.next指向p2.next
  • 添加节点
    • 需要先查找到要添加的位置p2,p1指向前一个节点。之后p1.next指向新节点,新节点.next指向p2即可。

在这里插入图片描述
在这里插入图片描述

2.移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回新的头节点 。
在这里插入图片描述
需要注意的是如果头结点的元素和要删除的元素相等。
这里我们通过引入空节点来避免这个状况。

func removeElements(head *ListNode, val int) *ListNode {
	emptyNode := &ListNode{}
	emptyNode.Next = head
	p1, p2 := emptyNode, head
	for p2 != nil {
		if p2.Val == val {
            //删除该节点
			p1.Next = p2.Next
		}else{
            p1 = p2
        }
		p2 = p2.Next
	}
	return emptyNode.Next
}

3. 设计链表

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

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

3.1 单向链表

//单链表实现
package main

import (
	"fmt"
)

type SingleNode struct {
	Val  int         // 节点的值
	Next *SingleNode // 下一个节点的指针
}

type MyLinkedList struct {
	dummyHead *SingleNode // 虚拟头节点
	Size      int         // 链表大小
}

func main() {
	list := Constructor()     // 初始化链表
	list.AddAtHead(100)       // 在头部添加元素
	list.AddAtTail(242)       // 在尾部添加元素
	list.AddAtTail(777)       // 在尾部添加元素
	list.AddAtIndex(1, 99999) // 在指定位置添加元素
	list.printLinkedList()    // 打印链表
}

/** Initialize your data structure here. */
func Constructor() MyLinkedList {
	newNode := &SingleNode{ // 创建新节点
		-999,
		nil,
	}
	return MyLinkedList{ // 返回链表
		dummyHead: newNode,
		Size:      0,
	}

}

/** Get the value of the index-th node in the linked list. If the index is
  invalid, return -1. */
func (this *MyLinkedList) Get(index int) int {
	/*if this != nil || index < 0 || index > this.Size {
		return -1
	}*/
	if this == nil || index < 0 || index >= this.Size { // 如果索引无效则返回-1
		return -1
	}
	// 让cur等于真正头节点
	cur := this.dummyHead.Next   // 设置当前节点为真实头节点
	for i := 0; i < index; i++ { // 遍历到索引所在的节点
		cur = cur.Next
	}
	return cur.Val // 返回节点值
}

/** Add a node of value val before the first element of the linked list. After
  the insertion, the new node will be the first node of the linked list. */
func (this *MyLinkedList) AddAtHead(val int) {
	// 以下两行代码可用一行代替
	// newNode := new(SingleNode)
	// newNode.Val = val
	newNode := &SingleNode{Val: val}   // 创建新节点
	newNode.Next = this.dummyHead.Next // 新节点指向当前头节点
	this.dummyHead.Next = newNode      // 新节点变为头节点
	this.Size++                        // 链表大小增加1
}

/** Append a node of value val to the last element of the linked list. */
func (this *MyLinkedList) AddAtTail(val int) {
	newNode := &SingleNode{Val: val} // 创建新节点
	cur := this.dummyHead            // 设置当前节点为虚拟头节点
	for cur.Next != nil {            // 遍历到最后一个节点
		cur = cur.Next
	}
	cur.Next = newNode // 在尾部添加新节点
	this.Size++        // 链表大小增加1
}

/** Add a node of value val before the index-th node in the linked list. If
  index equals to the length of linked list, the node will be appended to the
  end of linked list. If index is greater than the length, the node will not be
  inserted. */
func (this *MyLinkedList) AddAtIndex(index int, val int) {
	if index < 0 { // 如果索引小于0,设置为0
		index = 0
	} else if index > this.Size { // 如果索引大于链表长度,直接返回
		return
	}

	newNode := &SingleNode{Val: val} // 创建新节点
	cur := this.dummyHead            // 设置当前节点为虚拟头节点
	for i := 0; i < index; i++ {     // 遍历到指定索引的前一个节点
		cur = cur.Next
	}
	newNode.Next = cur.Next // 新节点指向原索引节点
	cur.Next = newNode      // 原索引的前一个节点指向新节点
	this.Size++             // 链表大小增加1
}

/** Delete the index-th node in the linked list, if the index is valid. */
func (this *MyLinkedList) DeleteAtIndex(index int) {
	if index < 0 || index >= this.Size { // 如果索引无效则直接返回
		return
	}
	cur := this.dummyHead        // 设置当前节点为虚拟头节点
	for i := 0; i < index; i++ { // 遍历到要删除节点的前一个节点
		cur = cur.Next
	}
	if cur.Next != nil {
		cur.Next = cur.Next.Next // 当前节点直接指向下下个节点,即删除了下一个节点
	}
	this.Size-- // 注意删除节点后应将链表大小减一
}

// 打印链表
func (list *MyLinkedList) printLinkedList() {
	cur := list.dummyHead // 设置当前节点为虚拟头节点
	for cur.Next != nil { // 遍历链表
		fmt.Println(cur.Next.Val) // 打印节点值
		cur = cur.Next            // 切换到下一个节点
	}
}

3.2 双向链表

//循环双链表
type MyLinkedList struct {
	dummy *Node
}

type Node struct {
	Val  int
	Next *Node
	Pre  *Node
}

//仅保存哑节点,pre-> rear, next-> head
/** Initialize your data structure here. */
func Constructor() MyLinkedList {
	rear := &Node{
		Val:  -1,
		Next: nil,
		Pre:  nil,
	}
	rear.Next = rear
	rear.Pre = rear
	return MyLinkedList{rear}
}

/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
func (this *MyLinkedList) Get(index int) int {
	head := this.dummy.Next
	//head == this, 遍历完全
	for head != this.dummy && index > 0 {
		index--
		head = head.Next
	}
	//否则, head == this, 索引无效
	if 0 != index {
		return -1
	}
	return head.Val
}

/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
func (this *MyLinkedList) AddAtHead(val int) {
	dummy := this.dummy
	node := &Node{
		Val: val,
		//head.Next指向原头节点
		Next: dummy.Next,
		//head.Pre 指向哑节点
		Pre: dummy,
	}

	//更新原头节点
	dummy.Next.Pre = node
	//更新哑节点
	dummy.Next = node
	//以上两步不能反
}

/** Append a node of value val to the last element of the linked list. */
func (this *MyLinkedList) AddAtTail(val int) {
	dummy := this.dummy
	rear := &Node{
		Val: val,
		//rear.Next = dummy(哑节点)
		Next: dummy,
		//rear.Pre = ori_rear
		Pre: dummy.Pre,
	}

	//ori_rear.Next = rear
	dummy.Pre.Next = rear
	//update dummy
	dummy.Pre = rear
	//以上两步不能反
}

/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
func (this *MyLinkedList) AddAtIndex(index int, val int) {
	head := this.dummy.Next
	//head = MyLinkedList[index]
	for head != this.dummy && index > 0 {
		head = head.Next
		index--
	}
	if index > 0 {
		return
	}
	node := &Node{
		Val: val,
		//node.Next = MyLinkedList[index]
		Next: head,
		//node.Pre = MyLinkedList[index-1]
		Pre: head.Pre,
	}
	//MyLinkedList[index-1].Next = node
	head.Pre.Next = node
	//MyLinkedList[index].Pre = node
	head.Pre = node
	//以上两步不能反
}

/** Delete the index-th node in the linked list, if the index is valid. */
func (this *MyLinkedList) DeleteAtIndex(index int) {
	//链表为空
	if this.dummy.Next == this.dummy {
		return
	}
	head := this.dummy.Next
	//head = MyLinkedList[index]
	for head.Next != this.dummy && index > 0 {
		head = head.Next
		index--
	}
	//验证index有效
	if index == 0 {
		//MyLinkedList[index].Pre = index[index-2]
		head.Next.Pre = head.Pre
		//MyLinedList[index-2].Next = index[index]
		head.Pre.Next = head.Next
		//以上两步顺序无所谓
	}
}

4.反转链表

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

4.1 双指针法

利用了pre,cur两个指针来实现翻转操作,temp是一个临时变量,用于保存cur的下一个节点,防止cur.Next被覆盖后找不到下一个节点的位置。

func reverseList(head *ListNode) *ListNode {
    var temp,pre *ListNode
    cur := head
    for cur!=nil{
        //temp是专门用来保存cur的下一个指针
        temp = cur.Next
        //翻转操作
        cur.Next = pre
        //更新各个指针的
        pre = cur
        cur = temp
    }
    return pre
}

4.2 递归法

func reverseList(head *ListNode) *ListNode {
    return reverse(nil,head)
}

func reverse(pre,cur *ListNode) *ListNode{
    if cur==nil {
        return pre
    }
    temp := cur.Next
    cur.Next = pre
    // pre = cur
    // cur = temp
    return reverse(cur,temp)
}

5. 两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
在这里插入图片描述
我们可以想象pre指向的是1号节点,p1指的是3号节点,p2指的是4号节点。

func swapPairs(head *ListNode) *ListNode {
	// 创建一个虚拟的头结点
    emptyNode := &ListNode{-999,head}
    // 空列表
    p1 := head
    if p1==nil{
    	//判断是否是空列表,防止取p2时报错
        return p1
    }
    p2 := head.Next
    pre := emptyNode
    for p1!=nil && p2!=nil{
        temp:=p2.Next
        pre.Next = p2
        p2.Next = p1
        pre = p1
        p1.Next = temp
        //更新p1,p2
        p1 = temp
        if(p1!=nil){
            p2 = p1.Next
        }
    }
    return emptyNode.Next
}

6.删除链表的倒数第N个节点

使用双指针,一个指针先走N步。之后一起向前走,当前面到达尾端时,后面这个就是倒数第N个节点。
就好比,两个人跑步。一个人先跑到50m,之后两个人在以一样的速度前进,当第一个到达终点时,第二个人距离终点50m。

func removeNthFromEnd(head *ListNode, n int) *ListNode {
    // 添加空节点
    emptyNode := &ListNode{-999,head}
    pre := emptyNode
    fastPoint,slowPoint := head,head
    for n>0{
        fastPoint = fastPoint.Next
        n--
    }
    for fastPoint!=nil{
        pre = slowPoint
        fastPoint = fastPoint.Next
        slowPoint = slowPoint.Next
    }
    pre.Next = slowPoint.Next

    return emptyNode.Next
}

7.链表相交

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

7.1 方法一

思路1:求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置。此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    lenA,lenB := 0,0
    p1,p2 := headA,headB
    for p1!=nil{
        p1=p1.Next
        lenA++
    }
    for p2!=nil{
        p2=p2.Next
        lenB++
    }
    //将两个链表变得长度相等
    sub := lenA-lenB
    if sub>0 {
        for sub>0 {
            sub--
            headA = headA.Next
        }
    }else{
        for sub<0{
            sub++
            headB = headB.Next
        }
    }
    //开始查找
    for headA!=headB{
        headA=headA.Next
        headB=headB.Next
    }
    return headA
}

7.2 方法二

思路:使用map,遍历一个链表的全部节点,放入其中。再遍历另外一个链表,并在其中map中进行查询,当第一次查询到时即是交点。

 type void struct{
 }
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    mp := make(map[*ListNode]void,0)
    for headA!=nil{
        mp[headA]=void{}
        headA=headA.Next
    }
    for headB!=nil{
        _,ok := mp[headB]
        if ok{
            return headB
        }
        headB = headB.Next
    }
    return nil
}

8.环形链表②

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

8.1 快慢指针

func detectCycle(head *ListNode) *ListNode {
    slow,fast := head,head
    for fast != nil && fast.Next != nil{
        slow = slow.Next
        fast = fast.Next.Next
        if(slow == fast){//有换?
            index1 := fast
            index2 := head
            // 两个指针,从头结点和相遇节点,各走一步,直到相遇,相遇点即为环入口
            for index1 != index2{
                index1 = index1.Next
                index2 = index2.Next
            }
            return index1
        }
    }
    return nil
}

8.2 哈希

type void struct{
}

func detectCycle(head *ListNode) *ListNode {
   mp := make(map[*ListNode]void,0)
    for head!=nil{
        mp[head]=void{}
        head=head.Next
        if _,ok:=mp[head];ok{
            return head
        }
    }
    return nil
}

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

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

相关文章

rust疑难杂症

rust疑难杂症解决 边碰到边记录&#xff0c;后续可能会逐步增加&#xff0c;备查 cargo build时碰到 Blocking waiting for file lock on package cache 原因是Cargo 无法获取对包缓存的文件锁&#xff0c; 有时vscode中项目比较多&#xff0c;如果其中某些库应用有问题&…

脸爱云一脸通智慧管理平台 SystemMng 管理用户信息泄露漏洞(XVE-2024-9382)

0x01 产品简介 脸爱云一脸通智慧管理平台是一套功能强大,运行稳定,操作简单方便,用户界面美观,轻松统计数据的一脸通系统。无需安装,只需在后台配置即可在浏览器登录。 功能包括:系统管理中心、人员信息管理中心、设备管理中心、消费管理子系统、订餐管理子系统、水控管…

Kafka介绍、安装以及操作

Kafka消息中间件 1.Kafka介绍 1.1 What is Kafka&#xff1f; 官网&#xff1a; https://kafka.apache.org/超过 80% 的财富 100 强公司信任并使用 Kafka &#xff1b;Apache Kafka 是一个开源分布式事件流平台&#xff0c;被数千家公司用于高性能数据管道、流分析、数据集成…

CentOS7安装MySQL8.3(最新版)踩坑教程

安装环境说明 项值系统版本CentOS7 &#xff08;具体是7.9&#xff0c;其他7系列版本均可&#xff09;位数X86_64&#xff0c;64位操作系统MySQL版本mysql-8.3.0-1.el7.x86_64.rpm-bundle.tar 实际操作 官网下载安装包 具体操作不记录&#xff0c;相关教程很多。 mkdir /o…

Mysql-黑马

Mysql-黑马 编写规范&#xff1a;## 一级1. 二级三级 1.Mysql概述 数据库概念mysql数据仓库 cmd启动和停止 net start mysql180 net stop mysql180备注&#xff1a;其中的mysql180是服务名 客户端连接 远程连接数据仓库 -h 主机号 -P端口号 mysql [-h 127.0.0.1] [-P 33…

YOLOv5改进之bifpn

目录 一、原理 二、代码 三、在YOLOv5中的应用 一、原理 论文链接:

Android4.4真机移植过程笔记(二)

5、盘符挂载 先定义overlay机制路径&#xff0c;后面storage_list.xml要用到&#xff1a; 在路径&#xff1a; rk3188_android4.4.1/device/rockchip/OK1000/overlay/frameworks/base/core/res/res/xml/定义好&#xff0c;注意名字要和emmc的代码片段&#xff08;往下面看&am…

大数据信用花了,一般多久能正常?

在当今数字化时代&#xff0c;大数据技术被广泛应用于各个领域&#xff0c;包括金融、电商、社交等。然而&#xff0c;随着大数据技术的普及&#xff0c;个人信用问题也日益凸显&#xff0c;其中“大数据信用花”现象尤为引人关注。那么&#xff0c;大数据信用花究竟是什么?一…

(四)小程序学习笔记——自定义组件

1、组件注册——usingComponents &#xff08;1&#xff09;全局注册&#xff1a;在app.json文件中配置 usingComponents进行注册&#xff0c;注册后可以在任意页面使用。 &#xff08;2&#xff09;局部注册&#xff0c;在页面的json文件中配置suingComponents进行注册&#…

2023 广东省大学生程序设计竞赛(部分题解)

目录 A - Programming Contest B - Base Station Construction C - Trading D - New Houses E - New but Nostalgic Problem I - Path Planning K - Peg Solitaire A - Programming Contest 签到题&#xff1a;直接模拟 直接按照题目意思模拟即可&#xff0c;为了好去…

labview强制转换的一个坑

32位整形强制转换成枚举的结果如何&#xff1f; 你以为的结果是 实际上的结果是 仔细看&#xff0c;枚举的数据类型是U16&#xff0c;"1"的数据类型是U32&#xff0c;所以转换产生了不可预期的结果。所以使用强制转换时一定要保证两个数据类型一致&#xff0c;否则…

04 - 步骤 JSON input

简介 Kettle 的 JSON Input 步骤是用于从 JSON 格式的数据源中读取数据的步骤。它允许用户指定 JSON 格式的输入数据&#xff0c;然后将其转换成 Kettle 中的行流数据&#xff0c;以供后续的数据处理、转换和加载操作使用。 使用 场景 1、拖拽到面板 2、指定JSON input 为 K…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-9.1-LED灯(模仿STM32驱动开发实验)

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

阿里云开源大模型开发环境搭建

ModelScope是阿里云通义千问开源的大模型开发者社区&#xff0c;本文主要描述AI大模型开发环境的搭建。 如上所示&#xff0c;安装ModelScope大模型基础库开发框架的命令行参数&#xff0c;使用清华大学提供的镜像地址 如上所示&#xff0c;在JetBrains PyCharm的项目工程终端控…

【IDEA】IDEA自带Maven/JDK,不需要下载

IDEA是由Java编写的&#xff0c;为了保证其运行&#xff0c;内部是自带JDK的。IDEA 2021 及 之后的版本是自带Maven的&#xff1a; 视频连接&#xff1a; https://www.bilibili.com/video/BV1Cs4y1b7JC?p4&spm_id_frompageDriver&vd_source5534adbd427e3b01c725714cd…

3-4STM32C8T6按键控制LED开与关

实物接线如下&#xff1a; 为了代码的简洁性&#xff0c;这里需要对LED与KEY进行封装如下&#xff1a; #include "stm32f10x.h" // Device headervoid LED_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GP…

粤嵌gec6818开发板-播放视频、音频文件(管道文件控制)

前段时间做了一个项目&#xff0c;用到了linux环境下gec6818开发板播放视频、音频文件&#xff0c;在这里给大家分享一下。 这里使用的方法是利用mplayer播放器进行播放&#xff0c;首先先给开发板装上mplayer播放器&#xff0c;这里就不详细说明了。 我用的是管道文件来控制视…

如何解决DA14531编译工程出现大量报错的问题

在编译DA14531某个工程时&#xff0c;在这台电脑可以编译&#xff0c;另外一台电脑就编译不过&#xff0c;出现很多错误问题。那要怎样处理呢&#xff1f; 建议安装新MDK版本 可能是MDK版本问题&#xff0c;在不同的电脑安装不同的MDK版本&#xff0c;用新的版本可以编译通过&…

ZABAPGIT问题,导入github上的程序包时报 DBSQL_DUPLICATE_KEY_ERROR

跟踪程序发现在94050行 INSERT seocompotx FROM TABLE it_descriptions 报的错 刚开始&#xff0c;不想着改动他&#xff0c;把seocompotx 表的数据做下指定清楚&#xff0c;但是5次清楚后&#xff0c;果断注释掉 改成 MODIFY seocompotx FROM TABLE it_descriptions。 在用…

Winfrom —— 计算阶乘

首先搭建一个界面 创建listBox输入框进行输入内容 界面图如下 计算按钮的事件 private void button1_Click(object sender, EventArgs e) {if (textBox1.Text.Length 0){textBox1.Text "";}else{int n 1;int sum 1;n Convert.ToInt32(textBox1.Text);for (; n…