算法69题

求一个数的平方根,我首先想到的是顺序查找,判断前后两次比较的结果,但是这样的话,时间复杂度就是o(n),如果采用二分法,可以将时间复杂度降低到o(logn),于是我试着手搓一个二分法:
class Solution {
public:
int mySqrt(int x) {
int left = 0;
int right = 1 << 16;
int ret;
while(left<right)
{
int middle = (right-left)/2 + left;
if(x > middle*middle)
{
left = middle;
}
else if (x < middle*middle)
{
right = middle;
}
else
{
ret = middle;
break;
}
}
return ret;
}
};
在上面手搓的代码中,有几处问题,分别是:
- 当
middle较大时,middle * middle可能会超过int的表示范围(−到
−1)。这会导致错误的结果
- 在二分法中,
left和right的调整方式需要确保边界收缩时不会无限循环
对上面的代码中存在的问题进行修改:
- 通过将
middle转换为long long来计算平方值,防止乘法溢出。 - 当
square < x时,将left设置为middle + 1,是为了在确定目标值x大于middle * middle的情况下,将搜索范围移动到右半部分,从而排除掉middle本身(因为middle已被检查过),以此来避免程序陷入死循环。
下面是修改后的代码:
class Solution {
public:
int mySqrt(int x) {
if (x < 2) return x; // 特殊情况处理
int left = 0;
int right = 1<<16;
int ret = 0; // 保存结果
while (left < right) {
int middle = left + (right - left) / 2; // 防止溢出
long long square = static_cast<long long>(middle) * middle; // 避免溢出
if (square == x) {
return middle; // 恰好是平方根
} else if (square < x) {
ret = middle; // 暂时保存可能的解
left = middle + 1;
} else {
right = middle;
}
}
return ret; // 返回计算结果
}
};
这里,static_cast 是一种类型转换操作符,用于在编译时执行显式的类型转换。与传统的 C 风格类型转换((type)variable)相比,static_cast 更加类型安全,并且语义更加明确。
算法282题
题目描述为:
我第一次给出的代码如下:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int count = 0;
for(auto it = nums.begin();it != nums.end();it++) {
while (*it == 0) {
nums.erase(it);
count++;
}
}
for(int i = 0;i != count; i++) {
nums.push_back(0);
}
}
};
思路是用迭代器进行数组的遍历,然后将零元素剔除,再用一个for循环进行补零,结果当测试用例只有一个0的时候,代码会陷入死循环,原因是:
当 nums 中只有一个零时:
while (*it == 0)进入循环,因为*it为零。nums.erase(it)删除当前的零元素,并使it失效。- 此时,
it是一个无效的迭代器,但你没有更新它。for循环随后调用it++,这会导致行为未定义,但实际上它仍然可能继续指向删除后的位置。 - 由于零已经被删除,数组
nums的end()也发生了变化,但你的代码会继续访问已经删除的位置。 - 结果是
while (*it == 0)反复进入循环,陷入死循环或崩溃
对原代码进行修改,如下:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int count = 0;
for (auto it = nums.begin(); it != nums.end(); ) {
if (*it == 0) {
it = nums.erase(it); // 更新迭代器为下一个有效位置
count++;
} else {
++it; // 如果当前不是 0,正常前进
}
}
for(int i = 0;i != count; i++) {
nums.push_back(0);
}
}
};
问题解决了,但是代码效率太低了,当数组中的0太多时,时间复杂度接近,采取GPT的建议,重写代码:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int nonZeroIndex = 0; // 非零元素的插入位置
// 将所有非零元素移到数组前部
for (int i = 0; i < nums.size(); ++i) {
if (nums[i] != 0) {
nums[nonZeroIndex++] = nums[i];
}
}
// 将剩余位置填充为零
for (int i = nonZeroIndex; i < nums.size(); ++i) {
nums[i] = 0;
}
}
};
算法题26

这题我用双指针法,定义两个迭代器,fast_it和it,一开始两个迭代器的指向如下图,然后让f_it往前移动,当两个迭代器指向的值不一致时,把it拉到f_it的位置 ,定义一个vector用于存放it指向的值,定义一个count进行计数。
具体代码如下:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
vector<int> ret;
int count = 1;
auto it = nums.begin();
ret.push_back(*it);
for(auto fast_it = nums.begin() + 1; fast_it != nums.end();fast_it++)
{
if(*it != *fast_it)
{
count++;
it = fast_it;
ret.push_back(*it);
}
}
nums = ret;
return count;
}
};
但是这种解法中,复制了一个新的数组,这会使得空间复杂度上升到,官方解法是一边遍历,一边对原数组进行覆写,下面贴一下官方给的代码:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int fast = 1, slow = 1;
while (fast < n) {
if (nums[fast] != nums[fast - 1]) {
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
}
};
&spm=1001.2101.3001.5002&articleId=144168550&d=1&t=3&u=2a2d2eb09b8f4970b46dc6aa51055a1b)
349

被折叠的 条评论
为什么被折叠?



