Day1|数组理论基础|704.二分查找

数组理论基础

知识点

  1. 数组是存放在连续内存空间上的相同类型数据的集合
  2. 数组下标都是从0开始的。
  3. 数组内存空间的地址是连续的。
  4. 删除下标为3的元素,需要对下标为3的元素后面的所有元素都要做移动操作
  5.  二维数组在空间中的地址也是连续的
  6. 下一元素地址 = 该元素地址 + 元素所占大小

704.二分查找

该题的前提条件:给定的数组元素是升序的,而且数组内没有重复元素。

题目链接:704. 二分查找 - 力扣(LeetCode)icon-default.png?t=O83Ahttps://leetcode.cn/problems/binary-search/description/

个人思路:直接简单暴力(效率极低)

int search(int* nums, int numsSize, int target) {
    for (int i = 0; i < numsSize; i++) {
        if (nums[i] == target) {
            return i;
        }
    }
    return -1;
}

注意:核心代码模式下,表示数组大小  numsSize

二分查找法

思路

定好左右边界(左闭右开或者左闭右闭),计算数组中间值mid,用mid表示的数组元素与目标值target比较,根据二者的大小关系不断缩小比较的边界

难点

  1. while(left <right)还是 while(left <=right)
  2. 缩小边界范围,直接等于mid还是mid + 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) / 2;
            if(nums[mid] > target) {
                right = mid - 1;
            }
            else if(nums[mid] < target ) {
                left = mid + 1;
            }
            else if (nums[mid] == target) {
                return mid;
            }
        }
        return -1;

   
    }
};

代码分析:

  • while()   由于是左闭右闭型(判断是哪种类型,看初始边界设定),循环条件应该为(left< =right),比如如果边界为[1,1],left = right成立,二者都能选中,条件合理。
  • 缩小边界范围:以nums[mid]>target为例,因为大于,所以在边界范围中就没有mid,而且是左闭右闭,则将right = mid - 1即可

注意:

  1. c++表示数组的大小:nums.size()
  2. mid计算:(l+r)/2(慢)     l+(r-l)/2(快且不会减少溢出风险)
  3. 进行判断,有大于,等于,小于三种情况,全部列出,并以else if的形式写出,不用写else

情况二:(左闭右开)

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

代码分析:

  • while(),因为左闭右开型,循环条件为left<right,不能等于,如果边界条件为[1,1),不能出现既取到1又取不到1的情况
  • 缩小边界范围:以 nums[mid] > target为例,因为大于,所以在边界范围中就没有mid,而且是左闭右开,所以直接取right = mid即可

现在是2024.10.2,23点,刚加入训练营,以前从来没有这样刷过题,感觉比较生疏,效率太低了,不过我觉得还是非常有效的,今天能做多少做多少,相信明天的自己比今天的更好!

27.移除元素

题目链接:
力扣—27.移除元素

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

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

  • 返回 k

  • 暴力法

个人思路尝试:

循环暴力解决,新数组长度 = 原数组长度 —覆盖次数

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int k = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] == val) {
                nums[i + 1] = nums[i];
                k++;
            }
            
        }
        return k;
 
    }
};

代码疑点:

  1. 本代码中只使用一层循环,该循环用于遍历数组,但是元素的删改需要不断的用后一个元素删除前一个元素,需要再用一层循环,进行元素删除覆盖。
  2. 删除覆盖时,如果用nums[i + 1] = nums[i],数组中的最后一个元素会被数组外的空间替代,边界处理出现问题 

正确代码:

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++) {
                nums[j - 1] = nums[j];
                }
                i--;
                size--;
            }
            
        }
        return size;
 
    }
};

代码分析:

  1. 用size代替数组长度函数nums.size(),以便后续数组的操作
  2. 使用双循环,一层用于遍历数组,另一层用于覆盖元素,时间复杂度为O(n^2)
  3. 修复了边界问题
  4. i--的解释:因为i之后的数组元素整体向前移动一位,如果不i--,下一步会进入第一层循环,进行i++,则新移动到i处的元素不会被判断,因此先i--,然后进入循环的i++,这样就不会漏掉数据
  5. 简化求新数组长度的代码,每进行一整个覆盖操作(整体移动前一行),数组中少一个元素,长度减一即可。
  6. 新增:返回的是size的值,在分析代码的过程中,发现:如果在数组的末尾有需要删除的数,就无法进行覆盖删除的操作,思考过后发现,由于每次删除覆盖都会改变size和i,j的大小,经过操作,i不会到达无法被删除的位置,而且题目要求是直接返回新数组的大小即可,不需要管没被删掉的元素。

双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

看了卡尔的视频醍醐灌顶啊我去!!!

忘记写赋值的条件了,果然还是要自己写一遍呜呜呜

错误代码

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowindex = 0;
        for (int fastindex = 0; fastindex < nums.size(); fastindex++) {           
                nums[slowindex++] = nums[fastindex];
        }
        return slowindex;
    }
};

思路:先要搞清楚快慢指针的作用,快指针要获取新数组元素,并且要赋值给慢指针,相当于种花,而慢指针指的是新数组下标的位置,相当于花盆。

正确代码

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowindex = 0;
        for (int fastindex = 0; fastindex < nums.size(); fastindex++) {
            if (val != nums[fastindex]) {
                nums[slowindex++] = nums[fastindex];
            }
        }
        return slowindex;
    }
};

代码分析:

  1.  nums[slowindex++] = nums[fastindex],表示先赋值再加,这里的加表示为下一个元素的存放做准备,相当于准备花盆,先有花盆才有花
  2. 返回的slowindex正好就是新数组的大小
  3. 时间复杂度:O(n)

977.有序数组的平方

题目链接:977. 有序数组的平方 - 力扣(LeetCode)

暴力法:

个人思路尝试:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for (int i = 0; i < nums.size(); i++) {
            nums[i] = nums[i] *nums[i];
        }
       

    }
};

代码分析:
把数组中的每一个数都平方,因为有负数存在,平方后要重新排序,再输出

疑点:不会排序,不会输出数组

正确代码:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for (int i = 0; i < nums.size(); i++) {
            nums[i] = nums[i] *nums[i];
        }
        sort(nums.begin(), nums.end());
        return nums;

    }
};

这里涉及到一个快速排序,先去补一下,然后再看

双指针法

自己的代码

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result(nums.size(), 0);
        int i = 0;
        int j = nums.size() - 1;
        for (int k = result.size(); k > 0; k--) {
            if(nums[i] * nums [i] < nums[j] *nums[j]) {
                result[k] = nums[j] * nums[j];
                j--;
            }
            else if (nums[i] * nums [i] > nums[j] * nums[j]) {
                result[k] = nums[i] * nums[i];
                i++;
            }
        }
        return result;

    }
};

思路:https://www.programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html#%E6%80%9D%E8%B7%AF 疑点:

在这里创建了一个新的数组,不知道是什么意思vector<int> result(nums.size(), 0);

代码分析:

  1. int k本质上是新数组的下标,旧数组的大小和新数组的大小相等,所以k的值等于nums.size() - 1,并且循环结束的范围是k大于等于0
  2. 进行判断操作时,因为题中说的是非递减数组,因此数组中的元素关系为递增,也可能有个别元素相等,相等的时候,j--和i++都可以,为了方便,把两个情况归作一类,故直接使用if-else即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值