【编程之路】面试必刷TOP101:二分查找/排序(17-22,Python实现)

【面试必刷TOP101】系列包含:


17.二分查找

在这里插入图片描述

17.1 二分法

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

时间复杂度:O(logn),对长度为 n 的数组进行二分,最坏情况就是取 2 的对数。
空间复杂度:O(1),无额外空间。

17.2 遍历查找法

class Solution:
    def search(self , nums: List[int], target: int) -> int:
        for i in range(0,len(nums)):
            if nums[i] == target:
                return i
        return -1

时间复杂度:O(n),遍历一次数组。
空间复杂度:O(1),无额外空间。

18.二维数组中的查找

在这里插入图片描述

18.1 二分查找

class Solution:
    def Find(self , target: int, array: List[List[int]]) -> bool:
        if len(array) == 0:
            return False
        m = len(array)
        if len(array[0]) == 0:
            return False
        n = len(array[0])
        for i in range(m-1,-1,-1):
            for j in range(0,n):
                if array[i][j] > target:
                    i = i - 1
                elif array[i][j] < target:
                    j = j + 1
                else:
                    return True
        return False

两个 for 循环也可以换成 while。

i = m - 1; j = 0
while i >= 0 and j < n:

时间复杂度:O(m+n),最多经过一行一列。
空间复杂度:O(1),没有使用额外空间。

18.2 暴力遍历

时间复杂度:O(nm),二维数组的遍历。
空间复杂度:O(1),没有使用额外空间。

19.寻找峰值

注意峰值不一定是最大值。

step 1:二分查找首先从数组首尾开始,每次取中间值,直到首尾相遇。
step 2:如果中间值的元素大于它右边的元素,说明往右是向下,我们不一定会遇到波峰,但是那就往左收缩区间。
step 3:如果中间值小于右边的元素,说明此时往右是向上,向上一定能有波峰,那我们往右收缩区间。
step 4:最后区间收尾相遇的点一定就是波峰。
在这里插入图片描述

19.1 二分查找

class Solution:
    def findPeakElement(self , nums: List[int]) -> int:
        left = 0
        right = len(nums) - 1
        while left < right:
            mid = int((left+right)/2)
            # 往左收缩区间
            if nums[mid] > nums[mid+1]:
                right = mid
            # 往右收缩区间
            else:
                left = mid + 1
        return right

时间复杂度:O(logn),二分法最坏情况为O(logn)。
空间复杂度:O(1),无额外空间使用。

20.数组中的逆序对

20.1 暴力法(不会AC)

class Solution:
    def InversePairs(self , data: List[int]) -> int:
        kmod = 1000000007
        num = 0
        n = len(data)
        for i in range(0,n):
            for j in range(i+1,n):
                if data[i] > data[j]:
                    num = num + 1
                    num = num % kmod
        return num

时间复杂度:O(N^2),对于此题显然超时
空间复杂度:O(1)

20.2 归并排序法

在这里插入图片描述

class Solution:
    def MergeSort(self, left: int, right: int, nums: List[int]) -> int:
        if left >= right:
            return 0
        mid = int((left+right)/2)
        count = 0
        count = self.MergeSort(left, mid, nums) + self.MergeSort(mid+1, right, nums)
        temp = [0 for i in range(0,right-left+1)] # 构建一个空数组,其大小 = 待合并的两个数组大小之和,最后会是一个有序数组
        i = left; j = mid + 1; p = 0 # 左边的指针、右边的指针、temp中的指针
        while i <= mid and j <= right: # 左边部分的边界mid,右边部分的边界right。这个while实际就是将两个部分,进行合并的时候排序。
            if nums[i] < nums[j]:
                temp[p] = nums[i]
                p = p + 1
                i = i + 1
            else:
                count = (count + (mid-i+1)) % 1000000007
                temp[p] = nums[j]
                p = p + 1
                j = j + 1
        # 当两个序列合并时,左边或者右边先通过指针移动完毕,全复制进temp后,就需要将另一边的数据,也全部复制给temp。
        if i <= mid:
            while i <= mid:
                temp[p] = nums[i]
                p = p + 1
                i = i + 1
        if j <= right:
            while j <= right:
                temp[p] = nums[j]
                p = p + 1
                j = j + 1
        for k in range(0, right-left+1): # 将合并好的数组再复制回nums,以保证有序。
            nums[left+k] = temp[k]
        return count
    def InversePairs(self , data: List[int]) -> int:
        n = len(data)
        return self.MergeSort(0, n-1, data)

21.旋转数组的最小数字

21.1 二分法

step 1:双指针指向旋转后数组的首尾,作为区间端点。
step 2:若是区间中点值大于区间右界值,则最小的数字一定在中点右边。
step 3:若是区间中点值等于区间右界值,则是不容易分辨最小数字在哪半个区间,比如 [1,1,1,0,1],应该逐个缩减右界。
step 4:若是区间中点值小于区间右界值,则最小的数字一定在中点左边。
step 5:通过调整区间最后即可锁定最小值所在。
在这里插入图片描述

class Solution:
    def minNumberInRotateArray(self , rotateArray: List[int]) -> int:
        left = 0
        right = len(rotateArray) - 1
        while left < right:
            mid = int((left+right)/2)
            if rotateArray[mid] > rotateArray[right]:
                left = mid + 1
            elif rotateArray[mid] == rotateArray[right]:
                right = right - 1
            else:
                right = mid
        return rotateArray[left]

21.2 遍历查找

class Solution:
    def minNumberInRotateArray(self , rotateArray: List[int]) -> int:
        res = rotateArray[0]
        for i in range(0,len(rotateArray)):
            res = min(res,rotateArray[i])
        return res

时间复杂度: O ( n ) O(n) O(n),其中 n n n 为数组大小,遍历一次数组。
空间复杂度: O ( 1 ) O(1) O(1),常数级变量,无额外辅助空间。

22.比较版本号

在这里插入图片描述

22.1 分割查找法

class Solution:
    def compare(self , version1: str, version2: str) -> int:
        a = version1.split('.')
        b = version2.split('.')
        i = 0
        for i in range(max([len(version1),len(version2)])):
            if i < len(a):
                num1 = int(a[i])
            else:
                num1 = 0
            if i < len(b):
                num2 = int(b[i])
            else:
                num2 = 0
            if num1 > num2:
                return 1
            if num1 < num2:
                return -1
        return 0

时间复杂度:O(max(n, m)),其中 m 和 n 分别为两个字符串的长度,流输入和 split 相当于遍历字符串,复杂度选取较高值。
空间复杂度:O(max(n, m)),使用了记录分割后修订号的数组,最坏长度为二者原本字符串长度最大值。

22.2 双指针遍历截取

class Solution:
    def compare(self , version1: str, version2: str) -> int:
        m = len(version1)
        n = len(version2)
        i = 0 ; j = 0
        while i < m or j < n:
            num1 = 0
            while i < m and version1[i] != '.':
                num1 = num1 * 10 + int(version1[i])
                i = i + 1
            i = i + 1 # 跳过点
            
            num2 = 0
            while j < n and version2[j] != '.':
                num2 = num2 * 10 + int(version2[j])
                j = j + 1
            j = j + 1 # 跳过点
        
            if num1 > num2:
                return 1
            if num1 < num2:
                return -1
        return 0

时间复杂度:O(max(n, m)),其中 m 和 n 分别为两个字符串的长度,遍历两个字符串,复杂度选取较高值。
空间复杂度:O(1),常数级变量,无额外辅助空间。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

G皮T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值