75. 颜色分类
给定一个包含红色、白色和蓝色、共 n
个元素的数组 nums
,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0
、 1
和 2
分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
题意:就是把数组按照顺序0-1-2排序,不能用sort,要原地排序
原地排序就是仅使用常数级的额外空间,要直接在数组上进行排序
思路:三指针,j代表0,k代表2,1不用排序,0和2排好了1自动就好了
class Solution {
public:
void sortColors(vector<int>& nums) {
int n=nums.size()-1;
// j:0
// k:2
for(int i=0,j=0,k=n;i<=k;){
if(nums[i]==0)swap(nums[i++],nums[j++]);// 当前数为0,扔到j
else if(nums[i]==2)swap(nums[i],nums[k--]);// 当前数为2,扔到k
else i++;
}
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O(
1
1
1)
80. 删除有序数组中的重复项 II
输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3。 不需要考虑数组中超出新长度后面的元素。
题意:原地删除重复出现的数组,超过2次的只能出现2次
思路:模拟一遍,数字最多出现2次,超过的不要
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k=0;
for(auto& x:nums){
//小于2个直接进
//左边第1个不等于当前数
//左边第2个不等于当前数
//再多就超了,不加入
if(k<2 || (nums[k-1]!=x || nums[k-2]!=x))nums[k++]=x;
}
return k;
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O( 1 1 1)
82. 删除排序链表中的重复元素 II
题意:删掉重复的元素
思路:模拟一遍,把前置指针放在2位置(如上图),找到下一个不重复的数字,再将p的next指向它(预知下两个2节点判断是否重复)
/**
* 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 p=dummy;
while(p->next){
auto t=p->next->next;
while(t && t->val==p->next->val)t=t->next;//预知下两个节点
if(t==p->next->next)p=p->next;// 没有重复,p向下走
else p->next=t;
}
return dummy->next;
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O( 1 1 1)
86. 分隔链表
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]
题意:小于x的节点放在x前面,大于x的放在x后面
思路:搞两个指针分别串一下小于x的和大于等于x的链表,然后合并下
/**
* 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* partition(ListNode* head, int x) {
auto lh=new ListNode(),rh=new ListNode();
auto lh1=lh,rh1=rh;
for(auto p=head;p;p=p->next)
if(p->val<x)lh=lh->next=p;
else rh=rh->next=p;
lh->next=rh1->next;
rh->next=nullptr;
return lh1->next;
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O( 1 1 1)
88. 合并两个有序数组
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
题意:将两个有序数组合并
思路:2个指针从2个数组从后向前放到nums1数组(从后向前是因为nums1后面没数据,不影响前面的数据)
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int k=m+n-1;
int x=m-1,y=n-1;
while(x>=0 && y>=0){
if(nums1[x]>=nums2[y])nums1[k--]=nums1[x--];
else nums1[k--]=nums2[y--];
}
while(y>=0)nums1[k--]=nums2[y--];
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O( 1 1 1)
125. 验证回文串
输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。
题意:去掉所有没用的东西,剩下来的字母小写化,判断是否是回文串
思路:全部变小写,串一遍,i从前面,j从后面,两边扫一遍
class Solution {
public:
bool isPalindrome(string s) {
for(int i=0,j=s.size()-1;i<j;i++,j--){
while(i<j && !isalnum(tolower(s[i])))i++;
while(i<j && !isalnum(tolower(s[j])))j--;
if(i<j && tolower(s[i])!=tolower(s[j]))return false;
}
return true;
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O( 1 1 1)
141. 环形链表
题意:判断是否有环
思路:快慢指针,最多转2圈就相遇
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(!head || !head->next)return false;
auto l=head,f=head;// f=head->next也行,无所谓,最后肯定相遇
while(f){
l=l->next,f=f->next;
if(!f)return false;
f=f->next;
if(l==f)return true;
}
return false;
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O( 1 1 1)
142. 环形链表 II
题意:找入环点
思路:快慢指针,这种环形链表,这种吊题基本都是找入环点,快慢指针,肯定能找到,数学证明此处不展开,自行搜索
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head)return NULL;
auto slow=head,fast=head;
while(fast){
slow=slow->next,fast=fast->next;
if(!fast)return NULL;
fast=fast->next;
if(slow==fast){
slow=head;
while(slow!=fast){//入环点,可以数学证明
slow=slow->next,fast=fast->next;
}
return fast;
}
}
return NULL;
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O( 1 1 1)
148. 排序链表
class Solution {
public:
ListNode* sortList(ListNode* head) {
// 创建一个虚拟头节点,方便操作
auto dummy = new ListNode(-1);
dummy->next = head;
// 计算链表的长度
int n = 0;
for (auto p = head; p; p = p->next) n++;
// 外层循环:归并的步长从1开始,每次翻倍
for (int i = 1; i < n; i *= 2) {
auto cur = dummy; // cur用于连接合并后的链表
// 内层循环:将链表分成大小为i的若干段,两两合并
for (int j = 1; j + i <= n; j += i * 2) {
auto p = cur->next, q = p; // p和q分别指向两段链表的起点
// 将q移动到第二段的起点
for (int k = 0; k < i; k++) q = q->next;
int l = 0, r = 0; // l和r分别记录两段链表的合并进度
// 合并两段链表
while (l < i && r < i && p && q) {
if (p->val <= q->val) {
cur->next = p;
p = p->next;
l++;
} else {
cur->next = q;
q = q->next;
r++;
}
cur = cur->next;
}
// 处理剩余未合并的节点
while (l < i && p) {
cur->next = p;
p = p->next;
l++;
cur = cur->next;
}
while (r < i && q) {
cur->next = q;
q = q->next;
r++;
cur = cur->next;
}
// 将cur连接到下一段的起点
cur->next = q;
}
}
// 返回排序后的链表头节点
return dummy->next;
}
};
- 时间复杂度:
O(n log n)
- 空间复杂度 😮(1)
151. 反转字符串中的单词
输入:s = "the sky is blue"
输出:"blue is sky the"
题意:反转整体顺序,然后反转局部顺序
思路:题目就是思路,模拟一下,当然先局部再整体也一样
class Solution {
public:
string reverseWords(string s) {
int k = 0; // 记录新字符串的写入位置
for (int i = 0; i < s.size(); i++) {
if (s[i] == ' ') continue; // 跳过空格
// 提取一个单词
int a = i, b = k;
while (a < s.size() && s[a] != ' ') s[b++] = s[a++];
// 反转单词
reverse(s.begin() + k, s.begin() + b);
// 添加一个空格
s[b++] = ' ';
// 更新索引
i = a;
k = b;
}
// 去除最后一个多余的空格
if (k) k--;
// 删除剩余字符
s.erase(s.begin() + k, s.end());
// 反转整个字符串
reverse(s.begin(), s.end());
return s;
}
};
- 时间复杂度:O( n n n)
- 空间复杂度:O( 1 1 1)