2023-11-29 二分查找和移除元素

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

704. 二分查找:时间复杂度O(log n)

核心:注意使用二分法的区间!第一种[left,right]属于左闭右闭 left=0 right=x-1 while left==<=======right left=mid+1 right=mid-1 !

704.二分查找

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left=0
        right=len(nums)-1
        while left<=right:
            mid=left+(right-left)//2 # 是防止整数过大导致int溢出 
            if nums[mid]==target:
                return mid
            elif nums[mid]>target:
                right=mid-1
            else:
                left=mid+1
        return -1

第二种[left,right)属于左闭右开 left=0 right=x while left==<==right left=mid+1 right=mid

704.二分查找1

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left=0
        right=len(nums)
        while left<right:
            mid=left+(right-left)//2 # 是防止整数过大导致int溢出  习惯使用/是真除法
            if nums[mid]==target:
                return mid
            elif nums[mid]>target:
                right=mid
            else :
                left=mid+1
        return -1

需要注意的是:python中//才是整除,别与其他语言混乱了!

时间复杂度:O(log n)

拓展题

35. 搜索插入位置
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        # 使用左右指针
        left,right=0,len(nums)-1
        while(left<=right):
            mid = left+(right-left)//2
            if nums[mid]==target:
                return mid
            elif nums[mid]>target:
                right=mid-1
            elif nums[mid]<target:
                left=mid+1
        # 关键是 没找到的时候left往前一步了 so left = right
        left=right
        return left+1
34. 在排序数组中查找元素的第一个和最后一个位置

这道题有意思!利用二分法来寻找左右边界,需要一个临时的值接受每一次出现相等target

# 第一种方法:利用二分法,寻找出左右边界
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        # 核心问题:是寻找左右边界
        left,right=0,len(nums)-1
        # 定义左右边界问题
        leftboundary = -2
        rightboundary = -2
        # 左边界
        def find_left_bounary(left, right):
            temp_result = -2    # 去接住最后一次位置
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] >= target: 	# 注意边界【自己画个图走一下了理解】
                    right = mid - 1
                    temp_result = mid
                else:
                    left =mid + 1
            return temp_result
        # 有边界
        def find_right_boundary(left, right):
            temp_result = -2
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] <= target:
                    left = mid + 1
                    temp_result = mid
                else:
                    right =mid - 1
            return temp_result
        leftboundary = find_left_bounary(left,right)
        rightboundary = find_right_boundary(left,right)
        # 如果target不在其中 那么leftboundary和rightboundary一定为初始值的
        if leftboundary == -2 or rightboundary == -2:
            return [-1,-1]
        if (rightboundary - leftboundary) >= 0:
            return [leftboundary,rightboundary]
        return [-1,-1]

            
# 第二种方法:使用二分法找到一个与target相等的值,然后使用左右指针寻找边界
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        left, right = 0, len(nums) - 1
        temp_index = -1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] == target:
                temp_index = mid
                # 从找target的下标开始沿着左右寻找
                left,right = mid, mid
                break
            elif nums[mid] > target:
                right = mid - 1
            else:
                left = mid + 1
        if temp_index == -1:
            return [-1, -1]
        # 左右寻找边界
        while left >= 0 and nums[left] == target:
            left -= 1
        while right <= len(nums) - 1 and nums[right] == target:
            right += 1
        return [left + 1, right - 1]

# 第三种方法:找到第一次出现的target的下标也就是左边界了,然后找到target+1的下标值,也就是右边界的下标值
 def searchRange(self, nums: List[int], target: int) -> List[int]:
        def left_margin():
            left, right = 0, len(nums) - 1
            temp_index = -2
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] >= target:
                    right = mid - 1
                    temp_index = mid
                else:
                    left = mid + 1
            return temp_index
        # 找出target+1的位置,
        def right_margin(target):
            left, right = 0, len(nums) - 1
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] == target:
                    index_first = mid
                    right = mid - 1
                elif nums[mid] < target:
                    left = mid + 1
                else:
                    right = mid - 1
            left = right	# 注意这个就好了
            return left 
            
        left_index = left_margin()
        print(left_index)
        right_index = right_margin(target+1)
        print(right_index)
        if left_index == -2:
            return [-1,-1]
        elif nums[left_index] == target and nums[right_index] == target:
            return [left_index, right_index]
        else:
            return [-1, -1]

27. 移除元素

第一想法结题:使用标志位,但是移动元素麻烦做不了!使用双指针来做,列举指针两端的各种可能!

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        # 使用快慢指针来做  列举四种的可能性
        slow,fast=0,0
        while fast<len(nums):
            if nums[slow] == val and nums[fast]==val:
                fast+=1
            elif nums[slow] == val and nums[fast] !=val:
                nums[slow],nums[fast] = nums[fast],nums[slow]
                slow +=1
                fast +=1
            elif nums[slow] !=val and nums[fast] ==val:
                slow +=1
            elif nums[slow] !=val and nums[fast] !=val:
                fast +=1
                slow +=1
        return slow

优化一下:

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow,fast=0,0
        while fast<len(nums):
            if nums[fast]!=val:
                nums[slow]=nums[fast] # 不用考虑移动后续的元素!只需要有效的前面元素而已
                slow +=1
        	fast +=1
        return slow

或者可以

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow=0
        for fast in range(len(nums)):
            if nums[fast]!=val:
                nums[slow]=nums[fast]
                slow+=1
        return slow