leetcode刷题day1:数组part01 数组理论基础,704. 二分查找,27. 移除元素 数组理论基础

第一章  数组part01

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

 数组理论基础  

文章链接:代码随想录


 

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

 704. 二分查找 

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

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

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解:代码随想录

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



 

个人理解方面:掌握704 与 34 的关键有:

  1. 循环不变量

  2. 关于区间的定义

先说循环不变量,在二分查找的程序中 target始终在我们的搜索范围内,其中搜索范围的定义可以有:[l , r], [l, r)  左闭右闭 也就是 l <= target <= r, 也可以是左闭右开 l <= target < r。

在循环开始到循环结束我们一直坚守这个循环不变量。


 

注意点    1. r = len(nums) - 1

我们留意以下两种写法,第一种是左闭右开,细节从第二行就要注意,由于是右开,num[r]并不是我们的搜索范围,所以右边界就等于数组长度,而不是最右元素的下标。

注意点      2. while r >= l:

到底是大于等于还是大于,对于左闭右开来说  [1, 1), [1,2],前者是没有意义的,所以我们不需要考虑等于的情况。 而对于左闭右闭的情况 [1, 1],这种是成立的,所以应该考虑这种情况。

34、在排序数组中查找元素的第一个和最后一个位置

子问题:找出左边界,找出右边界

左边界:也就是大于等于target数字的位置


 

5,7,7,8,8,10

回顾二分查找:

二分查找中的循环不变量是target存在LR 之间。


 

寻找左边界问题:

在while循环结束后的L,表示L左边的所有数字小于target,而R的右边表示大于等于target,最终返回L

本题的循环不变量有点难想明白:

1. L左边的数永远小于num[L],右边R都大于nums[R],而此时的。

2. 需要搜寻的左边界或者说我们的“目标”是第一个大于或者等于target的数

这两个都是我所理解的不变量

由此我们看到代码的写法 当 num[m] >= target 时我们把右边界向左夹逼,最终R的位置正好出于L - 1的位置

def left_bound(nums, target):
    l = 0
    r = len(nums) - 1
    while r >= l:
        m = (l + r) // 2
        if nums[m] >= target:
            r = m - 1
        else:
            l = m + 1
    return l



 

答案即是先找出左边界,再找出左边界+1位置。

容易进入的误区: 是先二分查找找到target,再用while循环左右探索,这样没有将二分查找利用到极致。

在第一次找到左边界后,由于返回的左边界是大于等于target的,如果所有数字都大于target则L = 0 ,此时num[L] != target。而所有数字都小于target的=,将会导致左边界等于数组长度。因此通过寻找左边界就能找到所有 -1的情况。之后再寻找右边界。

def searchRange(nums, target):

    def left_bound(target):
        l = 0
        r = len(nums) - 1
        while r >= l:
            m = (l+r)//2
            # nums[l] <=  (target <= nums[m]) <= nums[r]
            #case1 小于target, 一次比较我们有三种操作,小于 等于 大于,为了不断逼近最左边的target
            #等于的时候应该是r = m - 1, 而target大于num[m]也是r = m - 1 
            #由于答案现在是左闭右闭区间内所以case1 我们选择加一
            if nums[m] < target:
                l = m + 1
            else:
                r = m - 1
        return l
    
    #问题:我的假设是答案在边界中,可是如果num[len(num) - 1] < targe, 我们将会返回数组长度的l
    # 在 [8, 8], target = 9的情况下我们会返回 2
    left_bound1 = left_bound(target)
    print(left_bound1)
    #因此当l等于数组长度时,或者结果不等于target时则返回 -1
    if left_bound1 == len(nums) or nums[left_bound1] != target:
        return [-1, -1]
    right_bound1 = left_bound(target+1) - 1

    return [left_bound1, right_bound1]
    


print(searchRange([8, 8], 6))
# print(searchRange([5,7,7,8,8,10], 8))

 


 

27. 移除元素


 

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

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解:代码随想录

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



 

这一题我第一时间想到的快速排序中的分割步骤,也就是将一边的数大于某数,一边的数小于某数。将数组第一位设为像快速排序分割算法中,作为分割点的数,不需要删除的数放到这个数左边,需要删除的数放到右边。

def removeElement(nums, val):
        count = 0
        current = 1
        current_val = 0
        nums.insert(0,val)
        while current < len(nums):
            if nums[current] != val:
                nums[current], nums[current_val]= nums[current_val],nums[current]
                current_val += 1
                count +=1
            current +=1
         
        print(nums)
        return count


#由分割算法改良的快慢指针

def removeElement(nums, val):

        count = 0

        # fast = 0

        slow = 0

        for fast in range(0, len(nums)):

            if nums[fast] != val:

                nums[fast], nums[slow] = nums[slow], nums[fast]

                slow += 1

                count +=1

           

        print(nums)

        return count

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值