链表题目汇总

序言

努力汇总链表题目,好好干饭,好好努力,好好加油哦

题目一:删掉链表的某一个node

实现一种算法,删除单向链表中间的某个节点(即不是第一个或最后一个节点)

输入:删除单向链表a->b->c->d->e->f中的节点c
结果:不返回任何数据,但该链表变为a->b->d->e->f

链表中的每一个node有两个属性 一个是 val 一个是next表方向
所以想要删掉某特定的node,只要改变这个node的这两个属性,使之变成下一个node的两个属性

node.val = node.next.val
node.next = node.next.next

class Node:
	"""
	定义节点Node
	"""
	def __init__(self,val=0,next=None):
		self.val = val
		self.next = next
def deleteNode(node):
	node.val = node.next.val
    node.next = node.next.next

题目二:倒转的链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

把这个修改一下,就很明显了思路:

输入: 1->2->3->4->5->NULL
输出:NULL<-1<-2<-3<-4<-5

思路:
当输入头节点的时候,需要改变方向和next
用三个值 pre,cur,nxt来改变方向和赋值。

首先是 初始化 pre = None ; cur =0
赋值 nxt = cur.next
然后把cur的方向 指向在 pre,即 cur.next = pre;
然后把pre 和cur往后移动 即 pre = cur ; cur = nxt

重复下去,终止条件是 cur is None, 这个时候return pre即是倒转的链表

def reverseList(head):
	#初始化
	pre, cur = None , head
	while cur:
		nxt = cur.next
		cur.next = pre
		pre = cur
		cur = nxt 
	return pre

题目三:合并两个升序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
(1) l1 和 l2 均按 非递减顺序 排列
(2)两个链表的节点数目范围是 [0, 50]

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
输入:l1 = [], l2 = []
输出:[]
输入:l1 = [], l2 = [0]
输出:[0]

思路:如何合并两条升序链表??
好难哦 要用溯洄了 溯洄是我最难的搞的。 怎么溯洄呀~
比如有两条链表L1 和 L2
对于L1[0] , L2[0]
如果L1[0] < L2[0]: 则 L1[0] + merge(L1[1], L2[0])

如果L1[0] > L2[0]: 则 L2[0] + merge(L1[0], L2[1])

边界问题:如果有一个链表空了,那就返回l2 。

def mergeTwoLists(l1, l2):
	if not l1: return l2
	if not l2: return l1
	if l1.val < l2.val:
		l1.next  = self.mergeTwoLists(l1.next, l2)
		return l1
	else: 
		l2.next = self.mergeTwoLists(l1, l2.next)
		return l2 
 

题目四:找出两个链表的公共节点

题目四:输入两个链表,找出它们的第一个公共节点

在这里插入图片描述
在这里插入图片描述
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

思路是
考虑构建两个节点指针 A​ , B 分别指向两链表头节点 headA , headB ,做如下操作:
指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:
a + (b - c)
a+(b−c)

指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:
b + (a - c)
b+(a−c)

如下式所示,此时指针 A , B 重合,并有两种情况:
a + (b - c) = b + (a - c)
a+(b−c)=b+(a−c)

若两链表 有 公共尾部 (即 c > 0c>0 ) :指针 A , B 同时指向「第一个公共节点」node 。
若两链表 无 公共尾部 (即 c = 0c=0 ) :指针 A , B 同时指向 null。
所以当A = B的时候,即A和B重合的时候,即为第一个公共节点。

def getIntersectionNode(headA,headB):
	A,B = headA,headB 
	while A!= B:
		if A: A=A.next
		else A = headB 
		if B: B = B.next 
		else: B = headA 
	return A

题目五:LT141-判断链表是否有环

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

在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点

在这里插入图片描述

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

在这里插入图片描述

输入:head = [1], pos = -1
输出:false
解释:链表中没有环

方法一: 哈希表

首先,录入所有的元素,如果出现了重复的就返回True,否则是False。

def hasCycle(self, head: ListNode) -> bool:
	satck = []
	N = len(head)
	while head:
		if head in stack:
			return True 					
		stack.append(head)
		head = head.next 

时间复杂度是O(N): 最坏的情况是需要遍历所有节点N
空间复杂度都是O(N):这里是哈希表的开销,最坏的情况是每一个节点都收录入哈希表。

方法二:快慢指标

快慢指标是指 慢指标放在head, 快指标放在head.next, 快指标走两步,慢指标走一步。如果链表是有环的话,迟早慢指标和快指标会相遇。
若有环,两指针一定会相遇。因为每走 1 轮,fast 与 slow 的间距 +1,fast 终会追上 slow。
举个例子理解:
输入:head = [3,2,0,-4], pos = 1 即环在2那相连接。
下面展示slow和fast的路径
slow: 3 --> 2 --> 0
fast: 2 --> -4 --> 0
在0节点的时候重合了;
为什么不把slow和fast都放在同一节点开始 这是因为如果开始一样的话就没法进入循环 while slow != fast

def hasCycle(self, head: ListNode) -> bool:
	#排除特殊情况 
	if not head or not head.next: return False 
	slow = head 
	fast = head.next 
	while slow != fast:
		#如果fast是None 或者fast.next是None的话 表示有末端-->无环
		if not fast or not fast.next: return False 
		slow = slow.next 
		fast = fast.next.next 
	return True 	

空间复杂度:O(1)。我们只使用了两个指针的额外空间

时间复杂度:O(N)
当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。
当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N 轮

题目六:LT142-链表是否有环II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?

在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

在这里插入图片描述

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

同理方法一:哈希表

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        hashtable = []
        while head:
            if head in hashtable: 
                return head
            hashtable.append(head) 
            head = head.next 

但这里的时间复杂度和空间复杂度都是O(N).有没有空间复杂度低的方法呢,那肯定是慢块指针了

方法二:慢块指针
这里复杂了一点是因为需要输出 开始入环的第一个节点。 之前那道题只是输出是否有环。
所以这里的思路是:
假设链表是 a + b形式,a代表环外的节点数,b代表环内的节点数。
假设slow和fast相遇的时候,
必有
fast = 2 * slow 因为fast的步数是slow的两倍,现在是同一起点出发。

为了跟slow重合,fast比slow多跑n个环,也就是多跑nb个步数。
所以
fast = slow + nb
联合两式有:
slow = nb ; fast = 2nb

这个时候,我们想一下,刚好能到入环的节点是 a + bn 个步数。
所以这个时候 从节点出发 a个步数,slow也同时出发a个步数 则就在入环的节点重合
或者说 在这个时候,有一个指针从头开始走,步长为1,跟slow重合肯定是在入环的节点的。

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        if not head or not head.next: return  
        slow = head
        fast = head 

        while True: 
            if not fast or not fast.next: return 
            slow = slow.next 
            fast = fast.next.next 
            if slow == fast: break 
        fast = head 
        while fast != slow:
            slow = slow.next 
            fast = fast.next 
        return slow 

题目七 LT234-判断链表是否是回文链表

请判断一个链表是否为回文链表

输入: 1->2
输出: false
输入: 1->2->2->1
输出: true

方法一:把链表的va 值依次放入列表。然后通过对比 列表和反转的列表是否一致 来证明回文链表

def isPalindrome(head: ListNode):
	val_list = []
	cur = head
	while cur: 
		val_list.append(cur.val)
		cur = cur.next 
	return val_list == val_list[::-1]

这里的时间复杂度和空间复杂度是O(N)

方法二:快慢指针
思路是:由于快指针fast 比 慢指针slow 步数的两倍。
所以先
找到前半部分的链表
找到后半部分的链表
再反转后半部们的链表
对比这两个链表是否一致
回复原来的链表。
返回结果

#提取前半部分的链表
def first_half(self,head):
	fast = head
	slow = head
	while True:
		if not fast or not fast.next: return 
		fast = fast.next.next
		slow = slow.next 
		if slow == fast: break 
	return slow 
#反转链表
def reverse_list(self,head):
	pre,cur = None,head
	while cur:
		nxt = cur.next
		cur.next = pre 
		pre = cur
		cur = nxt 
	return pre
#
def isPalindrome(self, head: ListNode) -> bool:
        if head is None:
            return True
            
        # 找到前半部分链表的尾节点并反转后半部分链表
        first_half_end = self.first_half(head)
        second_half_start = self.reverse_list(first_half_end.next)

        # 判断是否回文
        result = True
        first_position = head
        second_position = second_half_start
        while result and second_position is not None:
            if first_position.val != second_position.val:
                result = False
            first_position = first_position.next
            second_position = second_position.next
        return result    
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jianafeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值