【ACWING】【1】排序(快速+归并)+二分(整数二分+浮点二分)

1. 排序算法

快速排序

时间复杂度:最优Onlogn,最坏On2
稳定性:不稳定,相同元素相对次序可能发生改变,将xi变为[xi,i]进行排序就可变为稳定
选择一个数作为anchor,按照比anchor大小划分为左右两部分,再在左右两部分内再选anchor细分,直到只剩一个元素(即lr指针相等)。

class Solution(object):
    def sortArray(self, nums):
        def quicksort(nums, l, r):
            if l >= r: return
            # 选择left作为哨兵
            pivot, i, j = l, l+1,r
            # 选择中心作为分界值,因为如果选左边的话对于顺序数组会超时
            mid = (l+r)>>1
            nums[pivot], nums[mid] = nums[mid],nums[pivot]
            while i <= j:
                while i <= j and nums[i] <= nums[pivot]: i+=1  # 找到大于nums[pivot]的数
                while i <= j and nums[j] > nums[pivot]: j-=1   # 找到小等于nums[pivot]的数
                if i <= j:
                    nums[i],nums[j] = nums[j], nums[i]
                    i, j = i+1, j-1
            # 当跳出循环时i的左边全都是小等于分界的数
            # 右边全都是大于分界的数
            # j||i 交换第一个数和j让还原分界值
            nums[pivot], nums[j] = nums[j], nums[pivot]
            # 递归左右两部分
            quicksort(nums,l,j-1)
            quicksort(nums,j+1,r)
            return
        quicksort(nums,0,len(nums)-1)
        return nums
sol = Solution()
nums = [5,4,3,2,2,1]
print(sol.sortArray(nums))

最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

class Solution(object):
    def getLeastNumbers(self, arr, k):
        """
        :type arr: List[int]
        :type k: int
        :rtype: List[int]
        """
        def quicksort(arr,l,r,k):
            if l >= r: return
            pivot, i, j, mid = l, l+1, r, (l+r)/2
            arr[pivot], arr[mid] = arr[mid], arr[pivot]
            while i <= j:
                while i <= j and arr[i] <= arr[pivot]: i += 1
                while i <= j and arr[j] > arr[pivot]: j -= 1
                if i <= j:
                    arr[i], arr[j] = arr[j], arr[i]
                    i += 1
                    j -= 1
            arr[pivot], arr[j] = arr[j], arr[pivot]
            if j == k-1: return
            # 分界线左边没到k个,右边需要再分
            elif j < k-1: quicksort(arr,j+1,r,k)
            # 分界线左边超过k个,在左边中再分
            elif j > k-1: quicksort(arr,l,j-1,k)
        quicksort(arr,0,len(arr)-1,k)
        return arr[:k]

归并排序

时间复杂度:nlogn
稳定性:稳定排序
自下而上进行排序,归的过程就是不断拆分成小部分,直到只剩一个元素,合则是合并两个有序子数组。

class Solution(object):
    def sortArray(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        def merge(nums,left,mid,right):
            i, j = left, mid+1
            temp = [] # 创建临时数组存合并结果
            # 判断两个有序数组大小 小的加入合并结果
            while i <= mid and j <= right:
                if nums[i] > nums[j]:
                    temp.append(nums[j])
                    j += 1
                else:
                    temp.append(nums[i])
                    i += 1
            # 若还有剩余没加入直接extend全部接入temp即可
            if i <= mid:
                temp.extend(nums[i:mid+1])
            if j <= right:
                temp.extend(nums[j:right+1])
            nums[left:right+1] = temp
            return
        def merge_sort(nums,left,right):
            if left >= right: return nums

            mid = (left+right)>>1
            # 归
            merge_sort(nums,left,mid)
            merge_sort(nums,mid+1,right)
            # 并
            merge(nums,left,mid,right)
            return nums
        merge_sort(nums,0,len(nums)-1)
        return nums
sol = Solution()
nums = [5,4,3,2,2,1]
sol.sortArray(nums)

数组中的逆序对

常规归并排序算法模板+合并时记录逆序对

class Solution(object):
    def reversePairs(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        self.count = 0
        def merge(nums,left,mid,right):
            l, r = left, mid+1
            temp = []
            while l <= mid and r <= right:
                if nums[l] <= nums[r]:
                    temp.append(nums[l])
                    l += 1
                else:
                    # 当右边的数组要加入时 说明左指针l右边的数都比当前要加入的值大
                    # 故当前r指的数和左边l及右侧的数构成逆序对
                    # 故总的逆序对加上mid-l+1
                    self.count += mid-l+1  
                    temp.append(nums[r])
                    r += 1
            if l <= mid: temp.extend(nums[l:mid+1])
            if r <= right: temp.extend(nums[r:right+1])
            nums[left:right+1] = temp
            return
        def merge_sort(nums,left,right):
            if left >= right: return
            mid = (left+right)>>1
            merge_sort(nums,left,mid)
            merge_sort(nums,mid+1,right)
            merge(nums,left,mid,right)
            return
        merge_sort(nums,0,len(nums)-1)
        return self.count

2. 二分

整数二分

二分法的本质是左右两区间按照某个不同性质进行划分。mid的归属是二分法的关键所在。

数的范围

题目描述
给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。
对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。
如果数组中不存在该元素,则返回“-1 -1”。
输入: nums=[1, 2, 2, 3, 3, 4] query = 3  5
输出: [3,4] [-1,-1]

思路:求k的起始位置和终止位置即为大于等于k的第一个数和小于等于k的最后一个数。

def find_range(nums, k):
    left, right = 0, len(nums)-1
    # 找到大等于target的第一个数位置
    while left < right:
        mid = (left+right)>>1
        # 要寻找的是大等于target的第一个位置,当mid处值大等于target时,说明结果在左边(也包括mid)
        if nums[mid] >= k: right = mid
        else: left = mid + 1
    start = left # 记录起始位置 left right都行 跳出时left=right
    if nums[start] != k: return -1,-1 # 不等于k说明数组内没有元素k
    # 找到小等于target的最后一个数
    left, right = 0, len(nums)-1
    while left < right:
        mid = (left+right+1)>>1  # 加1是为了跳出死循环 当right=left+1时 mid结果和left相等 一直left=mid死循环
        # 要寻找的是小等于target的最后一个位置,倘若mid处值小等于target,说明结果肯定在右边(也包含mid)
        if nums[mid] <= k: left = mid
        else: right = mid - 1
    end = left
    return start,end
nums = [1,2,2,3,3,4]
print(find_range(nums,3))
print(find_range(nums,5))

x的平方根

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
也就是找到平方值小于x的最大整数。

class Solution(object):
    def mySqrt(self, x):
        l, r = 0, x
        # 寻找平方小等于x的最大整数
        while l<r:
            mid = (l+r+1)>>1
            if mid*mid <= x: l = mid
            else: r = mid-1
        return l

小数二分

浮点数平方根二分法求解

class Solution:
    def mysqrt(self,x):
        l, r = 0, x
        while r-l>1e-6:
            mid = (l+r)/2
            if mid*mid <= x: l = mid
            else: r = mid
        return l
sol = Solution()
sol.mysqrt(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值