相关题目推荐
- 26.删除排序数组中的重复项
- 283.移动零
- 844.比较含退格的字符串
- 977.有序数组的平方
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
1. 暴力求解法
在数组中遍历寻找val,找到后,就将val位置以后的元素全部前移,注意:本来val后一个元素前移后,覆盖掉了val,下次要从该位置往后遍历,因为此时是原来val的后一个元素,所以i--,后在i++保证了下次遍历的第一个还是该位置上的元素,最后记得size--
时间复杂度:O(n^2)
空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; ++i){
if (nums[i] == val)
{
for (int j = i; j < size-1; ++j)
nums[j] = nums[j+1];
/* 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位,
下次仍然从i这个位置检测新元素,不然每次会漏检掉val后面的一个数*/
i--;
size--;
}
}
return size;
}
};
2.快慢双指针法
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
class Solution2 {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
int fast = 0, slow = 0;
while (fast < size)
{
if (nums[fast] != val)
nums[slow++] = nums[fast];
fast++;
}
return slow;
}
};
3.左右双指针法
一个left指针从前往后找val的位置,一个right指针从后往前找非val的位置,最后将后面找到的不等于val的值覆盖到前面val值的位置上。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int left = 0;
int right = nums.size() - 1;
while (left <= right) // 防止一个元素的情况 [1] 1
{
while (left <= right && nums[left] != val) // 找左边等于val的元素
left++;
while (left <= right && nums[right] == val) // 找右边不等于val的元素
right--;
// 左边已找到val位置,右边已找到非val位置,存在left已超过right情况如:
// [3,2,2] 2,则没必要覆盖直接返回左指针的位置
if (left < right)
nums[left++] = nums[right--];
}
return left;
}
};
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
法一.冒泡排序:冒泡排序,时间复杂度O(N²),将所有的0置底
时间复杂度O(N²)
class Solution {
public:
void Swap(int* left, int* right){
int temp = *left;
*left = *right;
*right = temp;
}
void moveZeroes(vector<int>& nums) {
int size = nums.size();
for (int i = 0; i < size - 1; ++i){
// 具体的冒泡方式:用相邻位置的元素进行比较,如果不满足条件,就进行交换
// j:表示前一个元素的下标
// -1目的:j最多只能取到冒泡区间的倒数第二个元素
for (int j = 0; j < size - i -1; ++j){
if (nums[j] == 0){
Swap(&nums[j], &nums[j + 1]);
}
}
}
// 或已经是换好的,直接返回
return;
}
};
法二:快慢双指针法(同上)
// 快慢双指针
class Solution {
public:
void Swap(int* left, int* right){
int temp = *left;
*left = *right;
*right = temp;
}
void moveZeroes(vector<int>& nums) {
// fast寻找新的不为0的元素,slow用来接受新元素替换掉位置上的0,交换后记得slow++
int fast = 0, slow = 0, size = nums.size();
while(fast < size){
// 如果fast指向非0,则停下来交换
if (nums[fast]!= 0)
Swap(&nums[slow++],&nums[fast]);
//快指针无论每次有没有找到0,都要往后移位
fast++;
}
return;
}
};
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
法1:利用压栈出栈的方式
思路:
最容易想到的方法是将给定的字符串中的退格符和应当被删除的字符都去除,还原给定字符串的一般形式。然后直接比较两字符串是否相等即可。
具体地,我们用栈处理遍历过程,每次我们遍历到一个字符:
如果它是退格符,那么我们将栈顶弹出;
如果它是普通字符,那么我们将其压入栈中。
// 利用栈来还原原字符串
class Solution {
public:
bool backspaceCompare(string S, string T)
{
return Rebuild(S) == Rebuild(T);
}
string Rebuild(string str)
{
string ret; // 用来接收还原后的字符串
for (auto ch : str)
{
if (ch != '#') // 不是退格符就压栈
ret.push_back(ch);
else { // 是退格符,判断栈内有没有字符,有就出栈,没有就不管
if (!ret.empty())
ret.pop_back();
}
}
return ret; // 返回还原后的串
}
};
法2:双指针法
class Solution {
public:
bool backspaceCompare(string S, string T) {
int skipS = 0, skipT = 0; // 定义各字符串"#"的数量
int i = S.length() - 1, j = T.length() - 1; // 字符串下标的上限
while (i >= 0 || j >= 0)
{
while (i >= 0)
{
// 找到"#",存储数量skipS+1
if (S[i] == '#') skipS++,i--;
// 找到有效元素
// 检查退格符号数量,如果有,则数量-1,该字符被抵消
else if (skipS > 0) skipS--,i--;
else break; // 无退格符
}
while (j >= 0)
{
if (T[j] == '#') skipT++,j--;
else if (skipT > 0) skipT--,j--;
else break;
}
if (i >= 0 && j >= 0)
{
if (S[i] != T[j])
return false;
}
else
{
if (i >= 0 || j >= 0)
return false;
}
i--, j--;
}
return true;
}
};
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
法1:暴力遍历+排序
这个时间复杂度是 O(n + nlogn), 可以说是O(nlogn)的时间复杂度,但为了和下面双指针法算法时间复杂度有鲜明对比,我记为 O(n + nlog n)。
// 暴力排序
时间复杂度 循环遍历O(N)+ 速排序O(NlogN) = O(NlogN)
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(auto& e :nums)
e*=e;
sort(nums.begin(), nums.end()); //快速排序
return nums;
}
};
//左右双指针
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int k = nums.size()-1;
//创建和nums的同容量大小的容器,并全赋初值0
vector<int> A(nums.size(), 0);
// 注意这里要i <= j,因为最后要处理两个元素
for (int i = 0 , j = nums.size()-1; i <= j;)
{
// 平方后的最大值一定在两边,所以每次比较两边的值,然后从大往小排
if (nums[i] * nums[i] < nums[j] * nums[j]){
A[k--] = nums[j] * nums[j];
j--;
}
else {
A[k--] = nums[i] * nums[i];
i++;
}
}
return A;
}
};