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

今日任务

第一章 数组part01

数组理论基础,704. 二分查找,27. 移除元素

详细布置
数组理论基础

文章链接:代码随想录

题目建议: 了解一下数组基础,以及数组的内存空间地址,数组也没那么简单。

704. 二分查找

题目建议: 大家今天能把 704.二分查找 彻底掌握就可以,至于 35.搜索插入位置 和 34. 在排序数组中查找元素的第一个和最后一个位置 ,如果有时间就去看一下,没时间可以先不看,二刷的时候在看。

先把 704写熟练,要熟悉 根据 左闭右开,左闭右闭 两种区间规则 写出来的二分法

题目链接:. - 力扣(LeetCode)

文章讲解:代码随想录

视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili

核心问题:

        正确理解对于left->right 双指针区域的含义,那么每次缩小区间时,都需要保障最终取值都可能在缩小后的每个位置上,也就是文章中提到的: 循环不变量规则

27. 移除元素

题目建议: 暴力的解法,可以锻炼一下我们的代码实现能力,建议先把暴力写法写一遍。 双指针法 是本题的精髓,今日需要掌握,至于拓展题目可以先不看。

题目链接:. - 力扣(LeetCode)

文章讲解:代码随想录

视频讲解:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili

public int removeElement(int[] nums, int val) { 
        if(nums == null || nums.length == 0)return 0;
        int i=0,k=0;
        while(i < nums.length) if(nums[i++]!=val) nums[k++] = nums[i-1];
        return k;
}
977.有序数组的平方

题目建议: 本题关键在于理解双指针思想

题目链接:. - 力扣(LeetCode)

文章讲解:代码随想录

视频讲解: 双指针法经典题目 | LeetCode:977.有序数组的平方_哔哩哔哩_bilibili

public static int[] sortedSquares(int[] nums) {
        if(nums == null || nums.length == 0){
            return nums;
        }
        int l=0,r=nums.length-1,t=nums.length-1;
        int[] res = new int[nums.length];
        while(l<=r){
            int tmpR = nums[r] * nums[r];
            int tmpL = nums[l] * nums[l];
            if(tmpR>tmpL){
                res[t--] = tmpR;
                r--;
            }else{
                res[t--] = tmpL;
                l++;
            }
        }
        return res;
}

总结:

【数组】这个数据结构我也希望你能真正理解它

1. 定义与基本概念
  • 定义‌:‌数组是一种基础的数据结构,‌用于在计算机内存中连续存储相同类型的数据。‌每个元素可以通过索引(‌或下标)‌进行访问,‌索引通常是从0开始的。‌
  • 特点‌:‌固定大小、‌连续存储、‌随机访问。‌
2. 数组的内存表示
  • 连续存储‌:‌数组中的元素在内存中连续存放,‌这使得通过索引访问元素非常高效,‌因为可以通过简单的数学计算(‌索引乘以元素大小)‌直接定位到元素的内存地址。‌
  • 内存分配‌:‌在声明数组时,‌需要指定数组的大小(‌或长度)‌,‌这决定了数组在内存中占用的空间大小。‌一旦分配,‌数组的大小就不能改变(‌静态数组)‌。‌
3. 数组的操作
  • 访问元素‌:‌通过索引访问数组中的元素,‌如arr[i]。‌
  • 插入元素‌:‌在静态数组中插入元素通常涉及元素的移动,‌因为需要为新元素腾出空间。‌这可能导致效率问题,‌特别是在数组接近满时。‌
  • 删除元素‌:‌同样,‌删除元素也需要移动其他元素来填补被删除元素留下的空位。‌
  • 遍历数组‌:‌通过循环遍历数组中的每个元素,‌执行相应的操作。‌
4. 数组的应用场景
  • 基础数据结构‌:‌数组是许多高级数据结构(‌如栈、‌队列、‌链表、‌哈希表等)‌的基础。‌
  • 数据存储‌:‌用于存储一系列有序的数据项,‌如学生的成绩、‌商品的库存等。‌
  • 算法实现‌:‌在排序、‌搜索等算法中,‌数组是常用的数据结构。‌
5. 数组的变种
  • 动态数组‌(‌如C++中的std::vector,‌Java中的ArrayList)‌:‌动态数组在需要时可以自动调整大小,‌解决了静态数组大小固定的问题。‌
  • 多维数组‌:‌用于存储二维或更高维度的数据,‌如矩阵、‌图像等。‌
  • 关联数组‌(‌或映射、‌字典)‌:‌虽然不严格属于数组范畴,‌但它们在许多编程语言中以类似数组的方式实现,‌允许使用非整数索引(‌如字符串)‌来访问元素。‌
6. 深入理解数组的复杂度
  • 时间复杂度‌:‌访问数组元素的时间复杂度为O(1),‌因为可以直接通过索引定位到元素。‌但在插入和删除元素时,‌如果数组大小固定,‌可能需要O(n)的时间复杂度(‌因为需要移动元素)‌。‌
  • 空间复杂度‌:‌静态数组的空间复杂度为O(n),‌其中n是数组的大小。‌动态数组的空间复杂度可能更高,‌因为需要额外的空间来存储数组的大小和可能的扩容空间。‌
7. java中创建数组的方式

说起来很可笑,第一次面试的时候,不让用ide,我忘记怎么创建数组了

声明数组并分配空间,初始化数组元素:

int[] numbers = new int[5]; // 创建一个长度为5的整数数组
numbers[0] = 1; // 给数组元素赋值
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;

使用花括号(初始化时直接赋值):

int[] numbers = {1, 2, 3, 4, 5}; // 创建并初始化一个整数数组

使用匿名数组:

new int[]{1, 2, 3, 4, 5}; // 匿名数组,通常用在不需要引用的情况下

基本类型数组的简写(自动推导类型):

int[] numbers = new int[]{1, 2, 3, 4, 5}; // 使用var关键字自动推导类型

基本类型数组的简写(使用花括号):

int[] numbers = {1, 2, 3, 4, 5}; // 使用var关键字和花括号初始化数组

多维数组:

int[][] multiNumbers = {
    {1, 2, 3}, 
    {4, 5, 6}
}; // 创建一个二维整数数组

使用Arrays类的静态方法fill()来初始化数组:

int[] numbers = new int[5];
Arrays.fill(numbers, 10); // 将数组所有元素初始化为10

使用Arrays类的静态方法copyOf()来复制数组:

int[] original = {1, 2, 3};
int[] copy = Arrays.copyOf(original, 5); // 复制数组,长度为5

循环不变量规则:

循环不变量(loop invariant)是指在循环体内、每次迭代均保持为真的某种性质,它常被用来证明程序或算法的正确性。

        对于一个给定的循环不变量,需要遵循以下三个关键属性:

  1. 初始化:在循环的第一次迭代之前,循环不变量为真。这意味着在循环开始时,该性质就已经成立。
  2. 保持:如果在循环的一次迭代之前循环不变量为真,那么在下一次迭代之前循环不变量同样为真。也就是说,只要前一轮迭代满足该性质,那么下一轮迭代开始前也会满足该性质。这类似于数学归纳法中的归纳步。
  3. 终止:当循环结束时,不变量能够提供有用的属性,用于帮助证实算法是正确的。通常,需要保证在循环结束时“循环不变量”和“循环终止条件”同时成立。这一点与数学归纳法有所不同,数学归纳法常采用无限的归纳步,而循环不变量的归纳往往随着循环的终止而结束
        示例:

                二分法的循环不变量: 每次迭代时目标元素始终在当前搜索区间内

                冒泡排序的循环不变量: 每次迭代后,子序列末尾已排好序的元素个数是不断增加

                插入排序的循环不变量: 每次迭代后,前面已排好序的子序列始终保持有序

                快排的循环不变量: 每次迭代后,选定的基准元素左侧的元素都不大于它,右侧的元素都不小于它。                

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值