力扣刷题第二天(算法69,282,26)

算法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;
        
    }
};

在上面手搓的代码中,有几处问题,分别是:

  1. 当 middle 较大时,middle * middle 可能会超过 int 的表示范围(−2^{31} 到 2^{31}−1)。这会导致错误的结果
  2. 在二分法中,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 中只有一个零时:

  1. while (*it == 0) 进入循环,因为 *it 为零。
  2. nums.erase(it) 删除当前的零元素,并使 it 失效。
  3. 此时,it 是一个无效的迭代器,但你没有更新它。for 循环随后调用 it++,这会导致行为未定义,但实际上它仍然可能继续指向删除后的位置。
  4. 由于零已经被删除,数组 nums 的 end() 也发生了变化,但你的代码会继续访问已经删除的位置。
  5. 结果是 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太多时,时间复杂度接近o(n^2),采取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;
        
    }
};

但是这种解法中,复制了一个新的数组,这会使得空间复杂度上升到o(n),官方解法是一边遍历,一边对原数组进行覆写,下面贴一下官方给的代码:
 

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;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值