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)