代码随想录算法训练营第一天 | 704. 二分查找、27. 移除元素、35. 搜索插入位置

学习目标

  • 复习数组理论基础
  • 完成二分查找和移除元素相关题目

学习内容

数组理论基础

文章链接:数组理论基础

那么二维数组在内存的空间地址是连续的么?
不同编程语言的内存管理是不一样的,以C++为例,在C++中二维数组是连续分布的。

像Java是没有指针的,同时也不对程序员暴露其元素的地址,寻址操作完全交给虚拟机。
所以看不到每个元素的地址情况…

所以Java的二维数组可能是如下排列的方式:
Java数组可能的形式

小结:数组是连续空间内存放相同元素的集合,但不同的语言对二维数组的实现很可能不一样,C++存储二维数组仍然是一段连续的空间,而Java由于看不到内部,且二维数组内各组数组地址不一,很可能是上述图示的排列方式。


704.二分查找(Easy)

题目链接:704.二分查找(Easy)

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

思路:双指针分别指向数组 index=0index=nums.length 处,mid = ((left + right) >> 1) 根据 nums[mid] target大小关系的对leftright进行迭代。
时间复杂度:O(logn) 空间复杂度:O(1)

解决方案:
这是一种[0, nums.length-1] 的解法,个人认为比较直观易记。

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

        return -1;
    }
}

这是[0, nums.length) 的解法,与上述不同的地方有二:

  • 初始化时 right = nums.length
  • 迭代时 right = mid 而不是 mid+1
class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length;
        while(left < right) {
            int mid = (left + right) >> 1;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        return -1;
    }
}

小结:上面是二分查找的两种方法,个人认为第一种左闭右闭区间感觉上比较对称,不容易出错。


27. 移除元素(Easy)

题目链接:27. 移除元素

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

思路1:暴力法,遇到一个就将其后面的数依次向前覆盖。
时间复杂度:O(n^2) 空间复杂度:O(1)

解决方案:

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

思路2:对于数组的操作,可以考虑是否适用双指针法。这里快指针遍历查找新数组的元素,慢指针指新数组的最后一位。
时间复杂度:O(n) 空间复杂度:O(1)

解决方案:

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

        return slow;
    }
}

小结:乍一写暴力法还略有失误,因为涉及到遍历时遍历范围的变化。因此也带来一个小思考:最好不要在遍历过程中改变遍历的范围。 过于危险。


35. 搜索插入位置(Easy)

题目链接:35. 搜索插入位置

题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。

思路:
二分法找到目标值索引,若数组中不存在目标值,则可能存在的位置有三处:

  1. 数组左侧
  2. 数组右侧
  3. 数组中间

但是这上述三种情况都会收敛于left == right的情况(最后一次循环中),此时若:

  1. nums[mid] > target,则 target 位置必定是在 mid 左侧,执行的是right = mid - 1, 此时left不变,正好是 target 位置;
  2. nums[mid] < target,target 位置必定在 mid 右侧 + 1,执行的是left = mid + 1;, 正好是 target 位置。

因此找不到 target 时返回 left 的值是合理的。
时间复杂度:O(logn) 空间复杂度:O(1)

解决方案:
这是一种[0, nums.length-1] 的解法,个人认为比较直观易记。

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            
            if(nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }
}

小结:这是一种比较取巧的方法,练习该题目主要目的还是为了锻炼二分法的使用。


总结

  • 二分查找可以有[0, nums.length][0, nums.length)两种范围,两种范围写法略有差异,建议使用前闭后闭区间。
  • 对于数组的操作,可以考虑是否适用双指针法。
  • 最好不要在遍历过程中改变遍历的范围。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第二十二天的算法训练营主要涵盖了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、付费专栏及课程。

余额充值