代码随想录算法训练营第一天 | LeetCode704.二分查找、LeetCode27.移除元素、LeetCode977.有序数组的平方

代码随想录算法训练营第一天 | LeetCode704.二分查找、LeetCode27.移除元素、LeetCode977.有序数组的平方

01 理论基础
  • ref:数组:连续内存空间
  • 要点:下标本从0开始、内存空间连续、C++如果使用vector<int>实现数组,其在调用erase时复杂度为O(n)
02-1 LeetCode704.二分查找

相关资源

题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

第一想法:因为本科的时候系统学习过算法设计课程,也有一定的C语言编程经验,因此对二分法是比较熟悉的,这种二分查找法适用于已经排好序的数组,通过O(logn)复杂度即可查找到目标。

实现:理想很丰满,现实很骨感,在实现过程中遇到while条件和边界赋值两大难关,借助Debug逐渐理清思路,我采用的其实是左开右开的区间定义,这样边界无脑用middle更新即可。最初写的代码如下:

#include<iostream>  
#include <limits>
#include<vector>
using namespace std;

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int i = 0;
        int j = nums.size()-1;
        int result = -1;
        bool flag = false;
        // 最开始就要判断边界以获得开区间
        if (nums[i] == target) {
            result = i;
            flag = true;
        }
        else {}
        if (nums[j] == target) {
            result = j;
            flag = true;
        }
        else {}
        // while循环中结束条件设置为 i < j - 1
        // 因为(i,i+1)其实就已经没有数了,即可跳出循环
        while (i < j-1) {
            // 听代码随想录之前其实脑子里没有循环不变量的意识,其实下面两行代码可以优化掉
            if (nums[i] == target) {
                result = i;
                flag = true;
                break;
            }
            else {}
            if (nums[j] == target) {
                result = j;
                flag = true;
                break;
            }
            else {}
            int middle = (j + i) / 2;
            // 边界大胆赋值middle即可
            if (nums[middle] < target) {
                i = middle;
            }
            else if(nums[middle] == target) {
                result = middle;
                flag = true;
                break;
            }
            else {
                j = middle;
            }
        }
        if (flag) {
            return result;
        }
        else {
            return -1;
        }
    };
};

int main(){
    vector <int> nums { -1,0,3,5,9,12 };
    int target = 9;
    Solution solution;
    int result = solution.search(nums, target);
    cout << result << endl;
    return 0;
}

看完代码随想录之后的想法: 写算法题不能一股脑就是干,要多想想,因为逻辑不太清晰,初次写的代码有很多步骤是完全不需要的,有待优化

收获

  • 明确二分法的循环不变量规则,即每次循环区间定义保持一致

  • 更常见的区间定义为左闭右闭、左开右闭

ToDo:尝试优化左开右开、尝试左闭右闭、左闭右开

02-2 LeetCode27.移除元素

相关资源

题目:

给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

  • 更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
  • 返回 k

第一想法:之前略微了解过双指针,就没有太往暴力双循环去考虑。之前接触过的双指针通常是首指针和尾指针,然后逐渐向之间逼近,秉持此思维定势,设计算法:把尾指针不等于val的元素和首指针等于val的元素交换,等到汇合的时候即达到目标。

实现:

#include<iostream>  
#include <limits>
#include<vector>
using namespace std;

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        // 首指针
        int i = 0;
        // 尾指针
        int j = nums.size()-1;
        // 计数
        int sum = 0;
        while (i <= j) {
            // 从前往后找到第一个等于待删除数
            if (nums[i] != val) {
                i = i + 1;
                sum = sum + 1;
            }
            else {
                // 找到第一个等于待删除数,从后往前找到第一个非删除数和之前待删除数交换
                if (nums[j] != val) {
                    nums[i] = nums[j];
                    nums[j] = val;
                    i = i + 1;
                    sum = sum + 1;
                }
                else {}
                j = j - 1;
            }
            }
        return sum;
    }
};

int main() {
    vector <int> nums{ 0,1,2,2,3,0,4,2 };
    int target = 2;
    Solution solution;
    solution.removeElement(nums, target);
    cout << nums[2]<< endl;
    return 0;
}

疑问:C++调用函数的时候会直接修改原始的数组?我还以为会涉及到浅拷贝或者深拷贝,待进一步钻研。

看完代码随想录之后的想法: 可以采用快慢指针来实现,思维复杂度更低

收获

  • 双指针还有快慢指针,不一定是首尾指针,但均一次遍历即可达到目标。快指针:寻找新数组的元素,慢指针:指向更新新数组下标的位置

  • C++如果使用vector<int>实现数组,其在调用erase底层机制就是这样,复杂度为O(n)

ToDo:尝试快慢指针

02-3 LeetCode977.有序数组的平方

相关资源

题目:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

第一想法:经典首尾双指针,设计算法:把尾指针元素和首指针元素平方进行比较,大的放在新数组的尾端,等到汇合的时候即达到目标。

实现:

#include<iostream>  
#include <limits>
#include<vector>
using namespace std;

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int i = 0;
        int j = nums.size() - 1;
        vector<int> sort = {};
        int square_left;
        int square_right;
        while (i <= j) {
            square_left = nums[i] * nums[i];
            square_right = nums[j] * nums[j];
            // 两端中较大的插入
            if (square_left >= square_right) {
                i = i + 1;
                sort.insert(sort.begin(), square_left);
            }
            else {
                j = j - 1;
                sort.insert(sort.begin(), square_right);
            }
        }
        return sort;
    };
};

int main() {
    vector <int> nums{ -7,-3,2,3,11 };
    Solution solution;
    vector<int> new_nums = solution.sortedSquares(nums);
    return 0;
}

看完代码随想录之后的想法: 算法设计上和代码随想录相同,但具体实现采用了库函数的insert,导致资源占用加大

收获:首尾指针

ToDo:预定义一个等大的数组,然后从后往前赋值以减少资源占用

第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值