LeetCode刷题笔记(算法思想 二)

三、贪心思想

455. 分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

注意:

你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。

示例 1:

输入: [1,2,3], [1,1]

输出: 1

解释: 
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

示例 2:

输入: [1,2], [1,2,3]

输出: 2

解释: 
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

思路: 利用贪心策略,给一个孩子的饼干应当尽量小并且又能满足孩子,这样大饼干才能拿来满足胃口较大的孩子。分别对孩子胃口和饼干进行排序,利用双指针,如果当前饼干能满足当前孩子则双指针指向下一位置,如果当前饼干不能满足当前孩子,则饼干指针指向下一位看是否能满足当前孩子。

代码实现:

class Solution:
    def findContentChildren(self, g, s):
        g.sort()
        s.sort()
        i, j, res = 0, 0, 0
        while i < len(g) and j < len(s):
            if s[j] >= g[i]:
                res += 1
                i += 1
            j += 1
        return res

435. 无重叠区间

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:

可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

示例 1:

输入: [ [1,2], [2,3], [3,4], [1,3] ]

输出: 1

解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

输入: [ [1,2], [1,2], [1,2] ]

输出: 2

解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

输入: [ [1,2], [2,3] ]

输出: 0

解释: 你不需要移除任何区间,因为它们已经是无重叠的了

思路: 将列表中每个区间按照第二个数从小到大排序,定义两个指针i, j,分别比较某区间第二个数和该区间下一区间第一个数的大小。该问题类似活动选择问题,贪心的选择区间结尾尽量小的,因为区间结尾越小,留给后面的空间就越大。

代码实现:

class Solution:
    def eraseOverlapIntervals(self, intervals):
        intervals.sort(key= lambda x:x[1])
        print(intervals)
        i, j, res = 0, 1, 0
        while i < len(intervals) and j < len(intervals):
            if intervals[j][0] < intervals[i][1]:
                res += 1
                j += 1
            else:
                i = j
                j += 1
        return res

452. 用最少数量的箭引爆气球

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。

一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

Example:

输入:
[[10,16], [2,8], [1,6], [7,12]]

输出:
2

解释:
对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。

思路: 利用贪心策略,贪心问题大多是对区间结尾排序。如果某区间的开始比上一区间的结尾小或等于,则不需要增加箭;若某区间的开始比上一区间的结尾大,则需要增加一只箭。

代码实现:

class Solution:
    def findMinArrowShots(self, points):
        if len(points) == 0:
            return 0
        points.sort(key= lambda x:x[1])
        print(points)
        res = 1
        end = points[0][1]
        for i in range(1,len(points)):
            if points[i][0] > end:
                res += 1
                end = points[i][1]
        return res

406. 根据身高重建队列

假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。

注意:总人数少于1100人。

示例

输入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

输出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

思路: 将身高按照降序排列,人数按照升序排列。遍历二维数组,从高个子到低个子,逐个把人插到以人数为索引的数组中即可完成重建。

代码实现:

class Solution:
    def reconstructQueue(self, people):
        people.sort(key= lambda x:(-x[0],x[1]))
        output = []
        for i in people:
            output.insert(i[1],i)
        return output

121. 买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

思路: 定义一个指针,记录股票价格的最低点,从这个最低点往后找最大的价格卖出,就能获得最大利润。

代码实现:

class Solution:
    def maxProfit(self, prices):
        if len(prices) == 0:
            return 0
        min_p = prices[0]
        res = 0
        for i in range(1,len(prices)):
            if prices[i] <= min_p:
                min_p = prices[i]
            else:
                res = max(res, prices[i] - min_p)
        return res

122. 买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例 2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

1 <= prices.length <= 3 * 10 ^ 4
0 <= prices[i] <= 10 ^ 4

思路: 对于连续上涨交易日,股票价格分别为p1,p2…pn,则第一天买最后一天卖收益最大,即pn-p1;等价于每天都买卖,即pn-p1 = (p2-p1)+(p3-p2)+…+(pn-pn-1)。对于连续下降交易日,不买卖即收益最大,即不会亏钱。所以只用遍历整个列表,所有上涨交易日都买卖(即赚到所有利润),所有下降交易日都不买卖(永不亏钱)。

代码实现:

class Solution:
    def maxProfit(self, prices):
        profit = 0
        for i in range(1,len(prices)):
            if prices[i] - prices[i-1] > 0:
                profit += prices[i] - prices[i-1]
        return profit

605. 种花问题

假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。

示例 1:

输入: flowerbed = [1,0,0,0,1], n = 1
输出: True

示例 2:

输入: flowerbed = [1,0,0,0,1], n = 2
输出: False

注意:

数组内已种好的花不会违反种植规则。
输入的数组长度范围为 [1, 20000]。
n 是非负整数,且不会超过输入数组的大小。

思路: 遍历数组,如果某位置是0并且它的两边都是0,计数器就加一,当计数器 >= n时,返回True;否则返回False。为了方便判断边界,给数组的首尾分别添加0。

代码实现:

class Solution:
    def canPlaceFlowers(self, flowerbed, n):
        flowerbed.insert(0,0)
        flowerbed.append(0)
        print(flowerbed)
        count, i = 0, 1
        while i < len(flowerbed)-1:
            if flowerbed[i] == 0 and flowerbed[i-1] == 0 and flowerbed[i+1] == 0:
                count += 1
                i += 1
            if count >= n:
                return True
            i += 1
        return False

392. 判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

示例 1:
s = “abc”, t = “ahbgdc”

返回 true.

示例 2:
s = “axc”, t = “ahbgdc”

返回 false.

后续挑战 : 如果有大量输入的 S,称作S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

思路: 利用双指针,如果匹配则两个指针都往后移,如果不匹配,则t字符串的指针后移,如果匹配次数等于s字符串的长度则可以直接返回True。

代码实现:

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        if len(s) == 0:
            return True
        # s = "abc", t = "ahbgdc"
        i, j,count = 0, 0, 0
        while i < len(s) and j < len(t):
            if s[i] == t[j]:
                count += 1
                i += 1
            if count == len(s):
                return True
            j += 1
        return False

665. 非递减数列

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

示例 1:

输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

示例 2:

输入: nums = [4,2,1]
输出: false
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

说明:

1 <= n <= 10 ^ 4
- 10 ^ 5 <= nums[i] <= 10 ^ 5

思路: 循环遍历数组,如果某数小于他前面的数,计数器+1,并且如果该数后一位置的数小于该数前一位置的数且该数小于该数前两个位置的数,直接返回False,如果计数器大于1,也返回False,循环正常结束返回True。

详细解释可看这篇题解:https://leetcode-cn.com/problems/non-decreasing-array/solution/python3xiang-jie-bu-gai-bian-shu-zu-yuan-su-zhi-ch/

代码实现:

class Solution:
    def checkPossibility(self, nums):
        # 输入: nums = [4,2,3]      输出: true
        count = 0
        for i in range(1,len(nums)):
            if nums[i] < nums[i-1]:
                count += 1
                if i+1 < len(nums) and i-2 >= 0:
                    if nums[i+1] < nums[i-1] and nums[i-2] > nums[i]:
                        return False
            if count > 1:
                return False
        return True

53. 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶: 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

思路: 遍历列表,如果一个数与它前一位置的和比它本身要大,则把它的值改为这两个之和。最后这个列表中最大的数就为最大子序和,因为说明它一路加过来,前一位置都是正数,自然是最大子序和。

代码实现:

class Solution:
    def maxSubArray(self, nums):
        if len(nums) == 0:
            return 0
        for i in range(1,len(nums)):
            if nums[i-1] + nums[i] > nums[i]:
                nums[i] = nums[i-1] + nums[i]
        print(nums)
        return max(nums)

763. 划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。

示例 1:

输入: S = "ababcbacadefegdehijhklij"
输出: [9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。

注意:

S的长度在[1, 500]之间。
S只包含小写字母'a'到'z'。

思路: 首先利用enumerate将每个字符最后出现的下标保存在字典(哈希表)里,定义两个指针left, right代表每个区间的开头和结尾,循环遍历字符串,将遍历到的每个字符的下标和right相比较,取其中较大的作为新的right。当循环的 i==right 时,说明这是其中一个区间,把区间长度保存在结果列表,并且更新区间的开头 left = i+1。

代码实现:

class Solution:
    def partitionLabels(self, S: str):
        hash = {char: i for i, char in enumerate(S)}   # 存每个字符最后出现的下标
        # 挨个把下标和字符加到字典hash里,相同的字符,后面的下标会覆盖掉前面的下标
        print(hash)
        left, right = 0, 0  # 表示每个区间的开头和结尾
        res = []
        for i,char in enumerate(S):
            right = max(right, hash[char])
            if i == right:
                res.append(i-left+1)
                left = i+1
        return res

四、二分查找

69. x 的平方根

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2

示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 
     由于返回类型是整数,小数部分将被舍去。

思路:
在这里插入图片描述
利用exp()和log()函数,math.exp(5) 表示求e的5次方是多少;math.log(5) 表示求ln5是多少。注意x=0的情况,由于取整的缘故(直接舍弃小数点后面的值),有可能得到的结果+1更接近x的平方根,所以要比较 结果 和 结果+1 谁是真正的平方根。

二分法思路:一个数x的平方根肯定小于等于这个数本身,在0到x的范围内利用二分法查找,如果mid平方大于x,right = mid-1;如果mid平方小于x,left = mid+1;当left > right时,循环退出,返回right。

代码实现:

# ---------- 二分法 -------------
class Solution:
    def mySqrt(self, x: int) -> int:
        left, right = 0, x
        while left <= right:
            mid = left + (right - left) // 2
            mid_square = mid ** 2
            if mid_square == x:
                return mid
            elif mid_square > x:
                right = mid - 1
            else:
                left = mid + 1

        return right
# ---------- 数学公式法 ---------
class Solution:
    def mySqrt(self, x: int) -> int:
        # math.exp(5) 表示求e的5次方是多少
        # math.log(5) 表示求ln5是多少
        import math
        if x == 0:
            return 0
        sq = math.exp(0.5 * math.log(x))
        sq = int(sq)
        sq1 = sq*sq
        sq2 = (sq+1)*(sq+1)
        if sq2 <= x:
            return sq + 1
        else:
            return sq

744. 寻找比目标字母大的最小字母

给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。

在比较时,字母是依序循环出现的。举个例子:

如果目标字母 target = 'z' 并且字符列表为 letters = ['a', 'b'],则答案返回 'a'

示例:

输入:
letters = ["c", "f", "j"]
target = "a"
输出: "c"

输入:
letters = ["c", "f", "j"]
target = "c"
输出: "f"

输入:
letters = ["c", "f", "j"]
target = "d"
输出: "f"

输入:
letters = ["c", "f", "j"]
target = "g"
输出: "j"

输入:
letters = ["c", "f", "j"]
target = "j"
输出: "c"

输入:
letters = ["c", "f", "j"]
target = "k"
输出: "c"

提示:

letters长度范围在[2, 10000]区间内。
letters 仅由小写字母组成,最少包含两个不同的字母。
目标字母target 是一个小写字母。

**思路:**当目标字母大于等于数组最后一个元素或者目标字母小于数组第一个元素时,均返回数组第一个元素。循环遍历数组进行二分查找,不断更新left和right以缩小查找区域。

代码实现:

class Solution:
    def nextGreatestLetter(self, letters, target: str):
        if target >= letters[-1] or target < letters[0]:
            return letters[0]
        left = 0
        right = len(letters)-1
        while left <= right:
            mid = (left + right) // 2
            if letters[mid] <= target:
                left = mid + 1
            else:
                right = mid - 1
        return letters[left]

540. 有序数组中的单一元素

给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

示例 1:

输入: [1,1,2,3,3,4,4,8,8]
输出: 2

示例 2:

输入: [3,3,7,7,10,11,11]
输出: 10

注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。

思路: 本题使用二分查找,当mid为偶数时,nums[mid] 前面有 偶数个 元素,此时分为两种情况:
(1) nums[mid] == nums[mid+1],例如:数组 [1, 1, 2, 2, 3, 3, 4, 5, 5], mid = 4, nums[4] == nums[5] == 3;说明 nums[mid] 前面的元素都出现了两次,所以要在 nums[mid] 后面找单一元素,于是更新区间:left = mid + 1;
(2) nums[mid] != nums[mid+1],例如:数组 [1, 1, 2, 3, 3, 4, 4, 5, 5], mid = 4, (nums[4] == 3) != (nums[5] == 4);说明 nums[mid] 前面的元素中存在单一元素,于是更新区间:right = mid - 1;

当mid为奇数时,nums[mid] 前面有 奇数个 元素,此时同样也分为两种情况:
(1) nums[mid] == nums[mid+1],例如:数组 [1, 1, 2, 3, 3, 4, 4, 5, 5], mid = 3, nums[3] == nums[4] == 3;说明 nums[mid] 前面的元素中存在单一元素,于是更新区间:right = mid - 1;
(2) nums[mid] != nums[mid+1],例如:数组 [1, 1, 2, 2, 3, 3, 4, 5, 5], mid = 3, (nums[3] == 2) != (nums[4] == 3);说明单一元素出现在 nums[mid] 的后面,于是更新区间:left = mid + 1。

注意边界条件,当 mid + 1 超出边界时,说明单一元素是数组的最后一个元素。

代码实现:

class Solution:
    def singleNonDuplicate(self, nums):
        if len(nums) == 1:
            return nums[0]
        left = 0
        right = len(nums)-1
        while left <= right:
            mid = (left + right) // 2
            if mid % 2 == 0 and mid + 1 < len(nums):  # mid是偶数(下标为奇数)
                if nums[mid] == nums[mid+1]:   # mid前面没有单一元素
                    left = mid + 1
                else:   # mid前面有单一元素
                    right = mid - 1
            elif mid % 2 != 0 and mid + 1 < len(nums):   # mid是奇数(下标为偶数)
                if nums[mid] == nums[mid+1]:  # mid前面有单一元素
                    right = mid - 1
                else:   # mid前面没有单一元素
                    left = mid + 1
            else:
                return nums[mid]
        return nums[left]

278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例:

给定 n = 5,并且 version = 4 是第一个错误的版本。

调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true

所以,4 是第一个错误的版本。 

思路: 用二分查找,如果isBadVersion(mid) == True,说明第一个错误的出现在mid左边,更新区间:right = mid - 1;如果isBadVersion(mid) == False,说明第一个错误的出现在mid右边,更新区间:left = mid + 1。

代码实现:

# The isBadVersion API is already defined for you.
# @param version, an integer
# @return a bool
# def isBadVersion(version):

class Solution:
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """
        left = 0
        right = n-1
        while left <= right:
            mid = (left + right) // 2
            if isBadVersion(mid):
                right = mid - 1
            else:
                left = mid + 1
        return left

153. 寻找旋转排序数组中的最小值

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

示例 1:

输入: [3,4,5,1,2]
输出: 1

示例 2:

输入: [4,5,6,7,0,1,2]
输出: 0

思路: 用二分查找,如果数组mid的数大于数组最后一个数,说明数组旋转后较小的一部分升序序列在mid右边,更新区间:left = mid + 1;如果数组mid的数小于数组最后一个数,说明数组旋转后较小的一部分升序序列在mid左边,更新区间:right = mid - 1。

代码实现:

class Solution:
    def findMin(self, nums):
        left = 0
        right = len(nums)-1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] > nums[-1]:
                left = mid + 1
            else:
                right = mid - 1
        return nums[left]

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

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

思路: 使用二分查找,分别查找target的左侧边界和右侧边界,注意检查出界情况。这篇文章对二分查找的讲解十分详细:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/er-fen-cha-zhao-suan-fa-xi-jie-xiang-jie-by-labula/

代码实现:

class Solution:
    def searchRange(self, nums, target):
        start = self.left_bound(nums,target)
        end = self.right_bound(nums,target)
        return [start, end]

    # 寻找左边界:
    def left_bound(self, nums, target):
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] > target:
                right = mid - 1
            elif nums[mid] < target:
                left = mid + 1
            elif nums[mid] == target:
                right = mid - 1  # 收缩右边界
        if left >= len(nums) or nums[left] != target:
            return -1
        return left

    # 寻找右边界:
    def right_bound(self, nums, target):
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] > target:
                right = mid - 1
            elif nums[mid] < target:
                left = mid + 1
            elif nums[mid] == target:
                left = mid + 1  # 收缩左边界
        if right < 0 or nums[right] != target:
            return -1
        return right

================================================================
以上为个人学习笔记总结,供学习参考交流,未经允许禁止转载或商用。

  • 题目全部出自LeetCode官网:https://leetcode-cn.com/
  • 题目分类参考这篇文章:https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值