二 链表
1.1 基础
链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
-
链表结构: 常用两种---------无头单链 和 有头(保护节点)双链
-
链表基本操作
- 创建链表
# 链节点类
class ListNode:
def __init__(self, val = 0, next = None):
self.val = val
self.next = next
# 创建单链表
class CreatLinkedList:
def creat(self, data):
self.head = ListNode(0)
cur = self.head
for d in data:
node = ListNode(d)
cur.next = node
cur = cur.next
- 查找
def serch(self, target):
cur = self.head
while cur:
if cur.val == target:
return cur.val
cur = cur.next
- 在
i
前处插入节点
def InsertInside(self, val, i):
count = 0
cur = self.head
while cur and count < i - 1:
cur = cur.next
count += 1
if not cur: return "Error"
node = ListNode(val)
node.next = cur.next
cur.next = node
- 删除
i
处节点
def removeInside(self, i):
count = 0
cur = self.head
while cur and count < i - 1:
cur = cur.next
count += 1
if not cur: return "Error"
cur.next = cur.next.next
- 总结
- 链表使用一组任意的存储单元(可以是连续的,也可以是不连续的),来存储一组具有相同类型的数据
- 链表最大的优点在于可以灵活的添加和删除元素。链表进行访问元素、改变元素操作的时间复杂度为O(n) ,进行头部插入、尾部插入、头部删除、尾部删除元素操作的时间复杂度是O(1) ,普通情况下进行插入、删除元素操作的时间复杂度为O(n) 。
1.2 题目
1.2.1 203 . 移除链表元素
- 思路: 建立头部保护结点
dummy
>>> 遍历链表寻找符合val
值节点 >>> 删除继续遍历 >>> 注意: 遍历链表保证cur.next
非空, 因为需要判断cur.next.val
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
dummy = ListNode(next = head)
cur = dummy
while cur.next:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return dummy.next
1.2.2 61 . 旋转链表
- 思路: 首先计算出链表长度
length
>>> 使得cur
移动到尾部节点, 并将尾部节点与head
连接 >>> 找到端点 —length - k
处节点 >>> 建立新的尾部节点和头部节点即可
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if not head: return head
size = 0
cur = head
while cur.next: #计算村链表长度 :size + 1
size += 1
cur = cur.next
cur.next = head # 将原链表尾部与head先连接
k = k % (size + 1)
cur = head
for i in range(1, size + 1 - k): # 移动到 length - k 的位置
cur = cur.next
head = cur.next
cur.next = None
return head
1.2.3 206 . 反转链表
- 思路: 法一: 迭代法 >>> 先建立
pre
空节点, 并由head
指向空节点 >>> 遍历链表, 改变pre
,cur
指向节点, 并使得cur
对应节点指向pre
对应节点 >>> 完成反转
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
pre = None
cur = head
while cur:
temp = cur.next
cur.next = pre
pre = cur
cur = temp
return pre
- 法二 ---- 递归 :
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if head == None or head.next == None: #边界条件: 当前为空或下一为空
return head
cur = self.reverseList(head.next)
head.next.next = head # head.next 指向 head 即图中 5 > 4
head.next = None # 防止链表循环,需要将head.next设置为空
return cur #每次递归返回链表最后节点
1.2.4 21 . 合并两个有序链表
- 思路: 递归 >>> 当
l1
节点值较小时 , 其下一节点可由l1.next
与l2
求出 >>>>> 当某一链表为空 返回另一链表
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
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
1.2.5 148 . 排序链表
- 思路: 利用归并排序 >>> 快排和插排会超时 >>> 归并分为分割和合并两步骤 >>> 其中分割即设计快慢指针分为两个链表 >>> 合并即合并两个有序链表同上题思路
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:return head
#分割
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
left_head, right_head = head, slow.next
slow.next = None # 实现分割
#合并
def merge(left, right):
if not left:return right
if not right:return left
if left.val <= right.val:
left.next = merge(left.next, right)
return left
else:
right.next = merge(left, right.next)
return right
#递归排序
return merge(self.sortList(left_head), self.sortList(right_head))
1.2.6 82 . 删除排序链表中的重复元素 II
- 思路: 快慢指针 + 哑结点 >>>> 利用
slow
和fast
两个指针,fast
遍历链表,slow
指向非重复节点 >>>>fast
遇到重复节点, 移动到指向最后一节点 >>>> 利用slow.next = fast.next
跳过重复节点 >>> 无重复节点slow``fast
各移动一个位置
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
dummy = ListNode(next = head)
slow, fast = dummy, head
while fast:
while fast.next and fast.val == fast.next.val: #当出现重复节点 -> fast移动到重复节点末
fast = fast.next
if slow.next == fast: # slow下一节点仍为fast -> slow移动一位
slow = slow.next
else:
slow.next = fast.next # slow下一节点为重复节点 -> 跳过重复部分
fast = fast.next
return dummy.next
1.2.7 160 . 相交链表
- 思路: 双指针, 长短互补 >>> 利用
p1
,p2
指向两个链表头 >>> 在未找到相同节点前提下, 若其中一节点到达None
, 指向另一链表表头 >>> 弥补长度差距, 迟早相交 >>> 若无相同节点, 也会在同时指向None
时停下
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p1, p2 = headA, headB
while p1 != p2:
p1 = p1.next if p1 else headB
p2 = p2.next if p2 else headA
return p1