这里写目录标题
排序
147-对链表进行插入排序
对链表进行插入排序。
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
插入排序算法:
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
方法一:原地插入排序(执行用时: 144 ms)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* insertionSortList(ListNode* head) {
auto dummy = new ListNode();
dummy->next = head;
auto lastSorted = head;
auto curr = head->next;
while(curr){
auto nxt = curr->next;
// 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
if(lastSorted->val <= curr->val){
lastSorted = lastSorted->next;
// 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
}else{
// lastSorted.val > curr.val
auto prev = dummy;
while(prev->next->val < curr->val){
// 查询插入点
prev = prev->next;
}
curr->next = prev->next; // 将“待处理节点”curr的下一个节点为插入点的下一个节点
prev->next = curr; // 插入点的前一个节点连接到“待处理节点”curr
lastSorted->next = nxt; // 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点,使得整个链表重新连接起来
}
curr = nxt; // 开始堆下一个节点进行插入排序
}
return dummy->next;
}
};
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
dummy = ListNode()
dummy.next = head
lastSorted = head
curr = head.next
while curr:
nxt = curr.next
# 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
if lastSorted.val <= curr.val:
lastSorted = lastSorted.next
# 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
else: # lastSorted.val > curr.val
prev = dummy
while prev.next.val < curr.val: # 查询插入点
prev = prev.next
curr.next = prev.next # 将“待处理节点”curr的下一个节点为插入点的下一个节点
prev.next = curr # 插入点的前一个节点连接到“待处理节点”curr
lastSorted.next = nxt # 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点,使得整个链表重新连接起来
curr = nxt # 开始堆下一个节点进行插入排序
return dummy.next
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
if not head:
return head
dummyHead = ListNode(0)
dummyHead.next = head
lastSorted = head
curr = lastSorted.next
while curr:
# 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
if lastSorted.val <= curr.val:
lastSorted = lastSorted.next
# 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
if lastSorted.val > curr.val:
prev = dummyHead
while prev.next.val <= curr.val: # 查询插入点
prev = prev.next
lastSorted.next = curr.next # 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点
curr.next = prev.next # 将“待处理节点”curr的下一个节点为插入点的下一个节点
prev.next = curr # 插入点的前一个节点连接到“待处理节点”curr
curr = lastSorted.next
return dummyHead.next
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
dummy = ListNode()
dummy.next = head
lastSorted = head
curr = head.next
while curr:
nxt = curr.next
# 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
if lastSorted.val <= curr.val:
lastSorted = lastSorted.next
# 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
if lastSorted.val > curr.val:
prev = dummy
temp = prev.next # 此处不能用temp = head, 因为由于后面元素插入排序的原因, 最初的head节点有可能已经不是头节点了
while temp.val < curr.val: # 查询插入点
prev = prev.next
temp = temp.next
curr.next = prev.next # 将“待处理节点”curr的下一个节点为插入点的下一个节点
prev.next = curr # 插入点的前一个节点连接到“待处理节点”curr
lastSorted.next = nxt # 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点,使得整个链表重新连接起来
curr = nxt # 开始堆下一个节点进行插入排序
return dummy.next
方法二(错误重复操作,导致时间复杂度增加;执行用时: 1060 ms):
if lastSorted.val < curr.val
条件里没有将curr指针移动到next,导致curr停留在原来的位置,导致重复插入排序;
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
dummy = ListNode()
dummy.next = head
lastSorted = head
curr = head.next
while curr:
if lastSorted.val < curr.val:
lastSorted = lastSorted.next
# curr = curr.next # 如果没有此步骤,会导致重复进行插入排序
else:
prev = dummy
while prev.next.val < curr.val:
prev = prev.next
nxt = curr.next
curr.next = prev.next
prev.next = curr
lastSorted.next = nxt
curr = nxt
return dummy.next
方法三:开辟新的链表(执行用时: 1336 ms)
148-排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
示例 3:
输入:head = []
输出:[]
提示:
- 链表中节点的数目在范围 [ 0 , 5 ∗ 1 0 4 ] [0, 5 * 10^4] [0,5∗104] 内
- − 1 0 5 < = N o d e . v a l < = 1 0 5 -10^5 <= Node.val <= 10^5 −105<=Node.val<=105
进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
方法一:最小堆
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: ListNode) -> ListNode:
heap = []
curr = head
while curr:
heapq.heappush(heap, curr.val)
curr = curr.next
dummy = ListNode()
prev = dummy
while heap:
node = ListNode(heapq.heappop(heap))
prev.next = node
prev = prev.next
return dummy.next
方法二:归并排序
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
# 归并排序
def sortList(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
left_end = self.find_left_end(head)
new_head = left_end.next
left_end.next = None
left = self.sortList(head)
right = self.sortList(new_head)
return self.merged(left, right)
# 快慢指针查找链表中点(左半链表的结尾节点)
def find_left_end(self, head):
if head is None or head.next is None:
return head
slow,fast = head, head
while fast and fast.next and fast.next.next:
slow=slow.next
fast=fast.next.next
return slow
# 合并有序链表
def merged(self, left, right):
dummy = ListNode()
prev = dummy
while left and right:
if left.val < right.val:
prev.next = left
left = left.next
else:
prev.next = right
right = right.next
prev = prev.next
prev.next = left if left else right
return dummy.next
删除
剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
注意:此题对比原题有改动
示例 1:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
说明:
- 题目保证链表中节点的值互不相同
- 若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode* dummy = new ListNode();
dummy->next = head;
auto prev = dummy;
auto curr = head;
while(curr){
auto nxt = curr->next;
if(curr->val == val){
prev->next = nxt;
}
prev = prev->next;
curr = nxt;
}
return dummy->next;
}
};
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
dummy = ListNode()
dummy.next = head
prev = dummy
curr = head
while curr:
nxt = curr.next
if curr.val == val:
prev.next = nxt
prev = prev.next
curr = nxt
return dummy.next
83-删除排序链表中的重复元素
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。
返回同样按升序排列的结果链表。
示例 1:
输入:head = [1,1,2]
输出:[1,2]
示例 2:
输入:head = [1,1,2,3,3]
输出:[1,2,3]
提示:
- 链表中节点数目在范围 [0, 300] 内
- -100 <= Node.val <= 100
- 题目数据保证链表已经按升序排列
方法一:一次遍历
由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。
具体地,我们从指针 cur 指向链表的头节点,随后开始对链表进行遍历。如果当前 cur 与 cur.next 对应的元素相同,那么我们就将cur.next 从链表中移除;否则说明链表中已经不存在其它与 cur 对应的元素相同的节点,因此可以将cur 指向 cur.next。
当遍历完整个链表之后,我们返回链表的头节点即可。
细节:当我们遍历到链表的最后一个节点时,cur.next 为空节点,如果不加以判断,访问 cur.next 对应的元素会产生运行错误。因此我们只需要遍历到链表的最后一个节点,而不需要遍历完整个链表。
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head:
return head
cur = head
while cur.next:
if cur.val == cur.next.val:
cur.next = cur.next.next
else:
cur = cur.next
return head
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(!head){
return head;
}
auto curr = head;
while(curr->next){
if(curr->val == curr->next->val){
curr->next = curr->next->next;
}else{
curr = curr->next;
}
}
return head;
}
};
# 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:
dummy = ListNode()
dummy.next = head
prev = dummy
curr = head
while curr:
# 如果有重复节点,则跳过当前的重复节点,使得curr指向当前重复元素的最后一个重复节点位置
while curr.next and curr.val == curr.next.val:
curr = curr.next
# 如果经过上一步的while操作,prev的next不再指向原来指向的curr,则说明curr节点有重复元素,
# 则将prev.next指向最后一个重复节点curr的下一个节点,进入下一个循环
if prev.next != curr:
prev.next = curr
# 如果经过上一步的while操作,prev的next依旧指向原来指向的curr,则说明curr节点没有重复元素,则将prev、curr分别向后移动一位,分析下一个元素
else:
prev = prev.next
curr = curr.next
return dummy.next
82-删除排序链表中的重复元素 II
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。
返回同样按升序排列的结果链表。
示例 1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3]
输出:[2,3]
提示:
- 链表中节点数目在范围 [0, 300] 内
- -100 <= Node.val <= 100
- 题目数据保证链表已经按升序排列
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
auto dummy = new ListNode();
dummy->next = head;
auto prev = dummy;
auto curr = head;
while(curr){
// 如果有重复节点,则跳过当前的重复节点,使得curr指向当前重复元素的最后一个节点位置
while(curr->next && curr->val == curr->next->val){
curr = curr->next;
}
// 如果经过上一步的while操作,prev的next不再指向原来指向的curr,则说明curr节点有重复元素,
// 则将prev.next指向最后一个重复节点curr的下一个节点【相当于跳过了剩余的最后一个重复节点】,curr移动到下一个节点位置
if(prev->next != curr){
prev->next = curr->next;
curr = curr->next;
}
// 如果经过上一步的while操作,prev的next依旧指向原来指向的curr,则说明curr节点没有重复元素,则将prev、curr分别向后移动一位,分析下一个元素
else{
prev = prev->next;
curr = curr->next;
}
}
return dummy->next;
}
};
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def deleteDuplicates(self, head):
dummy = ListNode()
dummy.next = head
prev = dummy
curr = head
while curr:
# 如果有重复节点,则跳过当前的重复节点,使得curr指向当前重复元素的最后一个节点位置
while curr.next and curr.val == curr.next.val:
curr = curr.next
# 如果经过上一步的while操作,prev的next不再指向原来指向的curr,则说明curr节点有重复元素,
# 则将prev.next指向最后一个重复节点curr的下一个节点【相当于跳过了剩余的最后一个重复节点】,curr移动到下一个节点位置
if prev.next != curr:
prev.next = curr.next
curr = curr.next
# 如果经过上一步的while操作,prev的next依旧指向原来指向的curr,则说明curr节点没有重复元素,则将prev、curr分别向后移动一位,分析下一个元素
else:
prev = prev.next
curr = curr.next
return dummy.next
面试题 02.03-删除中间节点
若链表中的某个节点,既不是链表头节点,也不是链表尾节点,则称其为该链表的「中间节点」。
假定已知链表的某一个中间节点,请实现一种算法,将该节点从链表中删除。
例如,传入节点 c(位于单向链表 a->b->c->d->e->f 中),将其删除后,剩余链表为 a->b->d->e->f
示例:
输入:节点 5 (位于单向链表 4->5->1->9 中)
输出:不返回任何数据,从链表中删除传入的节点 5,使链表变为 4->1->9
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
};
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
node.val = node.next.val
node.next = node.next.next
19-删除链表的倒数第 N 个结点【剑指 Offer II 021. 删除链表的倒数第 n 个结点】
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
- 链表中结点的数目为 sz
- 1 <= sz <= 30
- 0 <= Node.val <= 100
- 1 <= n <= sz
方法一:多指针
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
auto dummy = new ListNode();
dummy->next = head;
auto prev = dummy;
auto slow = head;
auto fast = head;
for(int i = 0; i < n; i++){
fast = fast->next;
}
while(fast){
fast = fast->next;
slow = slow->next;
prev = prev->next;
}
prev->next = slow->next;
return dummy->next;
}
};
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
dummy = ListNode()
dummy.next = head
prev = dummy
slow = head
fast = head
for _ in range(n):
fast = fast.next
# 将fast指针移动到链表尾节点,此时slow位于倒数第n个节点,prev位于倒数第n个节点的前一个节点
while fast:
fast = fast.next
slow = slow.next
prev = prev.next
prev.next = slow.next
return dummy.next
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
dummy = ListNode()
dummy.next = head
slow = head
fast = head
prev = dummy
for _ in range(n - 1):
fast = fast.next
while fast and fast.next:
prev = prev.next
slow = slow.next
fast = fast.next
prev.next = slow.next
return dummy.next
合并
21-合并两个有序链表 【剑指 Offer 25. 合并两个排序的链表】
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
- 两个链表的节点数目范围是 [0, 50]
- -100 <= Node.val <= 100
- l1 和 l2 均按 非递减顺序 排列
参考归并排序中的归并步骤
方法一:迭代法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
auto dummy = new ListNode();
auto prev = dummy;
auto curr01 = l1;
auto curr02 = l2;
while(curr01 && curr02){
if(curr01->val < curr02->val){
prev->next = curr01;
prev = prev->next;
curr01 = curr01->next;
}else{
prev->next = curr02;
prev = prev->next;
curr02 = curr02->next;
}
}
while(curr01){
prev->next = curr01;
prev = prev->next;
curr01 = curr01->next;
}
while(curr02){
prev->next = curr02;
prev = prev->next;
curr02 = curr02->next;
}
return dummy->next;
}
};
# 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: ListNode, l2: ListNode