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

数组理论基础

数组是存放在连续内存空间上的相同类型数据的集合

数组徐璈注意的是:

  • 数组的下标都是从0开始的
  • 数组内存空间是的地址是连续的
    正因为舒适的内存空间是连续的,所以在删除和增添元素的时候,需要移动其他元素的地址。

在c++中,vector的底层实现是array,严格来说,vector是容器,不是数组。

数组的元素不能删除,只能被覆盖。

704 二分查找
题目链接:https://leetcode.cn/problems/binary-search/

给定一个 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

之前做过,但是现在一看还是很懵,没有一个清晰的解题思路。先看以便随想录上的解题思路,自己实现,总结收获。

解题思路:
首先需要看题的条件:有序数组,数组中无重复元素。如果是包含重复元素的数组,使用二分法查找返回元素下标可能不是唯一的。

区间的定义两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
第一种解法 左闭右闭:
[left, right]区间注意:

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1

自己按照思路写出的代码

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = **nums.size()**-1;
        while(left <= right){
            int mid = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2 这里不是很懂。
            //int mid = (right + left)/2;
            if(nums[mid] > target) {
                right = mid; //这里应该是right=mid-1,因为如果当前mid对应的值大于目标值,应该在[left,mid-1]区间继续找,因为是双闭的区间。
            }else if(nums[mid] < target){
                left = mid; //所以这里也就是left = mid+1
            }else return mid;                    
        }
        return -1;
    }
};

总结:
vector的size .size()
为什么要用left + ((right - left) / 2):
在计算中间值时,常见的表达式是 int middle = (left + right) / 2;。然而,当 left 和 right 是非常大的整数时,它们的和 left + right 可能会超过整数的最大值,从而导致溢出错误。此时,结果将是不正确的,可能会引发程序错误或崩溃。

第二种解法:左闭右开[left, right)
左闭右开时,仅需注意while(left < right) 两个不能相等
if(nums[mid] > target) right = mid; 即可

27.移除元素
题目链接:https://leetcode.cn/problems/remove-element/description/

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

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

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

题目分析:
原地:不使用额外的数组空间,在原地修改输入数组并在O(1)额外空间的条件下完成。
元素顺序可以改变,不需要考虑数组中超出新长度后面的元素。

暴力解法
思路:依次遍历数组中的所有元素,若等于val,size--,否则继续遍历。这时如何改变nums数组的值?即遇到值=val如何让数组后边的值覆盖前边的值。

class Solution {
public:
    int removeElement(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++){ //遇到数组中值=val,让后一个数组值覆盖当前值**
                    nums[j-1] = nums[j]; 
                }
            i--;// i--是当前值被后值覆盖,所以是新值,在下一轮循环还需要针对当前的i值做判断**
            size--;
            }
        }
        return size;
    }
};

重点 双指针法:
我的印象是设置一个fast一个slow指针。一个用于遍历数组,一个用于指示删除元素后数组的下标。
看完代码随想录后:
遇到数组值=val,fast继续向后遍历,slow不变,之后依次覆盖前面数组数值。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        int slow = 0;
        for(int fast = 0; fast < size; fast++){
            //错误写法 这里我想错了,总想等于val的时候应该做什么操作。
            // if(nums[fast] == val){
            //     fast++;
            //     nums[fast-1] = nums[fast];
            // }
            // else slow++;
            //只需要nums[fast] != val,才去给nums[slow]赋值就可以了。
            if(nums[fast] != val)
                nums[slow++] = nums[fast];
        }
        return slow;
    }
};

总结:
暴力解法注意双层遍历让nums数组后值覆盖前值。
双指针法注意只有nums[fast] != val,再去给nums[slow]赋值。

977.有序数组的平方
题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/

给你一个按 非递减顺序 排序的整数数组 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]

题目分析
数组是非递减,输出的新数组要求也按照非递减排序。
我的思路
这里给每一个数组元素平方在返回比较简单,但是如何返回的数组元素也按照非递减的顺序排列,没有太好的思路。依旧是双指针法,双指针怎么用?
看完随想录后
其实挺简单,让两个指针分别指向头和尾,定义一个新数组,两指针依次向中间遍历,看平方值的大小来判断哪个先放入新数组。

我的错误解法

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result;
        int i = 0, j = nums.size()-1;
        while(i <= j){
            //应该预先分配好result的值,再向数组加元素,这样减少频繁push_back
            //这个问题很大,我想的是如果i的nums值<j的nums值,就先向数组加入i的值,再加入j的值,这样是不对的。
            if(nums[i] * nums[i] < nums[j] * nums[j]){
                result.push_back(nums[i] * nums[i]);
                result.push_back(nums[j] * nums[j]);
            }
            if(nums[i] * nums[i] == nums[j] * nums[j]){
                result.push_back(nums[i] * nums[i]);
                result.push_back(nums[i] * nums[i]);
            }
            i++;
            j--;
        }
        return result;
    }
};

正确解法:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result(nums.size(),0);
        int i = 0, j = nums.size()-1;
        //定义一个新的全局k值,用于新数组的下标值
        int k = j;
        while(i <= j){
            // j--和i++只需要在其中nums[i]和nums[j]加入了新数组才用
            if(nums[i] * nums[i] < nums[j] * nums[j]){
                result[k--] = nums[j] * nums[j];
                j--;
            }else{
                result[k--] = nums[i] * nums[i];
                i++;
            }
        }
        return result;
    }
};

总结
我觉得最大的问题就是我总想着双指针i和j一起+或者-,这样是不对的,当满足条件仅改变其中一个就可以。

posted @   zzzzzzzhs  阅读(325)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示