【算法】代码随想录训练营Day1打卡,LeetCode 704二分查找 LeetCode27 移除元素

数组

代码随想录算法训练营Day1任务。

  1. 认识数组
  2. 力扣NO.704 二分查找
  3. 力扣No.27 移除元素

认识数组

数组是一种我们最常用也是最常见的数据结构。
学过数据结构的同学们都知道,数组是一种线性表,数组是一串连续的内存空间中相同类型的数据聚合
接下来我会根据自己的理解写出在C语言中的数组与在JS中的数组

C中的数组

C语言中的数组就是我们在数据结构中所描述的最经典的数组,它有着如下的一些特点

  1. 数组的下标起始位置都是从0开始
  2. 数组是一串连续的地址(内存空间中)
  3. 数组的每一片空间都是同一种数据类型,且数据类型和数组长度在定义时就确定了
  4. 数组无法增加或删除(即改变数组的长度)
  5. 数组只允许覆盖

JS中的数组

数组在JS这一门语言中与C和C++就大有不同了,这是因为JS的数组的底层设计原因。

  1. JS的数组下标起始位置是从0开始(相同)
  2. JS的数组可以存放不同类型的数据(不同)
  3. JS的数组在相同数据类型下是一串连续的地址(相同)
  4. JS的数组在内部存放不同数据类型的情况下,地址是不连续的(不同)
  5. JS的数组允许增加或删除,即可以随意的改变数组的长度(不同)

二分查找

力扣链接:https://leetcode.cn/problems/binary-search/description/
二分查找这道题其实并不难理解,只要理解了二分法就可以了,其中最重要的就是要明白里面的一些坑,到底判断的是left< right 还是 left<=right right的初值是nums.length 还是 nums.lenght-1;在缩小范围的时候到底是加1 还是 不加1

二分法

二分法最重要的是要在一个有序数组中,如果数组无序,则二分法无法判断。
在有序的这个条件下,通过一个左右边界来找出中间值,通过传入的数与中间值的比较来丢弃掉一半的数,进而缩小范围,来达到快速搜索的理念。

在这里我们有两种方法 这两种方法就可以理解上面的一系列坑

  1. 左闭右闭区间
    左闭右闭区间的意思就是,这个二分的范围的左边界和右边界都会纳入这一次的检查范围
    在这里插入图片描述
    根据上图我们就能充分明白左闭右闭区间中的各个判断条件
    即 nums[mid] < target 时 left = mid +1
    nums[mid] > target 时 right = mid -1
    nums[mid] == target 时 return
    而循环条件则是 left <= right 因为如图最后一次查询, 如果是left < right 则没有最后一次判断 直接 结束循环了

  2. 左闭右开区间
    左闭右开即右边界所指向的数不计入当此的检索范围
    ![在这里插入图片描在这里插入图片描述

根据上图我们就能充分明白左闭右开区间中的各个判断条件
即 nums[mid] < target 时 left = mid +1
nums[mid] > target 时 right = mid
nums[mid] == target 时 return
而循环条件则是 left < right 因为right右边界是不计入当次检索范围的 所以若是let <= right的话则是不必要的

C语言解法

左闭右闭区间

int search(int* nums, int numsSize, int target){
 int left = 0;
 int right = numsSize-1;
 int mid = 0;
 while(left <= right){
     mid = (right + left) /2 ;
     if(nums[mid] < target){
         left = mid+1;
     }
     else if(nums[mid] > target){
         right = mid-1;
     }
     else if(nums[mid] == target){
         return mid;
     }
 }
 return -1;
}

左闭右开区间

int search(int* nums, int numsSize, int target){
 int left = 0;
 int right = numsSize;
 int mid = 0;
 while(left < right){
     mid = (right + left) /2 ;
     if(nums[mid] < target){
         left = mid+1;
     }
     else if(nums[mid] > target){
         right = mid;
     }
     else if(nums[mid] == target){
         return mid;
     }
 }
 return -1;
}

JS解法

左闭右闭区间

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left = 0;
    let right = nums.length -1 ; //左闭右开区间  所以right = nums.length 时 不会检查right 
    while(left<=right){
        let mid = left + ((right - left)>>1);

        if(nums[mid] < target){
            left = mid+1;
        }else if(nums[mid] > target){
            //左闭右开
            right = mid-1;
        }else{
            return mid;
        }

    }
    return -1;
};

左闭右开区间

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left = 0;
    let right = nums.length; //左闭右开区间  所以right = nums.length 时 不会检查right 
    while(left< right){
        let mid = left + ((right - left)>>1);

        if(nums[mid] < target){
            left = mid+1;
        }else if(nums[mid] > target){
            //左闭右开
            right = mid;
        }else{
            return mid;
        }

    }
    return -1;
};

移除元素

https://leetcode.cn/problems/remove-element/description/

本题一共我能够想到3种解法

  1. 暴力解
    即用一层for循环遍历每一位,当遍历到的位与nums相同 则用第二层for循环 将后续的每一位覆盖前一位,之后 i-- 继续判断后续的数值,因为所有数都前移了一位 所以i需要减一位 重新判断当前位置的值
  2. 双指针(左右指针)
    该方法是用两个指针,右侧指针指向能够存放数据的空位,先把right移动到不为val的位置,然后判断nums[left] 是否等于val。若是等于 则把nums[left]的值赋值为nums[right],然后再把right–之后 再把right移动到部位val的位置,如此循环到最后返回left值就为新数组的大小
    注意:在每一次循环过后都要再将右指针移动到不为 val的位置,否则会有些事项通不过,因为–了之后可能这个值就为val,再这个地方卡了挺久了,错了很多次。
    3.双指针(快慢指针)
    该方法时用两个指针,当遇到nums[slot] == val时,该指针不动, fast指针继续往前走,并将nums[slot]的值赋值为nums[fast]。循环结束后 slot就是新数组的长度

C语言解法

快慢指针

int removeElement(int* nums, int numsSize, int val){
    int slot =0;
    int fast;
    for(fast =0;fast < numsSize;fast++){
        if(nums[fast] != val ){
            nums[slot++] = nums[fast];
        }
    }
    return slot;
}

JS解法

暴力

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let size = nums.length;
    for (let i=0;i<size;i++){
        if(val == nums[i]){
            for(let j=i+1;j<size;j++){
                nums[j -1] = nums[j]
            }
            i--;
            size--;
        }
    }
    return size;
};

左右指针

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
   let left = 0;
   let right = nums.length -1 ;
   while(nums[right] == val && right >=0)right--; //将右侧指针移动到 右侧不为val的值
   while(left <= right){
       if(nums[left] == val){
           nums[left] = nums[right];
           right--;
       }
       left++;
        while(nums[right] == val && right >=0)right--;
   }
   return left
};

快慢指针

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
   let slot = 0;
   let fast = 0;
   for(fast;fast<nums.length;fast++){
       if(nums[fast]!= val){
           nums[slot++] = nums[fast]
       }
   }
   return slot
};

今日心得

今日完成算法+完成文章的总共时间共2小时,今日的这几道题其实我也不是第一次刷了,在进入训练营之前也刷过一两遍代码随想录,但是没有特别的认真,都是比较粗略的一些想法,然后看一看代码随想录的思路,就稀里胡录的把代码写出来了,也没有进行一些复盘之类的,导致一些踩过的坑再刷仍然会踩,今天用心的理了思路,画了图,对题的记忆和解法更深刻了,对这些题也有一些自己的想法了,不像之前基本看着题没什么思路

  • 0
    点赞
  • 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题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值