【Leetcode】八大排序

总述

插入排序:直接插入排序;希尔排序;

选择排序:简单选择排序;堆排序;

交换排序:冒泡排序;快速排序;

归并排序;

桶排序/基数排序;

 直接插入排序

选取未排序区域的第一个元素,从后至前逐个与已排序区域的元素两两比较,若arr[i-1]>arr[i](前者大),交换arr[i-1]和arr[i],i-=1;需要保证已排序区域再加入这个元素后仍然有序——打扑克;

时间复杂度:最优O(n)--满足排序要求  最坏O(n^2)--与排序要求相反

空间复杂度:O(1)--原地实现

稳定性:稳定

def insertion_sort(arr):
    
    length = len(arr)
    if length <= 1:return

    for i in range(length):
        cur_index = i
        while cur_index-1 >= 0 and arr[cur_index-1] > arr[cur_index]:
            arr[cur_index], arr[cur_index-1] = arr[cur_index-1], arr[cur_index]
            cur_index -= 1

    return arr
希尔排序-缩小增量排序

通过比较一定间隔的元素进行插入排序,同时不断缩小间隔(增量),直到比较相邻元素。当增量为0时,算法终止。

时间复杂度:依赖于gap的选择,平均复杂度O(n^1.5)

空间复杂度:O(1)--原地实现

稳定性:不稳定

def shell_sort(arr):
    n = len(arr) 
	gap = n // 2
	while gap>0:
		for i in range(gap, n):
			while i-gap >= 0 and arr[i-gap]>arr[i]:
				arr[i], arr[i-gap] = arr[i-gap], arr[i]
				i -= gap
		gap = gap // 2

	return arr
选择排序

搜索未排序区域的最小值(最大值),将其交换到已排序区域的末尾,然后扩大已排序范围,直至得到升序(降序)排列;

时间复杂度:O(n^2)

空间复杂度:O(1)--原地实现

稳定性:不稳定

def select_sort(arr):

    length = len(arr)
    if length <= 1:return

    for i in range(length):
        min_index = i

        for j in range(i+1, length):
            if arr[min_index] > arr[j]:
                min_index = j

        if min_index != i:
            arr[min_index], arr[i] = arr[i], arr[min_index]
 堆排序

 通过数据上浮构造初始大根堆,然后将最大值与最后一个数交换,通过数据下沉恢复除最后一个节点的大根堆,实现倒序构造升序数组;

时间复杂度:O(nlogn)

空间复杂度:O(1)--原地实现

稳定性:不稳定

def heapify(arr, index, end):
    while True:
        left, right = 2*idx+1, 2*idx+2
        if right < end: cur = left if nums[left]>nums[right] else right
        elif left < end: cur = left
        else: break
 
        if nums[cur]>nums[idx]:
            nums[cur], nums[idx] = nums[idx], nums[cur]
            idx = cur
        else:break
    return


def heap_sort(arr):
    n = len(arr)
    for i in range(n//2, -1, -1):
        heapify(arr, i, n)
    for i in range(n-1, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]
        heapify(arr, 0, i)   # 调整边界
    return arr
冒泡排序

对未排序区域的元素两两比较,将较大值(较小值)向后交换,然后缩小未排序范围,直至未排序范围清空时得到升序(降序)排列;

时间复杂度:最优O(n)--满足排序要求  最坏O(n^2)--与排序要求相反

空间复杂度:O(1)--原地实现

稳定性:稳定

def bubble_sort(arr):

    length = len(arr)
    if length <= 1:return

    for i in range(length):
        is_made_swap = False               # 设置标志位

        for j in range(length - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                is_made_swap = True

        if not is_made_swap: break         # 无交换代表已经有序
快速排序

step1.从数列中挑出一个元素,称为"基准"(pivot),
step2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
step3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

时间复杂度:最优O(nlogn)--每次接近二分  最坏O(n^2)--每次只分出一个

空间复杂度:O(logn)

稳定性:不稳定

def partition(nums, left, right):
    pivot = nums[left]                 #初始化一个待比较数据
    i,j = left, right
    while i<j:
        while(i<j and nums[j]>=pivot): #从后往前查找,直到找到一个比pivot更小的数
            j-=1
        nums[i] = nums[j]              #将更小的数放入左边
        while(i<j and nums[i]<=pivot): #从前往后找,直到找到一个比pivot更大的数
            i+=1
        nums[j] = nums[i]              #将更大的数放入右边
    
    nums[i] = pivot                    #待比较数据放入最终位置 
    return i                           #返回待比较数据最终位置

def quicksort(nums, left, right):
    if left < right:
        index = partition(nums, left, right)
        quicksort(nums, left, index-1)
        quicksort(nums, index+1, right)
归并排序

把长度为n的输入序列分成两个长度为n/2的子序列,在子序列中也采用归并排序;当子序列中排序完成后,将其合并;

时间复杂度:最优O(n)   最坏O(nlogn)

空间复杂度:O(n)

稳定性:不稳定

def merge_sort(arr):
	if len(arr) <= 1:
		return arr
   
	mid = len(arr) // 2    
	left = merge_sort(arr[:mid])
	right = merge_sort(arr[mid:])
	return merge(left, right)   
    
def merge(left, right):
	new = []
	i = j = k = 0
	while(i < len(left) and j < len(right)):
		if left[i] < right[j]:
			new.append(left[i])
			i += 1
		else:
			new.append(right[j])
			j += 1
	new = new + left[i:] + right[j:]
	return new

 桶排序/基数排序

将数据按位进行分桶,按桶代表的数值进行排序,然后切换到下一个位;

def radix_sort(arr):
    max_num = max(arr)
    place = 1
    while max_num >= 10**place:  # 统计最大值的位数
        place += 1
    for i in range(place):
        buckets = [[] for _ in range(10)]
        for num in array:
            radix = int(num/(10**i) % 10)
            buckets[radix].append(num)
        j = 0
        for k in range(10):
            for num in buckets[k]:
                arr[j] = num
                j += 1
    return arr

备注:

冒泡排序和选择排序大体思路一致,区别在于冒泡是通过交换都得到未排序区域的最值,而选择只记录了对应下标;冒泡的有序区域在列表末尾,选择的有序区域在列表开头(这个也可换);冒泡比较出相对关系,可在满足要求时提前结束排序,而选择无法优化;

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值