一篇文章看懂所有排序方法(面试必备)

注意:本篇文章只是讲所有的排序逻辑和代码实现,完全掌握还需要自己手动敲出一遍甚至多遍代码,代码中的注意的注释是比较容易疏忽的地方,本篇文章没有图片进行更直观的展示排序原理,但是做到心中有图,代码独立编写也就没有了问题

基本算法

插入排序
插入排序就是遍历两次数组,一次是前面的排序好的,一个是后面未排序的,将未排序的跟排序好的进行比较,如果比它大就放在它的位置就是从大到小排序,比他小放在它的位置就是从小到大排序,所以当需要遍历完所有的已排序数组的时候是最坏的情况,时间复杂度最坏的情况为o(n^2),最好的情况为o(n),即永远只需要遍历第一个就可以进行插入操作了

# 创建一个随机的数组作为输入
import random
test_list = [random.randomint(0,100) for i in range(10)]
# 插入排序
def insert(test_list):
	# 遍历未排序的数组,因为在0坐标的位置的数组只有一个也就是已经排序完成,所以在1坐标开始
    for i in range(1,len(test_list)):
    	# 遍历未排序的数组
        for j in range(0,i):
        	# 进行插入操作,注意相等的情况
            if test_list[i]<=test_list[j]:
                test_list.insert(j,test_list.pop(i))
        print('第{}次'.format(i),test_list)
test_list = [random.randint(1,100) for i in range(10)]
print(test_list)
insert(test_list)

冒泡排序
冒泡排序的原理是交换,如果存在交换,证明此时还需要排序,如果不存在交换,则循环结束,得到一个冒泡,交换就是从第一个往后进行判断,看是否满足前大后小或者前小后大的顺序逻辑,所以就是从头到尾进行判断,如果0小于1进行0和1交换,1和2比较,如果1小于2进行交换,如此往下,最后一个一定是所有数中最大的数

def bubble_sort(test_list):
	# 循环i次
    for i in range(len(test_list)-1,0,-1):
        flog = False
        # 做一次冒泡排序(进行从头到尾的依次比较)
        # 注意这里j+1不会越界
        for j in range(i):
            if test_list[j]>test_list[j+1]:
                test_list[j],test_list[j+1] = test_list[j+1],test_list[j]
                flog = True
        print('第{}次'.format(10-i),test_list)
        if not flog:
            break
test_list = [random.randint(1,100) for i in range(10)]
print(test_list)
bubble_sort(test_list)

选择排序
搞懂了冒泡排序,选择排序的逻辑就比较简单了,那就是一共有多少个就循环多少次,每次挑一个最大的或者最小的出来,这样的最终序列一定是有序的

# 选择排序
def choose_sort(test_list):
    for i in range(len(test_list)-1,0,-1):
        max = i
        for j in range(0,i):
            if test_list[j] > test_list[max]:
                max = j
        test_list[max],test_list[i] = test_list[i],test_list[max]
        print('第{}次'.format(10-i),test_list)
test_list = [random.randint(1,100) for i in range(10)]
print(test_list)
choose_sort(test_list)

高级排序

堆排序
首先讲堆排序,因为它的逻辑和选择差不多,也是首先找个最大的或者最小的
那么如何找这个最大的,就需要堆这个数据结构,
这里把一个数组的0坐标位置插入一个0,使得数组的坐标和堆的坐标相互对应,即如图所示:
在这里插入图片描述

堆其实就是完全二叉树的另一个名字,只不过有大根堆小根堆的区别,大根堆就是任意节点的值大约两个子节点,所以只要把数组分成了一个大根堆,再吧大根堆的根节点“选择”出来,把堆节点和最后一个节点进行交换,然后除开这个节点,对剩下的进行造堆操作,如此循环就能得到有序的排列
那么第一步,如何造堆(把一个杂乱的数组变成一个满足大根堆的排序)
首先考虑传入参数,传入堆的根节点的坐标和未节点,造堆的逻辑就是把每个父节点跟他的较大的那一个子节点进行比较,如果父节点小于该子节点,进行交换

def heapify(root_node,end_node,test_list):
    # 判断是否存在后续节点
    father_node = root_node
    son_node = father_node*2
    while son_node<=end_node:
        # 注意这里son_node+1<end_node和test_list[son_node+1]>test_list[son_node]的顺序不能调换,若调换则报错(在只有左节点没有右节点的情况)
        if son_node+1<=end_node and test_list[son_node+1]>test_list[son_node]:
        	# 找到较大的子节点,这里需要特别注意是son_node+1<=end_node否则会在一些特殊情况排序失败
            son_node +=1
        # 进行比较
        if test_list[father_node]<test_list[son_node]:
            test_list[father_node],test_list[son_node] = test_list[son_node],test_list[father_node]
            father_node = son_node
            son_node = father_node*2
        else:
            break
test_list = [random.randint(1,100) for i in range(10)]
test_list.insert(0,0)
print(test_list)
# 对中间节点(即最后一个有左右节点的节点进行造堆操作)和他前面的每个节点
for i in range(len(test_list)//2,0,-1):
    heapify(i,len(test_list)-1,test_list)
    print(test_list)
# 这里形成的就是满足大根堆的排序规则了

造堆完成后就是相当于选择排序一样的操作了

def heap_sort(test_list):
    for i in range(len(test_list)-1,0,-1):
        test_list[1],test_list[i] = test_list[i],test_list[1]
        这里注意每次i-1,除开已经确定好位置的节点
        heapify(1,i-1,test_list)

快速排序
快速排序的要点是随便的找一个数,一般我们会找数组的第一个数作为基准值,第一次快速排序达到左边的数字都比该基准值小,右边的数字都比基准值大的效果,这样之后将数组以该基准值为界分开,分别进行递归快速排序就能得到一个有序的数组

import random
# 方便理解的非原地版:
def quick_sort(test_list):
    if len(test_list)<=1:
        return test_list
    key = test_list[0]
    left,right = [],[]
    mid = []
    for i in range(0,len(test_list)):
        if test_list[i]<key:
            left.append(test_list[i])
        elif test_list[i]>key:
            right.append(test_list[i])
        else:
        	# 注意和基准值相等的情况
            mid.append(test_list[i])
    quick_sort(left)
    quick_sort(right)
    print(left+mid+right)
    return left+mid+right
test_list = [random.randint(0,100) for i in range(10)]
print(test_list)
quick_sort(test_list)

优化肯定是取消它额外的空间,这里我们通过左右指针的方式进行优化,具体的实现是通过左右指针同时对数组进行遍历,如果发现左指针的位置比基准值大,将左指针的值赋给

# optimize quick
import random
def optimize_quick_sort(left,right,test_list):
    if left>=right:
        return
    key = test_list[left]
    l,r = left,right
    # 注意这里l,r对坐标进行接受,方便之后的快速排序
    while l<r:
    	# 
        while l<r and test_list[r]>=key:
            r -= 1
        test_list[l]=test_list[r]
        # print(test_list)
        while l<r and test_list[l]<=key:
            l += 1
        test_list[r]=test_list[l]
    test_list[l]=key
    print(test_list)
    # 注意这里的左右指标已经相等,去除基准值对左右两数组进行快速排序
    optimize_quick_sort(left,l-1,test_list)
    optimize_quick_sort(l+1,right,test_list)

test_list = [random.randint(0,100) for i in range(10)]
print('begin:',test_list)
optimize_quick_sort(0,len(test_list)-1,test_list)

归并排序
归并的含义,只要理解了怎么拆开,怎么合并就掌握了归并排序
首先分析拆开,这里还是用递归,如果是5个数,分成2,3;再分成1,1和1,2;再对1,2进行拆成1,1;如果是4个数就是2:2;然后1:1和1:1
所以递归结束的条件就是数组的长度为1的时候结束

import random
def merge_sort(test_list):
    if len(test_list)<=1:
        return test_list
    print(test_list)
    mid = len(test_list)//2
    left = merge_sort(test_list[:mid])
    right = merge_sort(test_list[mid:])
    i,j = 0,0
    result = []

怎么和,我们还是假设随机的5个数:
7 ,4 , 1,9,2
第一次拆变成:7,4 和
1,9,2
7,4拆成了[7]和[4]
用result作为返回值,那他应该返回一个[4,7]
这里采取的办法是用i,j作为[7]和[4]的指针,遍历那个小,放到result中,被放之后+=1
所以获取到的结果为result = [4],i=0,j=1,想要返回[4,7],还需要加个[7],考虑到也有可能是被拆成[4],[7]的情况
最终的代码为

# merge sort
import random
def merge_sort(test_list):
    if len(test_list)<=1:
        return test_list
    print(test_list)
    mid = len(test_list)//2
    left = merge_sort(test_list[:mid])
    right = merge_sort(test_list[mid:])
    i,j = 0,0
    result = []
    while i<len(left) and j<len(right):
        if left[i]<right[j]:
            result.append(left[i])
            i+=1
        else:
            result.append(right[j])
            j+=1
    print('res:',result+left[i:]+right[j:])
    return result+left[i:]+right[j:]
test_list = [random.randint(0,100) for i in range(10)]
merge_sort(test_list)

此外的希尔排序和桶排序理解起来比较简单,这里不再介绍

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值