LinkedList 的操作.
- 加入
- 删除
- 翻转
这是三个最常考的类型.
先来讲翻转
还是经典题, Revert Linked List
然后通过这一题可以延伸出很多种不同的翻转.
在Linked List 中最常要考虑的就是保留开始的头节点, 因为每一次你链表变动的时候很容易就会把头节点丢了,而且别忘了测边界值当节点是 None
或者下一个是 None
的时候怎么办.
画个图吧
class Solution:
# @param {ListNode} head
# @return {ListNode}
def reverseList(self, head):
# prev 为一个空指针,
prev = None
# 当链表还有节点的时候
while head:
# 先用 temp 储存, 因为后面指针移动会用到
temp = head
# 拿到下一个节点, 将会变成前一个的头, 链表的移动体现在了这一步
head = head.next
# temp, 同时也是当前的节点, 下一个会变成前一个节点或者为空
temp.next = prev
# 指针指向当前节点
prev = temp
return prev
# 还有递归的做法, 但是一般不会考
class Recursive:
def reverseList(self, head):
return self._reverse(head)
def _reverse(self, head, prev=None):
if not head: return prev
tail = head.next
head.next = prev
return self._reverse(tail, head)
从这一题,延伸出了别的题目, 在面试中有些考官很喜欢问 follow up, 也一般是第一道题
转换类型的题目有以下:
reverse linked list 的变形, 其实只是部分的换,而且要照顾头尾的节点
class Solution:
# @param head, a ListNode
# @param m, an integer
# @param n, an integer
# @return a ListNode
def reverseBetween(self, head, m, n):
if not head or not head.next: return head
# 要一个临时的头节点, 为了在完成之后还能够找回原来的头节点
head2 = ListNode(-1)
head2.next = head
# 第一个指针指向第m节点的前一个位置
previous = head2
for i in range(m, 1, -1):
previous = previous.next
# 创造第二个指针, 跟head2用法一样
tail = previous.next
# 这一步是为了断开链表, 防止转换过程出现问题
previous.next = None
# 从这一步开始跟之前是一模一样的
current = tail
for i in range(n - m + 1, 0, -1):
if i == 1:
tail.next = current.next
temp = current.next
current.next = previous.next
previous.next = current
current = temp
return head2.next
Swap Nodes in Pairs
这一题也是 reverse linked list 的变形, 就是每翻转两个节点的时候,要注意连上前后的节点, 而且要留意长度为单数的时候要处理None
class Solution:
# @param a ListNode
# @return a ListNode
def swapPairs(self, head):
if not head or not head.next: return head
head2, node = ListNode(-1), head
pointer = head2
# 每两个节点的转换
while node and node.next:
# 这里从 next 变成了 next next, 因为我们要连上第三个节点
temp = node.next.next
pointer.next = node.next # frist node
pointer = pointer.next # second node
# swap
pointer.next = node # link to head
# 指针先保留,指向第三个
pointer = pointer.next # third node
# 连上第三个
node = temp
# 处理单个情况
if node:
pointer.next = node
pointer = pointer.next
pointer.next = None
return head2.next
Reorder List
这一道题要处理的问题是要把头尾连起来, reserve 的原理还是一样,重点是怎么拿到后半段的节点,然后按照前后前后的顺序连起来.
有两种方法,第一是先探测到中间点在哪里, 然后两个指针从头尾依次相连, 不需要额外空间. 另外一种方法是把双数的放到一个 stack 里面去,然后 pop 出来的节点就是已经倒过来的顺序了, 但是需要 O(n) 的空间.
class Solution:
class TwoPointers:
# @param head, a ListNode
# @return nothing
def reorderList(self, head):
if not head or not head.next or not head.next.next:
return
slow, fast = head, head.next
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast:
slow = slow.next
# 分成两半
head2 = slow.next
slow.next = None
head2 = self.reverse(head2)
h1 = head; h2 = head2
while h2:
temp1 = h1.next
temp2 = h2.next
h1.next = h2
h2.next = temp1
h1 = temp1
h2 = temp2
def reverse(self, head):
pointer = head; head2 = None
while pointer:
temp = pointer.next
pointer.next = head2
head2 = pointer
pointer = temp
return head2
class Stack:
def reorderList(self, head):
if not head or not head.next or not head.next.next:
return
stack = []
slow, fast = head, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast:
slow = slow.next
stack = []
# 把后半段加进 stack
while slow:
stack.append(slow)
slow = slow.next
slow = head
while stack:
temp = slow.next
node = stack.pop()
slow.next = node
# 重新连上单数的下一个节点
node.next = temp
slow = temp
slow.next = None
Partition List
这题比较简单, 只是用两个指针来把链表分成两个部分, 然后再把两个头连起来而已.
class Solution:
# @param head, a ListNode
# @param x, an integer
# @return a ListNode
def partition(self, head, x):
if not head: return head
sHead, bHead = ListNode(0), ListNode(0)
sTail, bTail = sHead, bHead
while head:
if head.val < x:
sTail.next = head
sTail = sTail.next
else:
bTail.next = head;
bTail = bTail.next
head = head.next
bTail.next = None
sTail.next = bHead.next
return sHead.next
Rotate List
这一题只是把链表拆分后重新连起来, 没有调换顺序, 主要考察的地方是怎样找到应该断开的 K 点
class Solution:
# @param head, a ListNode
# @param k, an integer
# @return a ListNode
def rotateRight(self, head, k):
if not head or k == 0: return head
pointer, index = head, 1
# 先知道链表总长
while pointer.next:
pointer = pointer.next
index += 1
# 计算出应该断开的 diff 点
diff = index - k % index # mod is for n larger than length
pointer.next = head # connect head and tail
while diff > 0:
pointer = pointer.next
diff -= 1
head = pointer.next # split the linked list
pointer.next = None # tail
return head