1.0707. 设计链表
1.1 题目大意
要求:设计实现一个链表,需要支持以下操作:
get(index)
:获取链表中第index
个节点的值。如果索引无效,则返回-1
。addAtHead(val)
:在链表的第一个元素之前添加一个值为val
的节点。插入后,新节点将成为链表的第一个节点。addAtTail(val)
:将值为val
的节点追加到链表的最后一个元素。addAtIndex(index, val)
:在链表中的第index
个节点之前添加值为val
的节点。如果index
等于链表的长度,则该节点将附加到链表的末尾。如果index
大于链表长度,则不会插入节点。如果index
小于0
,则在头部插入节点。deleteAtIndex(index)
:如果索引index
有效,则删除链表中的第index
个节点。
说明:
- 所有
val
值都在 [1,1000] 之内。 - 操作次数将在 [1,1000] 之内。
- 请不要使用内置的
LinkedList
库。
示例:
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); // 链表变为 1 -> 2 -> 3
linkedList.get(1); // 返回 2
linkedList.deleteAtIndex(1); // 现在链表是 1-> 3
linkedList.get(1); // 返回 3
运行 :
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self.head = None
def get(self, index):
if not self.head:
return -1
cur = self.head
for i in range(index):
if cur.next:
cur = cur.next
else:
return -1
return cur.val if cur else -1
def addAtHead(self, val):
new_node = ListNode(val)
new_node.next = self.head
self.head = new_node
def addAtTail(self, val):
new_node = ListNode(val)
if not self.head:
self.head = new_node
return
curr = self.head
while cur.next:
cur = cur.next
cur.next = new_node
def addAtIndex(self, index, val):
if index == 0:
self.addAtHead(val)
return
cur = self.head
for i in range(index - 1):
if cur:
cur = cur.next
else:
return
if not cur:
return
new_node = ListNode(val)
new_node.next = cur.next
cur.next = new_node
def deleteAtIndex(self, index):
if index == 0:
if self.head:
self.head = self.head.next
return
cur = self.head
for i in range(index - 1):
if cur:
cur = cur.next
else:
return
if not cur or not cur.next:
return
cur.next = cur.next.next
2.0206. 反转链表
2.1 题目大意
描述:给定一个单链表的头节点 head
。
要求:将该单链表进行反转。可以迭代或递归地反转链表。
说明:
- 链表中节点的数目范围是 [0,5000]。
- −5000≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤5000。
示例:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
解释
翻转前 1->2->3->4->5->NULL
反转后 5->4->3->2->1->NULL
运行 :
# Definition for singly-linked list.
class ListNode(object):
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution(object):
def reverseList(self, head):
cur, pre = head, None
while cur:
tmp = cur.next # 暂存后继节点 cur.next
cur.next = pre # 修改 next 引用指向
pre = cur # pre 暂存 cur
cur = tmp # cur 访问下一节点
return pre
3.0203. 移除链表元素
3.1 题目大意
描述:给定一个链表的头节点 head
和一个值 val
。
要求:删除链表中值为 val
的节点,并返回新的链表头节点。
说明:
- 列表中的节点数目在范围 [0,104] 内。
- 1≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤50。
- 0≤𝑣𝑎𝑙≤50。
示例:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
输入:head = [], val = 1
输出:[]
运行 :
# Definition for singly-linked list.
class ListNode(object):
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution(object):
def removeElements(self, head, val):
dummy = ListNode(0) #虚拟的头节点,以便简化删除操作
dummy.next = head #虚拟头节点指向真正的头节点
cur_Node = dummy #将cur_Node赋值为虚拟头节点
while cur_Node.next: #将cur_Node的下一节点作为判断依据
if cur_Node.next.val == val:
cur_Node.next = cur_Node.next.next
else:
cur_Node = cur_Node.next
return dummy.next #注意,这里返回的是真实的头节点
1.0328. 奇偶链表
1.1 题目大意
描述:给定一个单链表的头节点 head
。
要求:将链表中的奇数位置上的节点排在前面,偶数位置上的节点排在后面,返回新的链表节点。
说明:
- 要求空间复杂度为 𝑂(1)。
- 𝑛 等于链表中的节点数。
- 0≤𝑛≤104。
- −106≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤106。
示例:
输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]
输入: head = [2,1,3,5,6,4,7]
输出: [2,3,6,7,1,5,4]
运行:
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def oddEvenList(self, head):
odd = p1 = ListNode() #奇索引表【头节点】
even = p2 = ListNode() #偶索引表【头节点】
p1.next = p2.next = None #先让头节点都指向None
cur = head #head指向的就是第一个数据节点
count=1 #表示索引号,控制奇偶节点剥离
while cur:
temp=cur.next #temp是临时保存cur下一个节点的,否则会丢失后续节点
if count % 2 == 0: #分析索引的奇偶性,整个if语句实现节点剥离和插入到新表
cur.next = p1.next
p1.next = cur
p1 = cur
else:
cur.next = p2.next
p2.next = cur
p2 = cur
cur = temp #cur移动到后续未剥离的节点序列的第一个节点
count+=1 #每处理完一个,cur已经移动到下一个节点了,则索引+1
p2.next = odd.next #将【奇索引表】的尾巴和【偶索引表】的第一个节点连接
return even.next #返回重新排列后的链表的第一个数据节点
2.0234. 回文链表
2.1 题目大意
描述:给定一个链表的头节点 head
。
要求:判断该链表是否为回文链表。
说明:
- 链表中节点数目在范围 [1,105] 内。
- 0≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤9。
示例:
输入:head = [1,2,2,1]
输出:True
输入:head = [1,2]
输出:False
运行:
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def isPalindrome(self, head):
# 先把链表内容复制进入数组中
nums = [] # 创建一个空数组/列表,用来先讲链表复制到数组中
cur = head # 创建 cur 指向 head
while cur is not None: # 如果指针不为空
nums.append(cur.val) # 将当前指针所指的 value 复制添加进入数组
cur = cur.next # 更新 cur 指针
# 直接比较原数组/列表和反转数组/列表
return nums == nums[::-1]
3.0138. 复制带随机指针的链表
3.1 题目大意
描述:给定一个链表的头节点 head
,链表中每个节点除了 next
指针之外,还包含一个随机指针 random
,该指针可以指向链表中的任何节点或者空节点。
要求:将该链表进行深拷贝。返回复制链表的头节点。
说明:
- 0≤𝑛≤1000。
- −104≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤104。
Node.random
为null
或指向链表中的节点。
示例:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
运行:
# Definition for a Node.
# class Node:
# def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
# self.val = int(x)
# self.next = next
# self.random = random
class Solution(object):
def copyRandomList(self, head):
if not head:
return None
node_map={}
cur=head
while cur:
node_map[cur]=Node(cur.val)
cur=cur.next
cur=head
while cur:
if cur.next:
node_map[cur].next=node_map[cur.next]
if cur.random:
node_map[cur].random=node_map[cur.random]
cur=cur.next
return node_map[head]
1.0147. 对链表进行插入排序
1.1 题目大意
描述:给定链表的头节点 head
。
要求:对链表进行插入排序。
说明:
- 插入排序算法:
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
- 列表中的节点数在 [1,5000] 范围内。
- −5000≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤5000。
示例:
输入: head = [4,2,1,3]
输出: [1,2,3,4]
输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]
运行:
# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def insertionSortList(self, head):
if head == None or head.next == None:
return head
dummy = ListNode(0)
dummy.next = head
last_sorted = head # 已排序部分的最后一个节点
cur = head.next # 待插入的
while cur:
if last_sorted.val <= cur.val:
last_sorted = last_sorted.next
else:
# 从链表的头节点开始往后遍历链表中的节点,寻找插入 cur 的位置
prev = dummy
while prev.next.val <= cur.val:
prev = prev.next
# 插入操作:令 prev 为插入 cur 的位置的前一个节点
last_sorted.next = cur.next
cur.next = prev.next
prev.next = cur
cur = last_sorted.next
return dummy.next
2.0021. 合并两个有序链表
2.1 题目大意
描述:给定两个升序链表的头节点 list1
和 list2
。
要求:将其合并为一个升序链表。
说明:
- 两个链表的节点数目范围是 [0,50]。
- −100≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤100。
list1
和list2
均按 非递减顺序 排列
示例:
输入:list1 = [1,2,4], list2 = [1,3,4]
输出:[1,1,2,3,4,4]
输入:list1 = [], list2 = []
输出:[]
运行:
# 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,l1,l2):
# 创建一个哑节点作为合并链表的头部
head=ListNode(0)
# 创建一个指针用于遍历合并链表
cur=head
# 当两个链表同时都有节点时
while l1 and l2:
# 如果链表l1节点的值>=l2节点的值,将l2当前节点赋予current的下一个节点
# 将l2指向其下一个节点
if l1.val>=l2.val:
cur.next=l2
l2=l2.next
else:
cur.next=l1
l1=l1.next
cur=cur.next
# 将剩余部分连接到合并链表的末尾
cur.next= l1 if l1 else l2
return head.next
3.0148. 排序链表
3.1 题目大意
描述:给定链表的头节点 head
。
要求:按照升序排列并返回排序后的链表。
说明:
- 链表中节点的数目在范围 [0,5∗104] 内。
- −105≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤105。
示例:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
运行:
class Solution:
def bubbleSort(self, head):
node_i = head
tail = None
# 外层循环次数为 链表节点个数
while node_i:
node_j = head
while node_j and node_j.next != tail:
if node_j.val > node_j.next.val:
# 交换两个节点的值
node_j.val, node_j.next.val = node_j.next.val, node_j.val
node_j = node_j.next
# 尾指针向前移动 1 位,此时尾指针右侧为排好序的链表
tail = node_j
node_i = node_i.next
return head
def sortList(self, head):
return self.bubbleSort(head)
1.0141. 环形链表
1.1 题目大意
描述:给定一个链表的头节点 head
。
要求:判断链表中是否有环。如果有环则返回 True
,否则返回 False
。
说明:
- 链表中节点的数目范围是 [0,10^4]。
- −105≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤105。
pos
为-1
或者链表中的一个有效索引。
示例:
输入:head = [3,2,0,-4], pos = 1
输出:True
解释:链表中有一个环,其尾部连接到第二个节点。
输入:head = [1,2], pos = 0
输出:True
解释:链表中有一个环,其尾部连接到第一个节点。
运行:
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
# 快慢双指针
class Solution(object):
def hasCycle(self, head):
if not head:
return None
slow, quick = head, head.next # 快慢指针
while slow!=quick:
if quick==None or quick.next==None: # 快指针或快指针的next为空,则遍历至链表尾部,非环形链表
return False
else:
slow = slow.next # 慢指针走一步
quick = quick.next.next # 快指针走两步
return True # 快慢指针相遇,则为环形链表
2.0142. 环形链表 II
2.1 题目大意
描述:给定一个链表的头节点 head
。
要求:判断链表中是否有环,如果有环则返回入环的第一个节点,无环则返回 None
。
说明:
- 链表中节点的数目范围在范围 [0,104] 内。
- −105≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤105。
pos
的值为-1
或者链表中的一个有效索引。
示例:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
运行:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head):
fast = head
slow = head
#根据快指针能不能指到空来判断这是单向链表还是带有环的链表
while fast and fast.next:
fast = fast.next.next #快指针一次走两格
slow = slow.next #慢指针一次走一格
if fast == slow: #如果两个指针能够相遇,就说明是有环的
#确定有环之后,就要在相遇的节点新设一个节点index1
#在头节点新设一个节点index2
#两者同步走,相遇处即为入环口
index1 = fast
index2 = head
#遍历结束条件为两者相遇
while index1 != index2:
index1 = index1.next
index2 = index2.next
#相遇之后返回节点
return index1
return None
3.0019. 删除链表的倒数第 N 个结点
3.1 题目大意
描述:给定一个链表的头节点 head
。
要求:删除链表的倒数第 n
个节点,并且返回链表的头节点。
说明:
- 要求使用一次遍历实现。
- 链表中结点的数目为
sz
。 - 1≤𝑠𝑧≤30。
- 0≤𝑁𝑜𝑑𝑒.𝑣𝑎𝑙≤100。
- 1≤𝑛≤𝑠𝑧。
示例:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
运行:
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def removeNthFromEnd(self, head, n):
dummy = ListNode(0) # 创建一个虚拟头结点,值为0
dummy.next = head # 将虚拟头结点指向原链表的头结点
fast = dummy # 快指针初始指向虚拟头结点
slow = dummy # 慢指针初始指向虚拟头结点
for i in range(n+1): # 快指针先向前移动n步(包括虚拟头结点)
fast = fast.next
while fast: # 同时移动快指针和慢指针,直到快指针到达链表末尾
fast = fast.next # 快指针每次移动一步
slow = slow.next # 慢指针每次移动一步
slow.next = slow.next.next # 删除倒数第n个节点,将慢指针指向的节点的next指针指向下一个节点的next指针
return dummy.next # 返回链表的头结点