参考:https://www.cnblogs.com/onepixel/articles/7674659.html
排序的稳定性:如果排序数组具有相同值的元素,并且排序前排序后相对位置不变,那么我们称这种排序为稳定排序。稳定排序的作用一般在对类进行排序的时候,一个类有一些其他属性,对于这些属性存在顺序要求的话就需要用稳定排序。
两个指针i,j,i代表i之前都是有序的,arr[j]与arr[j-1]进行对比,假如arr[j] 时间复杂度:O(n^2) 两个指针i,j,i之前都是排序完成的,选择i之后最小的那个数与i进行交换,以此类推。 时间复杂度:O(n^2) 两个指针i,j,i之前都是排序完成的,选择i插入到之前其对应位置,以此类推。 时间复杂度:O(n^2) 其本质采用分治策略,步骤为先分后合。时间复杂度O(NlogN),空间复杂度O(N) 时间复杂度:O(nlogn) 逆序对-如果存在l 归并解决: reverse_pair_res是逆序对 题目:在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。 归并解决: 问题描述:给定一个数组arr和一个数num,让大于这数的数字放在数组的右边,小于等于这个数字放在数组的左边。 要求:时间复杂度O(n),额外空间复杂度O(1) 当j==len(arr)时停止 例子: 代码: 问题描述:给定一个数组arr和一个数num,让大于这数的数字放在数组的右边,等于这个数字放在数组的中间,小于这个数字放在数组的左边。 要求:时间复杂度O(n),额外空间复杂度O(1) 解决思路: 当j>k的时候停止 例子: 代码: 第一版本快速排序首先选择数组的最后一个元素做num,进行partition,再分别对左右两边进行partition,依次递归,代码如下: 第一版本快速排序存在一个问题就是假如有重复元素,就需要做多次判断,为了解决这个问题,可以使用荷兰国旗问题进行partition,代码如下: 时间复杂度:O(nlogn) 堆实际上是一个完全二叉树在数组层面的映射关系 对于大根堆,该完全二叉树具有父节点永远大于子节点的特性。 对于小根堆,该完全二叉树具有父节点永远大于子节点的特性。 完全二叉树:设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边。 节点间的下标关系 这里以小根堆为例 例子: 这里以小根堆为例 例子: 对于python而言,已经存在现成的堆的数据结构了,叫优先队列。 因为底层默认调用<比较的所以如果想要做大根堆,就需要重写__lt__方法。 有没有发现,最后打印出来的是有顺序的数组。这就是堆排序 时间复杂度:O(nlogn) 先确定排序中值的范围,比如值范围为0-10,那么开辟一个长度为10的数组a,遍历原来数组,统计各个值出现的次数,比如3出现了3次就在数组a索引为3的值记为3,然后遍历数组a进行排序。 基数排序(radix sorting)将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。 然后 从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。def bubble_sort(arr):
for i in range(len(arr)):
for j in range(len(arr) - 1, i - 1, -1):
if arr[j] < arr[j - 1]:
arr[j], arr[j - 1] = arr[j - 1], arr[j]
空间复杂度:O(1)
稳定性:稳定选择排序
def select_sort(arr):
for i in range(len(arr)):
min_value = arr[i]
min_index = i
for j in range(i, len(arr)):
if min_value > arr[j]:
min_value = arr[j]
min_index = j
arr[i], arr[min_index] = arr[min_index], arr[i]
空间复杂度:O(1)
稳定性:稳定插入排序
def insert_sort(arr):
for i in range(1, len(arr)):
j = i
while j > 0:
if arr[j] < arr[j - 1]:
arr[j], arr[j - 1] = arr[j - 1], arr[j]
j -= 1
else:
break
空间复杂度:O(1)
稳定性:稳定归并排序
归并排序
例子:
要将[5,3,4,2,1]排序
代码:def merge_sort(arr):
divide(arr, 0, len(arr) - 1)
return arr
def divide(arr, left, right):
if left >= right:
return
mid = int((left + right) / 2)
divide(arr, left, mid)
divide(arr, mid + 1, right)
merge(arr, left, mid, right)
def merge(arr, left, mid, right):
help_arr = []
i = left
j = mid + 1
while i <= mid and j <= right:
if arr[i] <= arr[j]:
help_arr.append(arr[i])
i += 1
else:
help_arr.append(arr[j])
j += 1
while i <= mid:
help_arr.append(arr[i])
i += 1
while j <= right:
help_arr.append(arr[j])
j += 1
for k in range(left, right + 1):
arr[k] = help_arr[k - left]
空间复杂度:O(n)
稳定性:稳定逆序对问题
该问题最好的解决方法就是使用归并排序,左边逆序个数+右边逆序个数+merge逆序个数。
暴力解决:def reverse_pair_force(arr):
cnt = 0
for i in range(len(arr)):
for j in range(i, len(arr)):
if arr[j] < arr[i]:
cnt += 1
return cnt
def reverse_pair(arr):
reverse_pair_res = []
cnt = divide(arr, 0, len(arr) - 1, reverse_pair_res)
return cnt, reverse_pair_res
def divide(arr, left, right, res):
if left >= right:
return 0
mid = int((left + right) / 2)
cnt_left = divide(arr, left, mid, res)
cnt_right = divide(arr, mid + 1, right, res)
cnt_merge = merge(arr, left, mid, right, res)
return cnt_left + cnt_right + cnt_merge
def merge(arr, left, mid, right, res):
help_arr = []
i = left
j = mid + 1
cnt = 0
while i <= mid and j <= right:
if arr[i] <= arr[j]:
help_arr.append(arr[i])
i += 1
else:
cnt += mid - i + 1
for k in range(i, mid + 1):
res.append((arr[j], arr[k]))
help_arr.append(arr[j])
j += 1
while i <= mid:
help_arr.append(arr[i])
i += 1
while j <= right:
help_arr.append(arr[j])
j += 1
for k in range(left, right + 1):
arr[k] = help_arr[k - left]
return cnt
cnt是逆序对的个数求数组的小和
例子:[1,3,4,2,5]
其实就是求逆序对的另一个版本,也就是归并排序降序版本的逆序对值的总和
暴力解法:def min_sum_force(arr):
cnt = 0
for i in range(len(arr)):
for j in range(i, len(arr)):
if arr[j] > arr[i]:
cnt += arr[i]
return cnt
def min_sum(arr):
cnt = divide(arr, 0, len(arr) - 1)
return cnt
def divide(arr, left, right):
if left >= right:
return 0
mid = int((left + right) / 2)
cnt_left = divide(arr, left, mid)
cnt_right = divide(arr, mid + 1, right)
cnt_merge = merge(arr, left, mid, right)
return cnt_left + cnt_right + cnt_merge
def merge(arr, left, mid, right):
help_arr = []
i = left
j = mid + 1
cnt = 0
while i <= mid and j <= right:
if arr[i] > arr[j]:
help_arr.append(arr[i])
i += 1
else:
for k in range(i, mid + 1):
cnt += arr[k]
help_arr.append(arr[j])
j += 1
while i <= mid:
help_arr.append(arr[i])
i += 1
while j <= right:
help_arr.append(arr[j])
j += 1
for k in range(left, right + 1):
arr[k] = help_arr[k - left]
return cnt
快速排序
partition问题
解决思路:
快慢指针法,定义2个指针i=0,j=0,i代表i左边的数字小于等于num,j代表遍历到的数字。
arr=[1,7,3,5,7,2] num=5
def partition(arr, num):
i, j = 0, 0
while j < len(arr):
if arr[j] <= num:
arr[i], arr[j] = arr[j], arr[i]
i += 1
j += 1
荷兰国旗问题
快慢指针法,定义3个指针i=0,j=0,k=len(arr)-1,i代表i左边的数字小于num,j代表遍历到的数字,k代表k右边的数字大于num。
arr=[1,7,3,5,7,5] num=5
def partition2(arr, num):
i, j, k = 0, 0, len(arr) - 1
while j <= k:
if arr[j] < num:
arr[j], arr[i] = arr[i], arr[j]
j += 1
i += 1
elif arr[j] > num:
arr[j], arr[k] = arr[k], arr[j]
k -= 1
else:
j += 1
快速排序第一版
def quick_sort1(arr):
def partition(arr, left, right, num):
i, j = left, left
while j <= right:
if arr[j] <= num:
arr[i], arr[j] = arr[j], arr[i]
i += 1
j += 1
return i - 1
def qsort(arr, left, right):
if left >= right:
return
num = arr[right]
mid = partition(arr, left, right, num)
qsort(arr, left, mid - 1)
qsort(arr, mid + 1, right)
if not arr:
return
qsort(arr, 0, len(arr) - 1)
快速排序第二版
def quick_sort2(arr):
def partition2(arr, left, right, num):
i, j, k = left, left, right
while j <= k:
if arr[j] < num:
arr[j], arr[i] = arr[i], arr[j]
j += 1
i += 1
elif arr[j] > num:
arr[j], arr[k] = arr[k], arr[j]
k -= 1
else:
j += 1
return i - 1, k + 1
def qsort(arr, left, right):
if left >= right:
return
num = arr[right]
i, k = partition2(arr, left, right, num)
qsort(arr, left, i)
qsort(arr, k, right)
if not arr:
return
qsort(arr, 0, len(arr) - 1)
空间复杂度:O(1)
稳定性:不稳定堆排序
堆
堆的插入
将新插入的数组放入队列尾部,循环进行up操作,up操作如下:
假如heap中存在数组[3,5,7,9]
此时想插入一个4
pop操作
将堆数组尾部的值copy到堆数组的第一个,堆长度index–,循环进行heapify操作,heapify操作如下:
假如heap中存在数组[3,4,7,9,5],pop弹出3输出,将5放到头部[5,4,7,9]
代码实现
class Heap:
def __init__(self):
self.heap = []
self.index = 0
def is_empty(self):
return self.index == 0
def up(self, up_index):
father_index = int((up_index - 1) / 2)
while father_index >= 0 and self.heap[up_index] < self.heap[father_index]:
self.heap[up_index], self.heap[father_index] = self.heap[father_index], self.heap[up_index]
up_index = father_index
father_index = int((up_index - 1) / 2)
def insert(self, num):
if self.index >= len(self.heap):
self.heap.append(num)
else:
self.heap[self.index] = num
self.up(self.index)
self.index += 1
def pop(self):
if self.index == 0:
return
num = self.heap[0]
self.index -= 1
self.heap[0] = self.heap[self.index]
self.heapify(0)
return num
def heapify(self, heapify_index):
while 1:
son_index_left = 2 * heapify_index + 1
son_index_right = 2 * heapify_index + 2
if son_index_right < self.index:
min_index = son_index_left if self.heap[son_index_left] < self.heap[
son_index_right] else son_index_right
elif son_index_left < self.index:
min_index = son_index_left
else:
break
if self.heap[heapify_index] > self.heap[min_index]:
self.heap[heapify_index], self.heap[min_index] = self.heap[min_index], self.heap[heapify_index]
heapify_index = min_index
else:
break
from queue import PriorityQueue
# 使用heapq实现优先队列
# 定义一个可比较对象
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return str(self.__dict__)
def __lt__(self, other): # operator <
return self.age > other.age
pq = PriorityQueue()
pq.put(Person("red", 11))
pq.put(Person("blue", 9))
pq.put(Person("black", 22))
pq.put(Person("white", 25))
pq.put(Person("yellow", 17))
while pq.qsize() != 0:
person = pq.get()
print(person)
结果:{'name': 'white', 'age': 25}
{'name': 'black', 'age': 22}
{'name': 'yellow', 'age': 17}
{'name': 'red', 'age': 11}
{'name': 'blue', 'age': 9}
空间复杂度:O(n)
稳定性:不稳定计数排序
但这个排序存在缺点就是只能对整数进行排序,而且一旦数组中的值变大,开辟的空间也要很大,严重占用内存。def count_sort(arr):
min_value = min(arr)
max_value = max(arr)
len_vaue = max_value - min_value + 1
bucket = [0 for i in range(len_vaue)]
for i in arr:
bucket[i - min_value] += 1
print(bucket)
for j in range(len(bucket)):
cnt = 0
for k in range(bucket[j]):
print(j+ min_value)
arr[cnt] = j + min_value
cnt += 1
基数排序
from queue import Queue
def radix_sort(arr):
bucket = [Queue() for i in range(10)]
my_arr = [str(i) for i in arr]
max_len = 0
for i in my_arr:
if max_len < len(i):
max_len = len(i)
for i, v in enumerate(my_arr):
my_arr[i] = (max_len - len(v)) * "0" + v
index = max_len - 1
while index >= 0:
for i in my_arr:
bucket[int(i[index])].put(i)
cnt = 0
for j in bucket:
while not j.empty():
my_arr[cnt] = j.get()
cnt += 1
index -= 1
return [int(i) for i in my_arr]