链表题目

  • 一、删除链表中的节点,且只能访问该节点
  • 二、找到链表当中的中间节点
  • 三、判断一个链表是否有环
  • 四、给定一个循环链表,找到环的开始节点
  • 五、删除一个链表中的倒数第N个节点
  • 六、给定一个链表,分成两个链表


一、删除链表中的节点,且只能访问该节点

只能访问该节点的话,那该节点的上一个节点我们是无法访问的。一般我们的思路是这样的,如果我们要删除节点b,那么我们需要用a节点的next指向b节点next指向的c节点,那么就做好了删除节点的操作了,被删除的节点会被python的垃圾回收机制处理

python删除单链表节点 python链表删除指定节点_leetcode


但是,现在的问题是,我们没有办法访问到a节点,只能从b节点向后访问,那么就需要我们换一个角度了

将c节点的值赋值给b节点,然后将b节点的next指向c的next,这样间接的实现了删除b节点的操作

python删除单链表节点 python链表删除指定节点_python删除单链表节点_02


思路有了,代码的实现是比较简单的

def remove_node(node):
    print(node.value)
    node.value = node.next.value
    node.next = node.next.next
    print(node.value)
    while node.next != None:
        node = node.next
        print(node.value)

二、找到链表当中的中间节点

中间节点的查找我们可能第一反应就是获取链表的长度,然后折半,这样就能找到中间节点了,这个思路是没问题的,但是有时候条件可能不是那么友好,如果加入一个限制条件,不允许我们使用链表的长度。这应该怎么办呢?
我们可以设置两个pointer,第一个pointer移动的距离是第二个pointer的2倍,那么,当第一个pointer遍历完整个链表之后,第二个pointer正好到达中间节点,此时输出第二个pointer

python删除单链表节点 python链表删除指定节点_leetcode_03


上代码

def find_middle_node(node):
	
	assert node.head.next is not None and node.head is None
	
	a1 = node.head  # 第一个pointer
	b1 = node.head  # 第二个pointer
	while a1.next != None and a1.next.next != None:
		a1 = a1.next.next
		b1 = b1.next
	print(b1.value)

python删除单链表节点 python链表删除指定节点_链表_04

三、判断一个链表是否有环

一个链表有环的情况无非就是两种

python删除单链表节点 python链表删除指定节点_leetcode_05


python删除单链表节点 python链表删除指定节点_python_06


思路:可以准备两个pointer,第一个pointer走的快,第二个pointer走的慢,当第一个pointer追上第二个pointer的时候,说明有环,为什么呢?当pointer的next为None的时候,说明链表已经走完,此时一定无环.

python删除单链表节点 python链表删除指定节点_链表_07


上代码:

def has_cycle(node):
	if node is None:
		print('has no cycle')
	fast = node
	slow = node

	while fast != None and fast.next != None:
		fast = fast.next.next
		slow = slow.next

		if fast == slow :
			print('has cycle!')
			break
node1 = Node(1)
node2 = Node(10)
node3 = Node(6)
node1.next = node2
node2.next = node3
node3.next = node1
has_cycle(node1)
node1 = Node(10)
node2 = Node(20)
node3 = Node(50)
node1.next = node2
node2.next = node3
node3.next = node2
has_cycle(node1)
node1 = Node(102)
node2 = Node(204)
node3 = Node(504)
node1.next = node2
node2.next = node3
has_cycle(node1)

结果

python删除单链表节点 python链表删除指定节点_链表_08

四、给定一个循环链表,找到环的开始节点

一个循环链表的环的判定,有两种情况:

左侧是情况1,开始节点和结束节点是重合的

右侧是情况2,环的开始节点在链表的中间部分

python删除单链表节点 python链表删除指定节点_数据结构_09


情况1的思路跟上一道题的思路是一样的,可以准备两个pointer,fast_pointer每次走两个,slow_pointer每次走一个,当fast_pointer追上slow_pointer的时候,就找到了环的开始节点了

下面附上情况1的代码:

def find_the_node_of_beginCycle(node):
	fast = node
	slow = node
	while fast is not None and fast.next is not None:
		fast = fast.next.next
		slow = slow.next

		if fast == slow:
			return fast
			break
n1 = Node(10)
n2 = Node(20)
n3 = Node(4)
n4 = Node(5)
n1.next , n2.next, n3.next, n4.next = n2, n3, n4, n1
res = find_the_node_of_beginCycle(n1)
print('the begin node value : %s'%res.value)

python删除单链表节点 python链表删除指定节点_python删除单链表节点_10

情况2的思路需要好好分析分析了

假设情况2的链表当中环的开始节点在a点,链表开始节点到环的开始节点距离是k,那么当slow_pointer走到a点的时候,fast_pointer已经走到了距离a点向后的k个距离处,那么什么时候相遇呢?如下图所示:

python删除单链表节点 python链表删除指定节点_链表_11


首先,我们清楚的是fast_pointer的速度是slow_pointer的两倍,当fast_pointer转了一圈的之后,slow_pointer只走了半圈,这个时候是无法相遇的,那么就需要继续向后再走,当slow_pointer走完剩下的半圈的之后,fast_pointer又走了一圈,那么在这期间两个pointer相遇了,那相遇的点在哪呢?在上图所示的b点,距离a点为k.

当slow_pointer走完一圈,fast_pointer走了两圈之后,这两个指针回到原来的位置,此时slow_pointer和fast_pointer相差k,从当前状态开始,让slow_pointer和fast_pointer保持之前的速度向反方向移动,那么当slow_pointer走到b点的时候经过k个距离,fast_pointer也走到了b点,距离为2k,正好相遇,此时,整个链表中会出现三个k值

python删除单链表节点 python链表删除指定节点_python删除单链表节点_12


这个时候,将slow_pointer挪到起始点,fast_pointer在b点不动,两个pointer都以一个速度依次向前遍历,当两个pointer相遇的时候就找到了链表的环的开始节点

下面附上情况2的代码:

def find_the_node_of_beginCycle(node):
	fast = node
	slow = node
	while fast != None and fast.next != None:
		fast = fast.next.next
		slow = slow.next
		
		if fast == slow:
			fast = node
			break
	while fast != slow:
		fast = fast.next
		slow = slow.next
	return slow

n1 = Node(10)
n2 = Node(20)
n3 = Node(4)
n4 = Node(5)
n5 = Node(44)
n6 = Node(7)
n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n5
n5.next = n6
n6.next = n3
res = find_the_node_of_beginCycle(n1)
print('the begin node value:%s'%res.value)

python删除单链表节点 python链表删除指定节点_数据结构_13

五、删除一个链表中的倒数第N个节点

第一印象的话我们可能会去想得到链表的size,然后用size-n获取它的位置,通过指向要删除的节点的next来删除倒数第N个节点。但是如果不能获取size呢?

还是那个思路,准备两个pointer,fast_pointer先走N个节点之后slow_pointer开始走,当fast_pointer走完链表之后,slow_pointer的位置就是要删除的节点

python删除单链表节点 python链表删除指定节点_数据结构_14


上代码:

def delete_the_nth_to_last(list, n):
	fast = list.head
	slow = list.head
	while n > 0:
		fast = fast.next
		n -= 1
	while fast.next is not None:
		fast = fast.next
		slow = slow.next
	res = slow
	res.next = slow.next.next
	list.size -= 1

L = LinkedList()
L.insert_first(4)
L.insert_first(23)
L.insert_first(66)
L.insert_first(89)
L.insert_first(75)
L.print_linkedList()
delete_the_nth_to_last(L, 2)
L.print_linkedList()

python删除单链表节点 python链表删除指定节点_数据结构_15

六、给定一个链表,分成两个链表

如果是偶数个节点直接对半即可,如果是奇数个节点,返回靠前的节点位置
分成两个链表的话需要三个pointer,fast_pointer遍历的速度是slow_pointer的2倍,mid_pointer的速度和slow_pointer是一致的,当fast_pointer遍历完链表之后,slow_pointe和mid_pointer也就到达了链表的中间位置,然后创建一个新的链表L1,将L1的next指向左侧的链表,再创建一个新的链表L2,将L2的next指向mid_pointer.next,mid_pointer.next指向None.

下图所示的链表的操作是偶数个节点的,奇数个节点同理

python删除单链表节点 python链表删除指定节点_leetcode_16

代码如下:

def split_LinkedList(node):
	fast = node
	slow = node
	mid = slow
	while fast is not None:
		mid = slow
		slow = slow.next
		fast = fast.next.next if fast.next is not None else None


	left = node
	right = mid.next
	mid.next = None
	return left, right

n1 = Node(10)
n2 = Node(20)
n3 = Node(4)
n4 = Node(5)
n5 = Node(44)
n6 = Node(7)
n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n5
n5.next = n6
left_node  = Node()
right_node = Node()
left_node, right_node = split_LinkedList(n1)
L1 = LinkedList()
L1.head.next = left_node
L1.print_linkedList()
L2 = LinkedList()
L2.head.next = right_node
L2.print_linkedList()

python删除单链表节点 python链表删除指定节点_leetcode_17