首页 > 其他分享 >代码随想录day4 | 24 两两交换链表节点 19 删除倒数第n个节点 142 环形链表

代码随想录day4 | 24 两两交换链表节点 19 删除倒数第n个节点 142 环形链表

时间:2024-07-20 14:42:11浏览次数:11  
标签:ListNode nil 随想录 Next 链表 节点 指针

24 两两交换节点

func swapPairs(head *ListNode) *ListNode {
	// 思路 涉及到链表增删改操作,优先考虑使用虚拟头节点  此处为双指针加上虚拟头节点
	if head == nil || head.Next == nil {
		return head
	}

	var dummyHead = &ListNode{0, head}
	var prev = dummyHead
	for prev.Next != nil && prev.Next.Next != nil {  // 同时考虑奇数长度链表和偶数长度链表终止条件
		node1 := prev.Next
		node2 := prev.Next.Next

		node1.Next = node2.Next
		node2.Next = node1
		prev.Next = node2

		prev = node1
	}
	return dummyHead.Next
}

时间 跳着便利链表本质上n/2 n  空间 有限单变量 1
func swapPairs(head *ListNode) *ListNode {
	// 思路 尝试根据双指针 写出 递归
	if head == nil || head.Next == nil {
		return head
	}

	var dummyHead = &ListNode{0, head}
	cur := dummyHead
	swap(cur)
	return dummyHead.Next
}

func swap(cur *ListNode) {
	if cur.Next == nil || cur.Next.Next == nil {  // 根据外层for循环判断递归终止条件
		return
	}

	// 循环体的交换节点结构
	node1 := cur.Next
	node2 := cur.Next.Next
	node1.Next = node2.Next
	node2.Next = node1
	cur.Next = node2

	// 递归条件,即变量的迭代条件prev = node1
	swap(node1)
	return
}

19 删除链表倒数第n个节点

func removeNthFromEnd(head *ListNode, n int) *ListNode {
	// 思路 链表的删除操作,优先考虑虚拟头节点
	// 然后尝试双指针,看了视频后的思路很清晰,重点 难点是n就是两个指针之间的间隔,cur == nil 就删除 pre

	var dummyHead = &ListNode{0, head}
	var pre, cur = dummyHead, dummyHead
	for i:=0 ; i<n; i++ { // 此处作用往后跳n个节点,题目已经说明n<size,所以不考虑越界情况
		cur = cur.Next
	}

	for cur.Next != nil { // cur == nil 循环终止,删除pre, 但是这样写我们没办法处理删除将pre上一个元素与pre之后连接,除非单独变量保存,所以这里采用cur.next != nil, pre存储上一个变量,要删除pre.Next
		cur = cur.Next
		pre = pre.Next  // pre 永远走在cur之后,所以不会空指针
	}

	// 遍历完成,此时pre位于删除元素上一个节点
	pre.Next = pre.Next.Next  // 直接next指向删除元素下一个节点,删除完成

	return dummyHead.Next
}

// 时间 遍历整个链表 n-常数间隔 = n  空间 有限单变量 1

160 判断链表相交节点

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    // 思路 类似双指针 快慢指针 快指针遍历链表a, 慢指针遍历链表b, 如果节点内存地址相等,视为相交

	for headA != nil {
		cur := headB // 由于是指针类型引用,所以这里的内存地址相同与head2
		for cur != nil {  // 这里使用cur而不是用head2是因为内部便利之后head已经变成尾节点了,不能直接使用了
			if cur == headA { // 判断内存地址是否相等
				return headA
			}
			cur = cur.Next
		}
		headA = headA.Next
	}

	return nil
}

// 思考
为甚么判断内存地址 &cur == &headA 这样子写是错误的?
因为 cur, head 是指针,指向的是其他变量的内存地址,但是指针也是变量,也有自己的内存地址,如果对指针再取指针,那么取得就是两个指针变量的内存地址,就不是我们想要的对比保存变量的内存地址结果了

时间 m*n  空间 1

  • 循环链表的思路(时间复杂度m+n)

  • 数学逻辑解释

假设链表 A 的长度为 (m),链表 B 的长度为 (n),它们在节点 (C) 处相交。链表 A 在相交前的部分长度为 (a),链表 B 在相交前的部分长度为 (b),相交部分的长度为 (c)。

因此,我们有:
[ m = a + c ]
[ n = b + c ]

  • 双指针方法的逻辑

初始化:两个指针 (pA) 和 (pB) 分别指向链表 A 和链表 B 的头节点。
遍历链表:两个指针同时向前移动,如果到达链表末尾,则重定位到另一个链表的头节点。
相遇:由于两个指针遍历的总长度相同,最终会在相交节点处相遇。

详细解释

当 (pA) 遍历完链表 A 后,它将重定位到链表 B 的头节点,并继续遍历链表 B。
当 (pB) 遍历完链表 B 后,它将重定位到链表 A 的头节点,并继续遍历链表 A。
经过第一次遍历后,两个指针分别走过的路径长度为:

[ pA: a + c + b ]
[ pB: b + c + a ]

由于 (a + c + b = b + c + a),因此两个指针在第二次遍历中会在相交节点 (C) 处相遇。

  • 数学推导

我们可以通过数学推导来验证这种方法的正确性:

第一次遍历:

指针 (pA) 走过的路径长度为 (a + c),然后重定位到链表 B。
指针 (pB) 走过的路径长度为 (b + c),然后重定位到链表 A。

第二次遍历:

指针 (pA) 继续走过链表 B 的长度 (b)。
指针 (pB) 继续走过链表 A 的长度 (a)。
由于两个指针走过的总路径长度相同,最终会在相交节点 (C) 处相遇。

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    // 思路 循环链表的思路。只需要不到两圈就能查找到相交点
	nodeA, nodeB := headA, headB

	for nodeA != nodeB { // 未到相交点
		// 遍历链表a
		if nodeA == nil { // 遍历完a遍历b
			nodeA = headB
		}else{
			nodeA = nodeA.Next
		}

		// b同理
		if nodeB == nil {
			nodeB = headA
		}else{
			nodeB = nodeB.Next
		}

	}

	return nodeA
}
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    // 思路 根据双指针写出递归
	return checkInter(headA, headB, headA, headB)
}

func checkInter(nodeA, nodeB, headA, headB *ListNode) *ListNode {
	if nodeA == nodeB {  // 循环结束条件就是递归终止条件
		return nodeA
	}
	// 遍历链表a
	if nodeA == nil { // 遍历完a遍历b
		nodeA = headB
	}else{
		nodeA = nodeA.Next
	}

	// b同理
	if nodeB == nil {
		nodeB = headA
	}else{
		nodeB = nodeB.Next
	}

	return checkInter(nodeA, nodeB, headA, headB)
}

142 环形链表入口

image

  • 快慢指针相遇:快指针一次移动两个节点,慢指针一次移动一个节点,快指针和慢指针在环内某个节点相遇时,快指针已经比慢指针多走了一个或多个完整的环。

  • 相遇后的距离关系:由于快指针比慢指针多走了一个或多个完整的环,我们可以得出从头节点到环入口的距离 a 等于从相遇点沿着环再走 c 的距离。

  • 找到环的入口:当快指针和慢指针相遇后,将快指针重新指向链表头节点,然后快指针和慢指针每次都移动一步。由于从头节点到环入口的距离等于从相遇点再走到环入口的距离,因此它们最终会在环的入口相遇。

func detectCycle(head *ListNode) *ListNode {
    // 思路 快慢指针方法

    // 极端情况处理
    if head == nil || head.Next == nil {
        return nil
    }

    fast, slow := head.Next.Next, head.Next
    for fast != slow { // 循环结束条件,快慢指针相交,此时快指针移动距离 两倍于 慢指针移动距离
        if slow == nil || fast == nil || fast.Next == nil { // 无环  fast.Next == nil 这里判断防止  fast = fast.Next.Next
            return nil
        }
        slow = slow.Next
        fast = fast.Next.Next
    }

    // 此时fast,slow相交,然后新指针从头节点遍历,头到环入口位置 = 慢指针移动常数圈 + 交点到入口距离,所以会相交于入口位置
    fast = head
    for fast != slow { // 此时已经判断有环,所以不用考虑nil边界情况
        fast = fast.Next
        slow = slow.Next
    }
    return fast  // return slow

}

标签:ListNode,nil,随想录,Next,链表,节点,指针
From: https://www.cnblogs.com/zhougongjin55/p/18313083

相关文章

  • 山东大学数据结构与算法实验8散列表(线性开型寻址/链表散列)
    A : 线性开型寻址题目描述要求使用线性开型寻址实现描述给定散列函数的除数D和操作数m,输出每次操作后的状态。有以下三种操作:插入x,若散列表已存在x,输出“Existed”,否则插入x到散列表中,输出所在的下标。查询x,若散列表不含有x,输出“-1”,否则输出x对应下标。......
  • 数据结构-线性表、链表
    一、线性表介绍1、线性结构​ 在数据元素存在非空有限集中:存在唯一的一个被称为“第一个”的数据元素存在唯一的一个被称为“最后一个”的数据元素除了第一个外,集合中每个数据元素都只有一个前趋元素除了最后一个外,集合中每个数据元素都只有一个后继元素2、线性表线性表......
  • 链表带环问题简单讲解
     #带环链表解题思路对于这道题我们可以定义两个指针,一个快指针,一个慢指针。快指针一次走两步,慢指针一次走一步。这样快指针就会比慢指针先进入环内。慢指针进入环后,这个问题就会演变成一个追击问题,即:快指针能否追上慢指针并与之重合。假设,慢指针进入环后与快指针的距......
  • 「代码随想录算法训练营」第十六天 | 二叉树 part6
    530.二叉搜索树的最小绝对差题目链接:https://leetcode.cn/problems/minimum-absolute-difference-in-bst/题目难度:简单文章讲解:https://programmercarl.com/0530.二叉搜索树的最小绝对差.html视频讲解:https://www.bilibili.com/video/BV1DD4y11779题目状态:通过思路:将二......
  • 算法第十一天:leetcode707.设计链表
    一、设计链表的题目描述与链接  707.设计链表的链接如下表所示,您可直接复制下面网址进入力扣学习,在观看下面的内容之前一定要先做一遍哦,这样才能印象深刻!https://leetcode.cn/problems/design-linked-list/https://leetcode.cn/problems/design-linked-list/题目描述:你......
  • Docker搭建BT-Tracker服务器贡献BT网络Tracker节点
    为畅享BT下载体验,(BT下载或做种)请添加Tracker:http://tracker.carlzeng.top:6969/announce长期自主自觉维护朗读全文Yourbrowserdoesnotsupporttheaudioelement.有什么用搭建BTTracker服务器,自建公共的BT网络Tracker节点为畅享更快BT下载体验,请给添加本站BTTrac......
  • 数据结构—双向链表
    文章目录1.双向链表的概念和结构1.1双向链表的概念1.2双向链表与单链表的对比1.3双向链表的结构2.双向链表的接口实现2.1申请一个新结点2.2初始化2.3打印2.4尾插2.5头插2.6判断链表是否为空2.7尾删2.8头删2.9查找2.10在指定位置之后插入数据2.11在指定位......
  • 线性表——链表(c语言)
    链表概念篇示意图1.单向链表2.双向链表3.循环链表4.链表的元素插入5.链表的元素删除代码篇1.链表的定义2.链表的创建3.链表的销毁4.链表的元素插入5.链表的元素删除6.链表的元素查找7.链表下标对应的结点8.链表元素的修改9.链表的打印10.完整代码代码运行效果概......
  • 链表(Linked List)-Python实现-使用类和使用函数
    链表链表(LinkedList)单链表(SinglyLinkedList)节点类(NodeClass)链表类(LinkedListClass)使用链表类不用类的方法实现链表实现单链表使用函数实现链表具体讲解类的方法实现链表Node类LinkedList类不用类的方法实现链表创建节点添加节点删除节点搜索节点显示链表总......
  • 链表。。。
    模板题AcWing826.单链表实现一个单链表,链表初始为空,支持三种操作:向链表头插入一个数;删除第k个插入的数后面的数;在第k个插入的数后插入一个数。现在要对该链表进行M次操作,进行完所有操作后,从头到尾输出整个链表。注意:题目中第k个插入的数并不是指当前链表的第......