一、数组移除难处
因为数组顺序存储的特性,要求删除数组中【指定数值】并不像链表一样简便,需要将其后续存储内容顺次前移覆盖。
抱着玩玩leetcode打发时间的心态,我尝试了一共四种写法
二、具体解法
1.暴力法
用两重循环,外层循环用于检查是否存在【nums[i]==val】,一旦发现,进入内层循环,将后续元素全部顺次前移一位,直到数组末尾
时间复杂度为O(n^2),空间复杂度O(1)
没啥好说的,直接上代码
int remove(vector<int> &nums, int val)
{
int size = nums.size();
for (int i = 0; i < size; i++)
{
if (nums[i] == val)
{
for (int j = i+1; j < size; j++)
{
nums[j - 1] = nums[j];
}
i--;
size--;
}
}
return size;
}
2.存储法
如果能有一个【笔记本】一样的东西,让我在遍历过程中把【我要的】元素记录下来,就可以省去两遍遍历
新开一个temp数组,将所有不重复元素存储进去,这样时间复杂度降到O(n),但相应的空间复杂度达到O(n
int remove(vector<int>& nums, int val)
{
int size = nums.size();
vector<int> newnums;
for (int i = 0; i < size; i++)
{
if (nums[i] != val)
{
newnums.push_back(nums[i]);
}
}
nums = newnums;
return nums.size();
}
3.存储法pro
时空复杂度与【存储法】没有本质区别,只是并不新申请一个vector,而是直接把所需元素记录在原有vector尾部,最后统一复写
int remove(vector<int>& nums, int val)
{
int size = nums.size();
int cnt=0;
for (int i = 0; i < size; i++)
{
if (nums[i] != val)
{
nums.push_back(nums[i]);
cnt++;
}
}
for (int i = 0; i < cnt; i++)
{
nums[i] = nums[size++];
}
return cnt;
}
4.快慢指针
是不同于前三种的较优解法,设计两个指针——慢指针+快指针,快指针随着for循环逐次更新,慢指针只有在写入【我要的数】后才进行更新
也正因如此,慢指针的数值就对应着新数组的大小
时间复杂度O(n),空间复杂度O(1)
上代码:
int remove_doublep(vector<int>& nums, int val)
{
int size = nums.size();
int slowidx = 0;
for (int fi = 0; fi < size; fi++)
{
if (val != nums[fi])
{
nums[slowidx++] = nums[fi];
//cout << slowidx << endl;
}
}
return slowidx;
}
三、LeetCode平台测试结果(题号27)
暴力法:
存储法:
存储法pro:
长短指针:
结果显示,在leetcode平台的113个case情况下,暴力法和存储法表现一般,且存储法有明显的空间性能劣势。
但【存储法pro】和【长短指针】都能达到4ms以下的解题时间,甚至【存储法pro】的空间利用更优,这种情况或许与vector的存储特性有关。