数据结构算法刷题(7)二分查找(python实现)

class Solution:

    def search(self, nums: List[int], target: int) -> int:

        left = 0

        right = len(nums) - 1 # 确定闭区间

        while left <= right:

            mid = left + int((right - left)/2)

            if nums[mid] == target:

                return mid

            if nums[mid] < target:

                left = mid + 1

            if nums[mid] > target:

                right = mid - 1

        return -1

 

 思路是找到target的左边界,然后向后寻找,直到元素值不等于target

class Solution:

    def search(self, nums: List[int], target: int) -> int:

        if len(nums) > 0: #确保给的数组长度不为0

            left = 0

            right = len(nums) - 1 #确定闭区间

            index = 0

            while left <= right:

                mid = left + int((right - left)/2)

                if nums[mid] == target: #确定左边界,就要把right向左收缩

                    right = mid - 1

                if nums[mid] < target:

                    left = mid + 1

                if nums[mid] > target:

                    right = mid - 1

            if left < len(nums) and nums[left] == target: #跳出循环时,left=right+1,要去检查left此时是否超出了下标,并且是否为target。

                index = left 

            while index < len(nums) and nums[index] == target:

                index += 1  #让index一直向后,直到不再等于target

            return index - left if (index - left) > 0 else 0 #确保输出的数大于0

        else:

            return 0

二分查找内容总结:

(1)确定闭区间:

  left = 0

 right = len(nums) - 1 

(2)确定循环终止条件:

while left <= right

(3)如果要寻找target:

if  num[mid] == target:

        return

如果要寻找左边界,则将right不断向左收缩,跳出循环后,left = right + 1,要检查left

if num[mid] == target:

        right = mid -1

if left < len(num) and num[left ] == target:

        return 

如果要寻找右边界,则将left不断向右收缩,跳出循环后,right = left -1 ,要检查right

if num[mid] == target:

        left = mid +1

if right > 0 and num[right ] == target:

        return 

(4)while循环中其他情况:小了就改变左边界,大了就改变右边界

if nums[mid] < target:

        left = mid + 1

if nums[mid] > target:

        right = mid - 1

class Solution:

    def searchInsert(self, nums: List[int], target: int) -> int:

        left = 0

        right = len(nums) -1

        while left <= right:

            mid = left + int((right - left)/2)

            if nums[mid] == target:

                right = mid -1 #找左边界

            if nums[mid] < target:

                left = mid + 1

            if nums[mid] > target:

                right = mid - 1

        if left < len(nums) and nums[left] == target or nums[left-1] < target:#找到了该值 或 找到比它大一点的值

            return left

        if left >=  len(nums) : #全部元素都小于该值

            return len(nums)

        else:  #全部元素都大于该值

            return 0

 

当有序数组中有两两相等,有且只有一个数没有出现两次,可以用两数和来找到该数。

class Solution:

    def singleNonDuplicate(self, nums: List[int]) -> int:

        left = 0

        right = len(nums) - 1

        if len(nums) >= 2: #数组中得大于两个数

            while left < right:

                if nums[left] + nums[right] == nums[left+1] + nums[right-1]:

                    left += 2

                    right -= 2

                else:

                    if nums[left] != nums[left+1]:

                        return nums[left]

                    if nums[right] != nums[right-1]:

                        return nums[right]

            if left == right:

                return nums[right] #跳出循环后,如果仍没有找到该数,那该数应该是left和right共同指向

        else:

            return nums[0] 

 

class Solution:

    def searchRange(self, nums: List[int], target: int) -> List[int]:

        left = 0

        right = len(nums) - 1

        index = 0

        if len(nums) > 0:

            while left <= right:

                mid = left + int((right - left)/2)

                if nums[mid] == target:

                    right = mid -1  #找左边界

                if nums[mid] < target:

                    left = mid + 1

                if nums[mid] > target:

                    right = mid - 1

            if left < len(nums) and nums[left] == target: #找到了该值的左边界

                index = left

                while index < len(nums) and nums[index] == target:

                    index += 1

                return [left,index-1]

            else: #没有该值

                return [-1,-1]

        else:

            return [-1,-1]

 

class Solution:

    def mySqrt(self, x: int) -> int:

        if x == 1 or x == 0:

            return x

        else:

            right = int(x/2) #x的平方根应该不会大于x的一半

            left = 1

            while left <= right:

                mid = left + int((right - left)/2)

                if mid*mid == x:

                    return mid

                if mid*mid > x:

                    right = mid - 1

                if mid*mid < x : #在这个范围内有可能找到值

                    if (mid+1)*(mid+1) > x: #如果正好卡在两个数中间

                        return mid

                    else:

                        left = mid + 1 #移动left

class Solution:

    def findPeakElement(self, nums: List[int]) -> int:

        outlist = float('-inf') #负无穷

        nums.insert(0,outlist)

        nums.append(outlist) #以防数组超出下标,所以给数组前后都加上负无穷

        letf = 1

        right = len(nums) - 2

        while letf <= right:

            mid = letf + int((right - letf)/2)

            if nums[mid] > nums[mid-1] and nums[mid] > nums[mid + 1]:

                return mid - 1 #当随机选择的值满足条件时

            if nums[mid] < nums[mid + 1]: 

                letf = mid + 1 #当i 小于i+1时,就往右走,修改左边界,让值往大走

            if nums[mid] > nums[mid + 1]:

                right = mid - 1 #当i大于i+1时,就往左走,修改右边界,让值往大走

思路解析:

在n+1个元素的数组中,每个元素的值[1,n],那至少有一个重复出现的数字,当只有一个数字重复,那它可能重复多次。

首先,我们要寻找某个数的时候,先想一下二分查找。我们需要两个指针:left和right,其次target就是我们要找到的重复元素。最后我们需要找到target的比较条件。

假设count表示对于值i来说,数组nums中有多少个不大于i的数。

class Solution:

    def findDuplicate(self, nums: List[int]) -> int:

        left = 1

        right = len(nums)

        while left <= right:

            mid = int((right+left)/2)

            count = 0

            for i in nums:

                count += i <= mid

            if count > mid:

                right = mid - 1

            if count <= mid:

                left = mid + 1

        return left

 

class Solution:

    def nextGreatestLetter(self, letters: List[str], target: str) -> str:

        left = 0

        right = len(letters) - 1

        while left <= right:

            mid = left + int((right-left)/2)

            if letters[mid] <= target: #如果此时的字母小于等于目标,则向右寻找

                left = mid + 1

            if letters[mid] > target:

                right = mid - 1

        if left < len(letters) : #跳出循环之后判断一下left,如果left超出下标,证明数组中全部数都不符合

            return letters[left]

        else:

            return letters[0]

 

class Solution:

    def reachNumber(self, target: int) -> int:

        s = 0

        n = 0

        target = abs(target) #正负都是一样的结果

        while s < target or (s - target) % 2:#未到终点或者距离终点是奇数

            n += 1

            s += n

        return n

 

也就是一共是两种结果:

(1)走了n步之后,距离目标是偶数,则和向前一直走到达目标的n是一致的。 

(2)走了s步之后,距离目标是奇数。

在经过的步长s>=target而且距离目标只有偶数的时候,此时经过的n步才是最终结果。即 while循环的条件是 s<target or (s-target)%2 ,在没有超过目标或者距离目标是奇数,都要继续走。 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值