练习 2 —— 顺序表/链表力扣刷题(中等难度)
1. 反转链表 II
力扣原题:https://leetcode.cn/problems/reverse-linked-list-ii/
题目描述
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例 1
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2
输入:head = [5], left = 1, right = 1
输出:[5]
1.1 解题方法 1
问题分析 拆解成为 子串 + 反转 + 拼凑 的三个问题
这里假设我们小伙伴们都刷过 “反转链表” 那道题(https://leetcode.cn/problems/reverse-linked-list/)我们比较这两道题就可以发现其中的 "反转部分其实是一样的,只是我们这个时候需要考虑的是 子链表反转
。
所以我们可以大大方方地把前面一道的代码 copy 下来,用来做这道题。
所以接下来的问题就变成了求子链表的问题,我们把原来的链表拆分成三个子链表。
首先,对于第一个子链表,我们按照原顺序抄袭一下即可,无需其他操作。
其次,中间的反转环节,我们参照(copy)之前的那道题即可;
最后,我们拼凑一下即可,注意移动 p1 指针到尾部再进行拼接。
/**
* 以下方法自己编写,复制发布博客等请注明出处 https://blog.csdn.net/smileyan9
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
// 最终答案链表
ListNode* list1 = new ListNode();
ListNode* p1 = list1;
ListNode* start = head;
// 复制left部分
for (int i = 0; i < left - 1; i++) {
p1 -> next = start;
p1 = p1 -> next;
start = start -> next;
}
// 反转中间的部分
ListNode* p2 = nullptr;
for (int i = left - 1; i < right; i++) {
ListNode* next = start -> next;
start -> next = p2;
p2 = start;
start = next;
}
// 复制right部分
p1 -> next = p2;
while (p1 -> next) {
p1 = p1 -> next;
}
p1 -> next = start;
return list1 -> next;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
2. 分隔链表
问题描述
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
示例 1
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]
示例 2
输入:head = [2,1], x = 2
输出:[1,2]
问题分析
首先推荐尝试一下不考虑空间复杂度的做法,也就是先新建两个链表,每次都 new Node 来创建新的结点并添加到链表中。这种方法比较简单易懂。
接着我们看看如果不创建新的空间应该怎么做,以下代码来自力扣的官方网站答题,比较简单明了。
重点在于循环中的内容,每次判断完大小以后,分别添加到两个链表中,注意这里没有新建空间,而是直接用 next 指针指向它。
另外就是头结点的使用非常关键,head 的作用在最后拼接的时候体现出来。
/**
* 以下源码来自力扣官网:https://leetcode.cn/problems/partition-list/submissions/
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* small = new ListNode(0);
ListNode* smallHead = small;
ListNode* large = new ListNode(0);
ListNode* largeHead = large;
while (head != nullptr) {
if (head -> val < x) {
small -> next = head;
small = small -> next;
} else {
large -> next = head;
large = large -> next;
}
head = head->next;
}
large -> next = nullptr;
small -> next = largeHead -> next;
return smallHead -> next;
}
};
3. 总结
本节介绍链表的两个中等难度的题解,通过这两道题应该清楚链表的基本操作,比如什么时候该 next 移动,什么时候借助中间的 tmp 记录一下 next 完成操作以后再 :“指回来”。
Smileyan
2023.04.30 17:21