代码随想录算法训练营第1天

代码随想录算法训练营第1天 | 数组理论基础,704. 二分查找,27. 移除元素,977.有序数组的平方

1. 刷题部分

1.1 数组基础理论

1.1.1 题目内容

知识性讲解,点击链接查看原文。

1.1.2 初见想法

是一些很基本的知识,看看有么有什么生疏的。

1.1.3 看录后想法

原来有的语言的二维数组元素地址是可以行与行之间不连续的。

1.1.4 遇到的困难

暂未遇到困难。


1.2 704. 二分查找

1.2.1 题目内容

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

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

1.2.2 初见想法

就是纯粹的二分查找嘛,本科的时候都学过了,写写看吧。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.size() == 0) return -1;
        int left = 0, right = nums.size() - 1;
        int mid;
        while (left <= right) {
            mid = (left + right)/2;
            if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] < target) {
                left = mid + 1;
            }
            else {
                return mid;
            }
        }
        return -1;
    }
};

感觉挺顺的,看看录里有没有什么点是我需要补充的吧。

1.2.3 看录后想法

  • 二分查找的一个重点是边界判断
  • 不同的边界定义会导致不同的条件判断,注意整个过程边界定义应保持不变

看完之后再写一个左闭右开的版本吧:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.size() == 0) return -1;
        int left = 0, right = nums.size();
        int mid;
        while (left < right) {
            mid = (left + right)/2;
            if (nums[mid] > target) {
                right = mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            }
            else {
                return mid;
            }
        }
        return -1;
    }
};

1.2.4 遇到的困难

vector的一些内容有点忘记了,去网上搜了点资料复习了一下。

1.3 27. 移除元素

1.3.1 题目描述

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

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

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

用户评测:

评测机将使用以下代码测试您的解决方案:

int[] nums = [...]; // 输入数组
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 长度正确的预期答案。
                            // 它以不等于 val 的值排序。

int k = removeElement(nums, val); // 调用你的实现

assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有的断言都通过,你的解决方案将会 通过

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3,_,_,_]
解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
注意这五个元素可以任意顺序返回。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

1.3.2 初见想法

题目说顺序无所谓,那么直接就会想到每次遇到把末尾的非val元素移动过来好了。之前应该是写什么东西的时候有过类似的思想。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        for (int i = 0; i < nums.size(); i++) {
            //先保证末尾的元素不是val
            while (nums[nums.size() - 1] == val) {
                nums.pop_back();
                //考虑直接整个删没了的特殊情况
                if (nums.size() == 0) {
                    return 0;
                }
            }
            //从后往前删把当前操作元素都删了那直接结束了
            if (i == nums.size()) {
                break;
            }
            //开始将末尾元素移动并替换待删元素
            if(nums[i] == val){
                nums[i] = nums[nums.size() - 1];
                nums.pop_back();
            }
        }
        return nums.size();
    }
};

1.3.3 看录后想法

原来快慢指针可以这么方便地解决这个问题,学到了!

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        //双指针
        int slow_index = 0, fast_index = 0;
        for(fast_index = 0; fast_index < nums.size(); fast_index++){
            if(nums[fast_index] != val){
                nums[slow_index] = nums[fast_index];
                slow_index ++;
            }
        }
        return slow_index;
    }
};

录里还有暴力解法,说是可以锻炼一下代码实现能力,好吧,那我就听话也写一遍:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        //暴力解法
        int val_count = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] == val) {
                //遇到val之后把后面所有元素前移1位,在末尾就退出循环
                if (i == (nums.size() - 1 -val_count)) {
                    val_count++;
                    break;
                }
                for(int j = i + 1; j < nums.size() - val_count; j++) {
                    nums[j - 1] = nums[j];
                }
                val_count++;
                //移动之后要会退i,防止连续val的情况
                i--;
            }
        }
        return nums.size() - val_count;
    }
};

跟录里的暴力代码比对一下,发现录里的没有写 i 到末尾的时候的判断,分析了一下确实不需要写,因为如果删最后一个元素的时候,不会进入移动元素的那个 for 循环,因而不会出现溢出。

1.3.4 遇到的困难

第一遍写我的方法时总是少考虑一些边界情况导致判题不通过,好在这些情况简单分析一下就知道怎么回事儿了。不过如果能够在判错之前就考虑好这些情况会更好,我还是需要多提高一下。

另外,在写暴力的时候发现自己确实暴露出一些代码的细节把握的不好的问题,可见暴力有时候也是有用的。

1.4 977.有序数组的平方

1.4.1 题目描述

给你一个按 非递减顺序 排序的整数数组 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 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非递减顺序 排序

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题

1.4.2 初见想法

先把各元素平方,然后左右两端进行比较,开一个新数组然后依次从大到小放进去。放的顺序是从后往前,这样新数组就是从小到大的了。写一下:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;
        vector<int> result(nums.size(), -1);
        int index = nums.size() - 1;
        while(left <= right){
            if(abs(nums[left]) <= abs(nums[right])){
                result[index--] = sqr(nums[right--]);
            }else{
                result[index--] = sqr(nums[left++]);
            }
        }
        return result;
    }
    //求绝对值
    int abs(int a){
        if(a < 0) return -a;
        else return a;
    }
    //求平方
    int sqr(int a){
        return a * a;
    }
};

1.4.3 看录后想法

与录基本一致。没有太多想法。

1.4.4 遇到的困难

无。

2. 总结部分

第一天整体来看还是比较简单的,也可能是这些数组的知识自己平时就用的比较多,所以比较熟练。后面做起来体感肯定是比今天要难的,加油吧!

学习时长:约3小时。

posted @ 2025-01-08 12:12  xc0208  阅读(1073)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示