代码随想录算法训练营第一天 | 704. 二分查找、27. 移除元素

本文介绍了数组的基础知识,包括数组的定义、特点以及在C++和Java中的内存管理差异。通过LeetCode的704.二分查找问题,详细解析了三种解题思路,包括暴力遍历和两种二分查找方法,讨论了它们的时间复杂度。此外,还探讨了27.移除元素问题的双指针解决方案。文章强调了二分法和双指针在数组操作中的应用及其效率优势。
摘要由CSDN通过智能技术生成

数组理论基础

数组的定义

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

在这里插入图片描述

数组的特点

  1. 数组下标都是从0开始的。
  2. 数组内存空间的地址是连续的
  3. 正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
    数组的元素是不能删的,只能覆盖。
    在这里插入图片描述

不同编程语言的内存管理是不一样的

C++

在C++中二维数组是连续分布的。
在这里插入图片描述

JAVA

在Java中二维数组是非连续分布的。
在这里插入图片描述

leetcode 704.二分查找

题目链接 : 704.二分查找

解题思路

思路一(暴力解法)

我第一感觉是通过for()对数组进行遍历,对数组中每个数与target进行比较,然后输出下标

class Solution {
    public int search(int[] nums, int target) {
        for(int i = 0; i < nums.length; i++){
            if(nums[i] == target){
                return i;
            }
        }
        return -1;
    }
}

时间复杂度: O(n);
空间复杂度: O(1);

思路二(二分法—左闭右闭)

由于思路一的解法时间复杂度为O(n),为了减少时间复杂度
分析题目:
1. 升序数组
2. 无重复值
我们可以从中间开始判断,会有一下三个结果:
1.sum[mid] < target 数组为升序数组,所以target只会存在于(mid + 1, l)
2.sum[mid] > target 数组为升序数组,所以target只会存在于(r, mid - 1)
3.sum[mid] = target 所以target = mid;

class Solution {
    public int search(int[] nums, int target) {
        int l = 0;
        int r = nums.length - 1;
        while(l <= r){
            int mid = (r + l) / 2 ; 
            if(nums[mid] < target){
                l = mid + 1;
            }else if(nums[mid] > target){
                r = mid - 1;
            }else{
                return mid;
            }
        }
        return -1;
    }
}

时间复杂度: O(log(n));
空间复杂度: O(1);

思路三(二分法—左闭右开)

与第二种思路不同的地方在于区间的不同,解释如图:
左闭右闭:

左闭右开:
在这里插入图片描述

class Solution {
    public int search(int[] nums, int target) {
        int l = 0;
        int r = nums.length;
        while(l < r){
            int mid = (l + r) / 2;
            if(nums[mid] < target){
                l = mid + 1;
            }else if(nums[mid] > target){
                r = mid;
            }else{
                return mid;
            }
        }
        return -1;
    }
}

注意:
1.在左闭右开算法中,我们没有将 l==r 考虑进去,所以我们需要考虑在最后存在两个值时两个值都已经进行了判断,但是mid值在(l+r)为奇数时,mid会取左边小的值,最小的值就会进行判断,所以在最后只存在两个值时,右边的值一定进行了判断,而右边的值不一定进行了判断,我们就将已经判断的值(mid)赋予r,所以对右边的值的选取我采取 r = mid,而左边值的选取采取 l = mid + 1。
2.但是上述方法存在一种情况无法解决,当右边值一直没有发生改变,右边值就没有进行判断,解决措施就是将r = num,length。

左闭右闭与左闭右开的区别

两者的根本的不同在于对区间的定义
左闭右闭:while( l <= r ) l == r 是有意义的,我们循环时可以将r的值mid+1,mid+1未知,我们循环最后会将 l == r 进行比较。
左闭右开:while(l < r) l == r 是没有意义的,我们循环时可以将mid 赋值给 r,因为mid已知,不需要在考虑 l == r的情况。

leetcode 27. 移除元素

题目链接27.移除元素

解题思路

思路一(暴力解法):

首先想到的将val从数组中剔除,但是数组无法直接将值直接删除,只能通过覆盖,将要删除的数,直接将后面的值全部往前移一位,然后将数字的长度减小。

class Solution {
    public int removeElement(int[] nums, int val) {
        int result = nums.length;
        for(int i = 0; i < result; i++){
            if(nums[i] == val){
                for(int j = i; j < result - 1; j++){
                    nums[j] = nums[j + 1];
                }
                result--;
                i--;
            }
        }
        return result;
    }
}

思路二(双指针—快慢双指针):

我们可以利用两个指针对数组进行改变,快指针对数组进行遍历如果num[fast]==val,我们将slow指针就不动,直到不相等时,将fast的值赋予slow。在这里插入图片描述

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        for(int fast = 0; fast < nums.length; fast++){
            if(nums[fast] != val){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
}

思路三(双指针)

使用两个指针(left,right)分别在数组左右两端,从left开始遍历如果num[left] == target,就将right与left的值进行替换,在对进行交换的值进行判断,如果等于target就将right-1与他交换,以此类推直到left > right结束。

class Solution {
    public int removeElement(int[] nums, int val) {
        int l = 0;
        int r = nums.length - 1;
        int res = nums.length;
        while(l <= r){
            if(nums[l] == val){
                int temp = nums[l];
                nums[l] = nums[r];
                nums[r] = temp;
                r--;
                l--;
                res--;
            }
            l++;
        }
        return res;
    }
}

今天的收获

1.对数组的一些定义进行巩固。
2.通过704.二分查找 学会如何使用二分法,还有二分法的两种方式和其区别。
3.通过27.移除元素 学会如何使用双指针。

感想

今天是我第一天写博客,花的时间比较长,但是今天的收获也很多,完成了博客也让我成就感满满,我希望我能一直坚持到最后一天,加油!!!

第二十二天的算法训练营主要涵盖了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题,题目要求在给定的数组中找到长度最小的子数组,
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值