两个点:
1、快排的时候,partition最后操作的是前面的sign, res1
2、归并的时候,如果只是if left < right,肯定是不行的,因为mid = (elft+right)//2,这样的话会导致肯定无法结束,当只有两个数的时候,所以必须if left < right-1。 如果只是用left < right的话,要注意两个的情况,当然也可以解决~~保证两个的时候,可以分成1和1的形式。
https://leetcode-cn.com/problems/sort-an-array/submissions/
面试无非:快排、归并、堆、冒泡、插入,以及复杂度、稳定性
空间复杂度常规计算:如果新增的内存空间例如列表M在递归中,那么空间就是O(N*M);如果在外部,那么就是O(N+M)。
快排 == 不稳定
相当于先序遍历 时间复杂度O(nlogn),最差O(n*n), 空间复杂度O(logn)===不稳定
空间复杂度的计算取决于递归深度,如果是排序好的数组,递归深度是N,那么空间复杂度就是(N),如果是其他数组,那么递归深度就是log(N)。
def partition(nums, left, right):
tar = nums[right]
res1 = left
res2 = left - 1
for i in range(left, right):
if nums[i] <= tar:
res2 += 1
nums[res2], nums[res1] = nums[res1], nums[res2]
res1 += 1
res2 += 1
nums[res2], nums[right] = nums[right], nums[res2]
return res2
def quick_sort(nums, left, right):
if left < right:
num = partition(nums, left, right)
quick_sort(nums, left, num-1)
quick_sort(nums, num+1, right)
return nums
nums = [100,2,2,4,6,7,4,2,5,7,8]
# print(quick_sort(nums, 0, len(nums)-1))
归并 == 稳定
等于后续遍历;时间复杂度O(nlogn), 空间O(N), 因为merge阶段使用了空间~~跟上面区别好。
注意点是核心思想是分治法,先分后合;所以并不是类似上面的排除掉一个,而是分成两部分,直到分的只剩一个。注意right~
# 归并排序
def merge(nums, left, mid, right):
res1 = nums[left:mid]
res2 = nums[mid:right]
num1 = 0
num2 = 0
tmp = left
while num1 < len(res1) and num2 < len(res2):
if res1[num1] <= res2[num2]:
nums[tmp] = res1[num1]
num1 += 1
else:
nums[tmp] = res2[num2]
num2 += 1
tmp += 1
if num1 == len(res1):
for i in range(num2, len(res2)):
nums[tmp] = res2[i]
tmp += 1
elif num2 == len(res2):
for i in range(num1, len(res1)):
nums[tmp] = res1[i]
tmp += 1
def merge_sort(nums, left, right):
if left < right - 1: # 关键 取还是不取
mid = (left + right)//2
merge_sort(nums, left, mid)
merge_sort(nums, mid, right)
merge(nums, left, mid, right)
return nums
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def merge(nums, left, mid, right):
res1 = nums[left:mid+1]
res2 = nums[mid+1:right+1]
num1 = 0
num2 = 0
res = left
while num1 < len(res1) and num2 < len(res2):
if res1[num1] < res2[num2]:
nums[res] = res1[num1]
num1 += 1
else:
nums[res] = res2[num2]
num2 += 1
res += 1
if num1 == len(res1):
for i in range(num2, len(res2)):
nums[res] = res2[i]
res += 1
else:
for i in range(num1, len(res1)):
nums[res] = res1[i]
res += 1
def merge_sort(nums, left, right):
if left < right: ############# right-1 不然的话,用于都有 left=len-1, right=len
mid = (left + right) // 2
merge_sort(nums, left, mid)
merge_sort(nums, mid+1, right)
merge(nums, left, mid, right)
merge_sort(nums, 0, len(nums)-1)
return nums
堆排序 == 不稳定
时间复杂度O(nlogn),空间O(1)。
写的时候犯了三个错误:
1、heapify出错,判断条件要分开,因为不一定时满二叉树
2、注意好是求 最大堆还是最小堆 == 如果是从小到大排序的话,那么求得就是最大堆了~
3、建堆的时候要从左下角,也就是len(nums)//2开始往回~~ 其实len(nums)//2就是左下角了。
# 堆排序
# 建堆
# heapify
# 插入
# heap_sort
def heapify(nums, i, length):
left = 2 * i + 1
right = 2 * i + 2
# if left < length and right < length: # 错误点三:要分别进行的
# res = min(nums[i], nums[left]) # 错误点2,找的是最大的
# res = min(res, nums[right])
# if res == nums[right]:
# nums[right], nums[i] = nums[i], nums[right]
# heapify(nums, right, length)
# elif res == nums[left]:
# nums[left], nums[i] = nums[i], nums[left]
# heapify(nums, left, length)
res = i
if left < length and nums[i] > nums[left]:
res = left
if right < length and nums[res] > nums[right]:
res = right
if res != i:
nums[res], nums[i] = nums[i], nums[res]
heapify(nums, res, length)
def build_heap(nums):
for i in range(len(nums)//2, -1, -1): # 错误1 应该从左下角开始~~不然的话,首位其实不一定是最小的。 nums = [8,5,4,1,3,2,6]
heapify(nums, i, len(nums))
return nums
def heap_sort(nums):
length = len(nums)
build_heap(nums)
for i in range(len(nums) - 1, 0, -1):
nums[0], nums[i] = nums[i], nums[0]
length -= 1
heapify(nums, 0, length)
return nums
print(heap_sort(nums))
冒泡 == 稳定
时间复杂度O(n*n), 空间O(1), 每次搞定最后一位,也就是最大的。
nums = [100,2,2,4,6,7,4,2,5,7,8]
# 冒泡排序
def bubble(nums):
num = 0
for i in range(len(nums)):
res = 0
for j in range(1, len(nums)-num):
if nums[j] < nums[res]:
nums[j], nums[res] = nums[res], nums[j]
res = j # 每次都是必须交换序号的
num += 1
return nums
print(bubble(nums))
插入排序 == 稳定
时间复杂度O(n*n), 空间O(1), 从左边开始,然后其实前面的部分就都是排好的,只需要插入。
nums = [100,2,2,4,6,7,4,2,5,7,8]
# 插入排序
def insert_sort(nums):
# 认为前面的都是排好的
for i in range(len(nums)):
res = i
for j in range(i-1, -1, -1): # 因为是从左边开始的,确实左边都是排好的。所以如果遇到比自己小的,就可以break。
if nums[res] < nums[j]:
nums[res], nums[j] = nums[j], nums[res]
res = j
else:
break
return nums
print(insert_sort(nums))
桶排序
数要相对均匀分布,桶的个数也要合理设计。总之桶排序是一种用空间换取时间的排序。在设计桶排序,你需要知道输入数据的上界和下界,看看数据的分布情况,再考虑是否用桶排序
时间复杂度分析
桶排序的时间复杂度到底是多少?
我们假设有n个待排序数字。分到m个桶中,如果分配均匀这样平均每个桶有n/m个元素。首先在这里我郑重说明一下桶排序的算法时间复杂度有两部分组成:
1.遍历处理每个元素,O(n)级别的普通遍历
2.每个桶内再次排序的时间复杂度总和
在这里如果到达极限情况n=m时。就能确保避免桶内排序,将数值放到桶中不需要再排序达到O(n)的排序效果,当然这种情况属于计数排序
import random
## 快排
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def partition(nums, left, right):
## 必须加优化
res = random.randint(left, right)
nums[res], nums[right] = nums[right], nums[res]
res1 = left - 1
res2 = left - 1
tmp = nums[right]
for i in range(left, right):
if nums[i] <= tmp:
res1 += 1
res2 += 1
nums[res1], nums[res2] = nums[res2], nums[res1]
else:
res2 += 1
res1 += 1 ## 这里是res1,其实就是原理遗漏了~~
nums[res1], nums[right] = nums[right], nums[res1]
return res1
def quick_sort(nums, left, right):
if left < right:
res = partition(nums, left, right)
quick_sort(nums, left, res-1)
quick_sort(nums, res+1, right)
quick_sort(nums, 0, len(nums)-1)
return nums
## 归并 时间复杂度:O(NlogN),这里 NN 是数组的长度;
# 空间复杂度:O(N)O(N),辅助数组与输入数组规模相当。
import random
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def merge(nums, left, mid, right):
res1 = nums[left:mid+1]
res2 = nums[mid+1:right+1]
num1 = 0
num2 = 0
res = left
while num1 < len(res1) and num2 < len(res2):
if res1[num1] < res2[num2]:
nums[res] = res1[num1]
num1 += 1
else:
nums[res] = res2[num2]
num2 += 1
res += 1
if num1 == len(res1):
for i in range(num2, len(res2)):
nums[res] = res2[i]
res += 1
else:
for i in range(num1, len(res1)):
nums[res] = res1[i]
res += 1
def merge_sort(nums, left, right):
if left < right: ############# right-1 不然的话,用于都有 left=len-1, right=len
mid = (left + right) // 2
merge_sort(nums, left, mid)
merge_sort(nums, mid+1, right)
merge(nums, left, mid, right)
merge_sort(nums, 0, len(nums)-1)
return nums
## 冒泡
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def bubble_sort(nums, left, right):
for i in range(left, right):
for j in range(left, right-i-1):
if nums[j] > nums[j+1]:
nums[j], nums[j+1] = nums[j+1], nums[j]
return nums
return bubble_sort(nums, 0, len(nums))
## 堆排
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def heapify(nums, i, size):
left = 2*i + 1
right = 2*i + 2
res = i
if left < size and nums[res] < nums[left]:
res, left = left, res
if right < size and nums[res] < nums[right]:
res, right = right, res
if res != i:
nums[res], nums[i] = nums[i], nums[res]
heapify(nums, res, size)
def stack_build(nums):
for i in range(len(nums)//2, -1, -1):
heapify(nums, i, len(nums))
def stack_sort(nums):
stack_build(nums)
size = len(nums)
for i in range(len(nums)):
heapify(nums, 0, size)
nums[0], nums[size-1] = nums[size-1], nums[0]
size -= 1
return nums
return stack_sort(nums)
## 插入排序
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
for i in range(1, len(nums)):
res = i
for j in range(i-1, -1, -1):
if nums[res] < nums[j]:
nums[j], nums[res] = nums[res], nums[j]
res = j
else:
break
return nums
#思路:每一轮选取未排定的部分中最小的部分交换到未排定部分的最开头,经过若干个步骤,就能排定整个数组。即:先选出最小的,再选出第 2 小的,以此类推。
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
## 选择排序
for i in range(len(nums)):
res = i
for j in range(i, len(nums)):
if nums[res] > nums[j]:
res = j
nums[res], nums[i] = nums[i], nums[res]
return nums