Leetcode学习之链表
链表
链表定义
链表是由一系列节点组成的元素集合,每个节点包括两部分,数据域**item
和指向下一个节点的指针next**。通过节点之间的互相连接,最终串联成一个链表
head ----> 86| | ----> 19| | ---->4| |---->12| |
创建
class Node:
def __init__(self,item):
self.item = item
self.next = None
a = Node(1)
b = Node(2)
c = Node(3)
a.next = b
b.next = c
print(a.next.item) # 2
print(a.next.next.item) # 3
print(a.next.next.next.item) # AttributeError: 'NoneType' object has no attribute 'item'
链表的逻辑结构与存储结构
逻辑结构:数据元素之间的逻辑 ,如:线性结构,树形结构,图结构
存储结构:
- 顺序存储:又称为顺序表,逻辑上相邻的元素物理位置也相邻
1 |
---|
2 |
3 |
4 |
5 |
- 链式存储:又称为单链表,逻辑上相邻的物理元素位置上不一定相邻
1 |
---|
2 |
3 |
4 |
5 |
- 索引存储
- 散列存储
单链表的定义
代码书写如下
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
带头节点的链表(书写方便)
头结点 | 结点1 | 结点2 | 结点3 | … | NULL |
---|---|---|---|---|---|
不带头结点的链表
结点1 | 结点2 | 结点3 | … | 结点10 | NULL |
---|---|---|---|---|---|
链表插入元素
带头结点插入
头插法创建代码
def create_linklist_head(li):
head = Node(li[0])
for element in li[1:]:
node = Node(element) # 创建新节点
node.next = head
head = node
return head
尾插法
def create_linklist_tail(li):
head = Node(li[0])
tail = head
for element in li[1:]:
node = Node(element) # 创建新节点
tail.next = node
tail = node
return head
特性
def print_linklist(lk):
while lk:
print(lk.item, end=',')
lk = lk.next
lk1 = create_linklist_head([1,2,3,6,8])
lk2 = create_linklist_tail([1,2,3,6,8])
print_linklist(
lk1
)
print('\n')
print_linklist(
lk2
)
8,6,3,2,1, # 头插法创建链表为倒序
1,2,3,6,8, # 尾插法创建链表为正序
链表节点
插入
# 第一步,将4与2连起来
p.next = curNode.next
# 第二步,将1与4连起来
cirNode.next = p
# -----------------
# 在第i个位置插入elem元素
def Insert(head, i ,elem):
assert i >=0
cur = head
while i !=0:
cur = cur.next
if not cur:
return False
temp = cur.next
cur.next = elem
elem.next = temp
return True
# 如下图演示
删除
p = curNode.next
# 第一步,把1与2连起来
curNode.next= curNode.next.next # curNode.next = p.next
# 第二步,把节点进行删除
del p
双链表
双链表的每个节点有两个指针,一个指向后一个节点,另一个指向前一个结点
class Node_double:
def __init__(self,item,next,prior):
self.item = item
self.next = None
self.prior = None
双链表的插入
p.next = curNode.next # 2 -->3
curNode.next.prior = p # 3 --> 2
p.prior = curNode # 2 -->1
curNode.next = p # 1 -->2
双链表的删除
p = curNode.next
curNode.next = p.next # 1 -->3
p.next.prior = curNode # 3-->1
del p
总结
顺序表(列表和数组)与链表比较
按元素查找
按下标查找
在某元素插入
删除某元素
特点
链表的插入和删除操作明显快于顺序表
链表的内存可以更灵活的分配
链表的链式结构存储对数据结构的树和图有很大的启发
练习
移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
输入:head = [], val = 1
输出:[]
输入:head = [7,7,7,7], val = 7
输出:[]
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
head = ListNode(next = head)
pre = head
while pre.next:
if pre.next.val == val:
pre.next = pre.next.next
else:
pre = pre.next
return head.next
旋转链表
给你一个链表的头节点 head
,旋转链表,将链表每个节点向右移动 k
个位置。
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例 2:
输入:head = [0,1,2], k = 4
输出:[2,0,1]
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if not head:
return None
length = 0
temp = head
while temp.next:
length += 1
temp = temp.next
temp.next = head
k = k % (length + 1)
temp = head
for i in range(length - k):
temp = temp.next
head = temp.next
temp.next = None
return head
习题:
合并两个有序链表
示例 1:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
if list1 is None:
return list2
elif list2 is None:
return list1
elif list1.val < list2.val:
list1.next = self.mergeTwoLists(list1.next, list2)
return list1
else:
list2.next = self.mergeTwoLists(list1,list2.next)
return list2
相交链表
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
图示两个链表在节点 c1
开始相交**:**
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal
- 相交的起始节点的值。如果不存在相交节点,这一值为0
listA
- 第一个链表listB
- 第二个链表skipA
- 在listA
中(从头节点开始)跳到交叉节点的节点数skipB
- 在listB
中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA
和 headB
传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal
- 相交的起始节点的值。如果不存在相交节点,这一值为0
listA
- 第一个链表listB
- 第二个链表skipA
- 在listA
中(从头节点开始)跳到交叉节点的节点数skipB
- 在listB
中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA
和 headB
传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
# 双指针
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
A, B = headA, headB
while A != B:
A = A.next if A else headB
B = B.next if B else headA
return A
删除排序链表中的重复元素
给定一个已排序的链表的头 head
, 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
示例 1:
示例2:
输入:head = [1,1,2,3,3]
输出:[1,2,3]
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head:
return head
pre = head
while pre.next:
if pre.val == pre.next.val:
pre.next = pre.next.next
else:
pre = pre.next
return head
参考