六、链表
1. 使用python定义链表结构
from util.Empty import Empty from util.Outbound import Outbound class Node(): def __init__ (self, value = None, next = None): self.value = value self.next = next class LinkedList(): def __init__(self): self.head = Node() self.tail = None self.length = 0 def peek(self): if not self.head.next: raise Empty( 'LinkedList is empty' ) return self.head.next def get_first(self): # 得到第一个元素 if not self.head.next: raise Empty( 'LinkedList is empty' ) return self.head.next def get_last(self): # 得到最后一个元素 if not self.head.next: raise Empty( 'LinkedList is empty' ) node = self.head while node.next != None: node = node.next return node def get(self, index): # 按索引得到元素 if (index < 0 or index >= self.length): raise Outbound( 'index is out of bound' ); if not self.head.next: raise Empty( 'LinkedList is empty' ) node = self.head.next for i in range(index): node = node.next return node def add_first(self, value): node = Node(value, None) node.next = self.head.next self.head.next = node self.length += 1 def add_last(self, value): new_node = Node(value) node = self.head while node.next != None: node = node.next node.next = new_node self.length += 1 def add(self, index, value): if (index < 0 or index > self.length): raise Outbound( 'index is out of bound' ) if not self.head.next: raise Empty( 'LinkedList is empty' ) new_node = Node(value) node = self.head for i in range(index): node = node.next new_node.next = node.next; node.next = new_node; self.length += 1 def remove_first(self): if not self.head.next: raise Empty( 'LinkedList is empty' ) value = self.head.next self.head.next = self.head.next.next self.length -= 1 return value def remove_last(self): if not self.head.next: raise Empty( 'LinkedList is empty' ) node = self.head.next prev = self.head while node.next != None: prev = node node = node.next prev.next = None return node.value def remove(self, index): if (index < 0 or index >= self.length): raise Outbound( 'index is out of bound' ); if not self.head.next: raise Empty( 'LinkedList is empty' ) node = self.head for i in range(index): node = node.next result = node.next; node.next = node.next.next; self.length += 1 return result; def printlist(self): node = self.head.next count = 0 while node and count<20: print(node.value, end = " ") node = node.next count = count + 1 print('')
2. 删除链表中的节点
- leetcode: 第237题. 删除链表中的节点,只允许访问要删除的那个节点。 难度:简单
- leetcode: 面试题18. 删除链表的节点 与此题要求不一样,有可能包括尾节点也要删除
# 传统的方法是找到prev节点,然后让prev.next = prev.next.next # 现在不让访问prev了,只允许访问当前的node def delete_node(node): print(node.value) node.value = node.next.value # 偷梁换柱,把下一个节点的value赋值给当前节点的值,删除下一个节点即可。 node.next = node.next.next
3. 找到链表的中间节点
- leetcode 第876题. 链表的中间结点 难度:简单
# 快慢指针思想 def find_middle(lst): assert lst.head is not None and lst.head.next is not None head = lst.head fast = head slow = head while fast is not None and fast.next is not None: fast = fast.next.next slow = slow.next return slow.value
4. 确定链表是不是环形的
- leetcode 第141题. 环形链表 难度:简单
def has_cycle(lst): return has_cycle_helper(lst.head) def has_cycle_helper(head): if head is None: return False slow = head fast = head while fast is not None and fast.next is not None: fast = fast.next.next slow = slow.next if slow == fast: # 环形的赛道一定会相遇 return True return False
5. 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
- leetcode 第142题. 环形链表 II 难度:中等
- 面试题 02.08. 环路检测 难度:中等
def find_beginning(head): fast = head slow = head meet = None while fast and fast.next is not None: fast = fast.next.next slow = slow.next if fast == slow: meet = fast break if meet is None or meet.next is None: return None while head and meet: if head == meet: return meet head = head.next meet = meet.next return None
6.
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
- leetcode 第19题. 删除链表的倒数第N个节点 难度:中等
- 面试题22. 链表中倒数第k个节点 难度:简单
- 面试题 02.02. 返回倒数第 k 个节点
def remove_nth(lst, n): assert n<=lst.length and n > 0 fast = lst.head while n > 0: fast = fast.next n = n - 1 if fast == None: return head slow = lst.head while fast.next is not None: fast = fast.next slow = slow.next result = slow.next slow.next = slow.next.next lst.length = lst.length - 1 return result
7. 把链表从中间切开成两个链表
def split(head): if (head is None): return slow = head fast = head front_last_node = slow while (fast is not None): # 先找到中间节点 front_last_node = slow slow = slow.next fast = fast.next.next if fast.next is not None else None front_last_node.next = None front = head back = slow return (front, back)
8. 合并两个有序的链表
- leetcode 第21题. 合并两个有序链表 难度:简单
# iteratively # O(m + n) def mergeTwoLists1(l1, l2): dummy = cur = Node(0) while l1 and l2: if l1.value < l2.value: cur.next = l1 l1 = l1.next else: cur.next = l2 l2 = l2.next cur = cur.next cur.next = l1 or l2 return dummy.next
9. 找到两个链表的第一个公共节点
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
def getIntersectionNode(headA, headB): curA, curB = headA, headB lenA, lenB = 0, 0 while curA is not None: lenA += 1 curA = curA.next while curB is not None: lenB += 1 curB = curB.next curA, curB = headA, headB if lenA > lenB: for i in range(lenA-lenB): curA = curA.next elif lenB > lenA: for i in range(lenB-lenA): curB = curB.next while curB != curA: curB = curB.next curA = curA.next return curA
10. 插入排序
- leetcode 第147题. 对链表进行插入排序 难度:中等
# O(n^2) def insertionSortList(head): dummy = Node(0) cur = head # pre is the sorted part # when see a new node, start from dummy # cur is the unsorted part while cur is not None: pre = dummy while pre.next is not None and pre.next.value < cur.value: pre = pre.next temp = cur.next # 插入 cur.next = pre.next pre.next = cur cur = temp return dummy.next
11. 对链表进行排序
- leetcode 第148题. 排序链表 难度:中等
# Merge sort的思想 def sortList(head): if head is None or head.next is None: return head mid = getMiddle(head) rHead = mid.next mid.next = None return merge(sortList(head), sortList(rHead)) def merge(lHead, rHead): dummyNode = dummyHead = Node(0) while lHead and rHead: if lHead.value < rHead.value: dummyHead.next = lHead lHead = lHead.next else: dummyHead.next = rHead rHead = rHead.next dummyHead = dummyHead.next if lHead: dummyHead.next = lHead elif rHead: dummyHead.next = rHead return dummyNode.next def getMiddle(head): if head is None: return head slow = head fast = head while fast.next and fast.next.next: slow = slow.next fast = fast.next.next return slow
12. 分隔链表
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
- leetcode 第86题. 分隔链表 难度:中等
- 面试题 02.04. 分割链表 难度:中等
def partition(head, x): left_head = Node(None) # head of the list with nodes values < x right_head = Node(None) # head of the list with nodes values >= x left = left_head # attach here nodes with values < x right = right_head # attach here nodes with values >= x # traverse the list and attach current node to left or right nodes while head: if head.value < x: left.next = head left = left.next else: # head.val >= x right.next = head right = right.next head = head.next right.next = None # set tail of the right list to None left.next = right_head.next # attach left list to the right return left_head.next # head of a new partitioned list
13.
- 面试题24. 反转链表
- leetcode 第206题. 反转链表 难度:简单
def reverse(lst): head = lst.head # 哨兵节点 result = None current = head.next nxt = None while current is not None: nxt = current.next # 事先存储元素 current.next = result # 指向前一个元素 result = current # 赋值给新链表 current = nxt head.next = result
14.
- leetcode 第92题. 反转链表 II 难度:中等
def reverseBetween(head, m, n): if m == n: return head dummyNode = Node(0) dummyNode.next = head pre = dummyNode for i in range(m - 1): # 先定位到m位置 pre = pre.next # reverse the [m, n] nodes result = None current = pre.next # 再使用第一种方法反转 for i in range(n - m + 1): nxt = current.next current.next = result result = current current = nxt pre.next.next = current pre.next = result return dummyNode.next
15.
- leetcode 第25题. K 个一组翻转链表 难度:困难
def reverseKGroup(head, k): if head is None or k < 2: return head next_head = head for i in range(k - 1): next_head = next_head.next if next_head is None: return head ret = next_head # 先用ret记录住当前第一个反转列表的位置,也是整个反转列表的开头,反转完返回它即可。 current = head while next_head: tail = current # 先用tail记录住当前current的位置 prev = None for i in range(k): if next_head: next_head = next_head.next nxt = current.next current.next = prev prev = current current = nxt tail.next = next_head or current return ret
16.
- leetcode 第234题. 回文链表 难度:简单
- 面试题 02.06. 回文链表 难度:简单
# 找到中间节点和中间节点的前一个节点 def isPalindrome(head): rev = None slow = fast = head while fast and fast.next: fast = fast.next.next rev, rev.next, slow = slow, rev, slow.next if fast: slow = slow.next while rev and rev.value == slow.value: slow = slow.next rev = rev.next return not rev
17.
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.
- leetcode 第83题. 删除排序链表中的重复元素
def deleteDuplicates(head): if head == None: return head node = head while node.next: if node.value == node.next.value: node.next = node.next.next else: node = node.next return head
18.
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.
- leetcode 第82题. 删除排序链表中的重复元素 II 难度:中等
def deleteDuplicates2(head): dummy = pre = Node(0) dummy.next = head while head and head.next: if head.value == head.next.value: while head and head.next and head.value == head.next.value: head = head.next head = head.next pre.next = head else: pre = pre.next head = head.next return dummy.next