代码随想录算法训练营第一天 | 704二分查找、27移除元素、977有序平方根

第一章 数组

1、二分查找

力扣原题

题目简介:

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

初见思路:

​ 这个我会嗷,之前学算法的时候就已经过了一遍了。但是实现起来还是有一些不熟练的地方。

class Solution {
public:
int search(vector<int>& nums, int target) {
        
    int n = nums.size();
    int i = 0; 
    int j = n-1;
    
    while(i<=j){

        int m = (j + i)/2;
       
        if(nums[m] > target){
            j = m -1;         
        }
        else if(nums[m] < target){
            i = m + 1 ;
        }
        else{
            return m;
        }
    }
    return -1;
}
};

​ 实现的时候,我对几个地方还是存在犹豫不清的地方:

1、这个while循环的结束条件到底是什么?

​ 我第一遍写的时候我写成了while(i<j)的形式,这样子的话如果nums中只有一个数值的时候就会导致无法进入循环而得到错误的结论。

2、ij这两个变量到底如何变化?

​ 我学的有点混乱,因为有好几种,什么左开右闭左闭右开的形式,好像就是和这个ij的变化有关系。这个可能得等后面我学成归来再来解决了。

3、m的计算方式?

​ 我这里翻车了,我一开始写了一个m = (i - j)/2,我把它当作距离长度的一半了,随然二分查找的意思确实是,两个头中间的位置,但是这和索引的计算还是不同的,哎很拉跨的错误。

算法思路:

​ 好好好,代码随想录中完美的解决了我的难题。

1、while循环的结束条件到底是什么?

​ 这个其实就是和我之前提到的左闭右开左闭右闭有关系了,如果是左闭右闭的情况,也就是这道题目的情况,那么[ left , right ]这个区间里,left = right这个是有意义的,所以再while循环中,是应该添加一个等于符号的。而如果是左闭右开的情况,也就是[ left , right )那么其实在每次进行寻找的时候,右边界其实是取不到的,所以while循环里是不会有一个这个等于号的。

2、ij这两个变量到底如何变化?

​ 这个也是和边界条件息息相关。如果是左闭右闭,那么就是左边界的数和有边界的数都是完全可以取到的,所以判断的时候已经判断过左、右两个边界是不是我们找的目标了,那么我们就应该分别+1-1得到新的区间;而如果是左闭右开的情况,那么右边的边界其实是找不到。那找不到的话,那么right -1其实就我们下次要找的数中的一个,而不能让他变成我们不可达的右边界。所以就是j=m

2、删除元素

力扣原题

题目简介:

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

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

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

初见思路:

​ 我一开始看见这个问题,观察到他说不用在乎超过新数组长度的哪些原来的数量,想到的就是把前面的val全搬到后面去,然后返回前面的数组长度就好了,用了一个标准库的swap函数完成的。

int removeElement(vector<int> &nums, int val)
{
    int n = nums.size();
    int j = n - 1;
    int i = 0;

    if (n == 1)
    {
        if (nums[0] == val)
        {
            return 0;
        }
        else
        {
            return 1;
        }
    }
    int count = 0;
    while (i <= j)
    {
        if (nums[i] == val)
        {
            while (nums[j] == val && j > i)
            {
                j--;
                count++;
            }
            swap(nums[i], nums[j]);
            count++;
            j--;
        }
        i++;
    }
    return n - count;
}

​ 速度也很快,但是就是很繁琐,有一些特殊情况要处理。

算法思路:

​ 还是妙啊,快慢指针法。但是我还是没有完全的理解这个概念的妙处到底是什么

​ 用两个Index来管理数组,快指针就是一路遍历过去,然后慢指针会在快指针检测到val的时候,就会让慢指针停止一个次增长。这样子其实快指针和慢指针之间就会逐渐产生插值,这个插值就是检测到的val的个数。

​ 其实如果只是为了检测到val的个数,也没必要叫做双指针,不过就是维护了两个变量,让两个变量之间的差值,代表了目标元素的个数。随意遍历一遍都能获得这个值。

​ 真正的核心精髓,我觉得应该是赋值阶段,他让快指针检测到的非val值赋值给了慢指针的索引处,要知道这个时候,慢指针正在val处,所以这个赋值其实就是将后面的非val值移到了前面来,然后覆盖掉了val。这就让慢指针的值有了新的含义,就是不含有val的数组的长度。

​ 所以后面才会返回慢指针的值就能代表答案。

int removeElement(vector<int> &nums, int val)
{
    int n = nums.size();
    int slowIndex=0;
    for (int fastIndex = 0; fastIndex < n;fastIndex++){

        if(nums[fastIndex] != val){
            nums[slowIndex] = nums[fastIndex];
            slowIndex++;
        }

    }
    return slowIndex;
}

​ 我现在有一个更好的形容方法,两个指针一起前进,如果发现了目标就让一个指针先停在这里,等待另一个指针发现可以替换的目标,如果发现了马上进行交换。

​ 慢指针的行动逻辑是,在交换完之后,向前移动一格;

​ 快指针的行动逻辑是,每轮都前进一格。没发现目标,就和慢指针交换一次;如果发现了目标就不进行交换。

int myPointFstandSlow(vector<int> &nums, int val)
{
    int n = nums.size();

    int fast = 0;
    int slow = 0;
    
    for (; fast < n;fast++){
        if(nums[fast] != val){
            nums[slow] = nums[fast];
            slow++;
        }
        else{  
        }
    }
    return slow;
}

​ 和答案比其实就是差不多的了。

3、有序平方数组

力扣原题

题目简介:

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

初见思路:

​ 忘记我的初见思路是啥了。

算法思路:

​ 还是双指针法。但是思想还是很妙的,由于这是一个递增的数组,或者说是非减的数组。

​ 如果不考虑负数,那么肯定是越到后面的数,平方起来越大,那么直接平方然后copy到新数组就好了;

​ 但如果考虑负数,那么就必须要想到,负数越小他的平方就越大, 那么平方后最大的数只可能在数组的两边出现,最小的数会出现在中间;

​ 所以,我们只需要每次都考虑两头谁大,然后填在新数组的最后就好了。

​ 这里还有一点小巧思,那就是提前预设vector的大小,很显然,我们每次算出来的数是最大的数,但是我们却要把他填充在新数组的最前后头,这可咋办呢?vector只能往数组的后面插入呀,那岂不是成了递减数组了。

​ 但如果我提前设好了vector的大小,我就可以利用下标访问到对应位置的元素了,直接赋值就好了。

    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(n);
        for(int i = 0, j = n-1,pos = n-1; i<=j;){
            if(nums[i]* nums[i] < nums[j] * nums[j]){
                ans[pos] = nums[j] * nums[j];

                j--;
            }else{
                ans[pos] = nums[i] * nums[i];

                i++;
            }
            pos--;
        }
        return ans;
    }

打卡完成!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值