算法与数据结构——六大排序

目录

##排序

##常见的排序算法

##菜鸟三人组

##冒泡排序(Bubble sort)

##图片展示

##代码

 ##运行结果

##时间复杂度O(n)

##选择排序(select sort)

##图片展示

##代码

##运行结果

##时间复杂度O(n)

##插入排序(Insert sort)

##图片展示

##代码

##运行结果

##时间复杂度O(n)

##天赋三人组

##快速排序(Quick sort)

##图片展示

##代码

##运行结果

##时间复杂度O(n)

##堆排序(Heapsort)

堆排序:是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆是一个特殊的完全二叉树

 ##图片展示

##代码

 ##运行结果

##时间复杂度 O(n)

##归并排序

##图片展示 

 ##代码

##时间复杂度O(n)

##小结



##排序

  • 排序:将一堆无序的记录序列调整为一堆有序的记录序列
  • 列表排序:将无序列表变为有序列表                                                                                                 >输入:列表                                                                                                                               >输出:有序列表
  • 内置排序函数:sort()                                                                                                            

##常见的排序算法

  • 菜鸟三人组:
    • 冒泡排序
    • 选择排序
    • 插入排序                                                  
  • 天赋三人组:
    • 快速排序
    • 堆排序
    • 归并排序

##菜鸟三人组

##冒泡排序(Bubble sort)

  • 冒泡排序:列表每俩个相邻的数,如果前面比后面大,则交换这俩个数。
  • 区域:一趟排序完成后,则无序区少一个数,有序区多一个数、
  • 关键点:趟和无序区的范围
##图片展示

默认升序排序!有颜色的是有序区,无颜色的是无序区,每一趟排序后都会多一个有序区,少一个无序区。每一趟排序都是排的无序区。指针指到谁,谁就和自身后面的元素比较,比它大就交换当指针指向8时,8和1进行交换;8在此时已经变为有序区了!指针不会再指向8.

##代码
import random               #导入随机的(random)模块
def bubble_sort(a_list):    #命名一个冒泡排序的函数
    for i in range(len(a_list)-1):
        exchange = False
        for j in range(len(a_list)-i-1):
            if a_list[j] > a_list[j+1]:
                a_list[j],a_list[j+1] = a_list[j+1],a_list[j]
                exchange = True
        if not exchange:
            return

a_list = [random.randint(0,100) for i in range(10)]
print(a_list)
bubble_sort(a_list)
print(a_list)

Python中return的意思的是返回数值的意思,它可以选择性的返回一个值给调用的方法,语法格式“return[表达式]”,如果不带表达式则return返回的是None.这里它可以结束函数!

 ##运行结果

##时间复杂度O(n)
  • O(n**2)
  • 原因:代码中有俩个for循环,并且是嵌套的

##选择排序(select sort)

  • 选择排序:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
  • 优化:从列表里随机选取一个数然后进行比较,这样时间复杂度就不会那么大
##图片展示

##代码
def select_sort(a_lsit):
    b_list = []
    for i in range(len(a_list)):#一个o(n)
        min_val = min(a_list)   #min操作时间复杂度也是o(n)
        b_list.append(min_val)
        a_list.remove(min_val) #此处不方便用pop,pop需要下标,remove操作时间复杂度也是o(n)
    return b_list
a_list = [1,34,54,23,2,5,63,33,2]
print(select_sort(a_list))
#用这个做法的时间复杂度有o(n**3)这么大,不是最优方案
def select_sort(a_lsit):
    for i in range(len(a_list)-1):
        min_index = i
        for j in range(i+1,len(a_list)):
            if a_list[j] < a_list[min_index]:
                min_index = j
        a_list[i],a_list[min_index] = a_list[min_index], a_list[i]
a_list = [1,34,54,23,2,5,63,33,2]
select_sort(a_list)
print(a_list)
##运行结果

##时间复杂度O(n)
  • O(n**2 or n**3)

##插入排序(Insert sort)

  • 插入排序:将待排序的数据分为已排序和未排序两部分。初始时,已排序部分只包含第一个元素,未排序部分包含剩下的元素。从未排序部分取出一个元素,与已排序部分的元素逐个比较,找到合适的位置插入。如果已排序部分的元素大于当前元素,就将已排序部分的元素向后移动一位,为当前元素腾出插入位置。将当前元素插入到找到的位置,重复上述步骤,直到未排序部分为空。排序完成后,数组中的元素按照从小到大的顺序排列。
  • 想法:把插入排序当成打扑克牌。初始时,手里只有一张牌(有序区),每次从(无序区)牌堆里摸一张牌出来,插入到手里的正确位置。
##图片展示

随便定一个数为有序区,所以只需要摸n-1张牌来插入到这个有序区中,插8时,把有序区的9往后挪,将8插入!

##代码
def insert_sort(a_list):
    for i in range(1,len(a_list)):
        tmp = a_list[i]
        j = i-1
        while j >=0 and a_list[j] > tmp:
            a_list[j+1] = a_list[j]
            j -=1
        a_list[j+1] = tmp
a_list = [3,34,12,1,4,5,2]
insert_sort(a_list)
print(a_list)
##运行结果

##时间复杂度O(n)
  • O(n**2)

##天赋三人组

##快速排序(Quick sort)

  • 快速排序:从待排序序列中选一个元素作为基准(pivot)。重新排序数组,将小于基准值元素放在基准值左边,将大于基准值元素放在基准值右边。对左、右两边分别进行快速排序。
  • 运作状况:递归完成排序(有点类似于二分查找)
  • 最坏情况:列表是降序排列的,然后要很麻烦的重新排一次;解决办法:随机找一个值,然后与第一个进行交换。也会可能发生最坏情况,但是概率非常小。一般情况都是nlogn。
##图片展示

左边有空位就从右边的指针从右往左开始找比5小的数!反之亦然。当left指针与right指针重合了,就将5放回来!

##代码
def partition(a_list,left,right):
    tmp = a_list[left]
    while left < right:
        while left < right and a_list[right] >= tmp:#从右边找比tmp小的数
            right -= 1#右边的指针往左边走
        a_list[left] = a_list[right]#把右边的值写在左边的空位上
        while left < right and a_list[left] <= tmp:
            left += 1#左边的指针往右边走
        a_list[right] = a_list[left]#把左边的值写在右边的空位上
    a_list[left] = tmp#把tmp归位
    return left
def quick_sort(a_list,left,right):
    if left < right:#至少俩个元素
        mid = partition(a_list,left,right)
        quick_sort(a_list,left,mid-1)
        quick_sort(a_list,mid+1,right)
a_list = [3,4,1,2,5,7,8,9,6]
quick_sort(a_list,0,len(a_list)-1)
print(a_list)
##运行结果

##时间复杂度O(n)
  • O(nlogn)
  • 理解:Left与right遇上了结束,意思是经历了n列表的整个长度,就是partition,所以是o(n),在每一层都是一次partition,o(n)的复杂度,有多少层呢?,每次往下减一半,logn例如:log16!所以,时间复杂度是o(nlogn),logn是指以2为底的对数!

##堆排序(Heapsort)

  • 堆排序:是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆是一个特殊的完全二叉树
  • 完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层的最左边的若干位置的二叉树。
  • 堆排序的过程:
    1. 首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
    2. 将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
    3. 将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组
 ##图片展示

##代码
def sift(a_list,low,high): #写一个调整的函数;low:根节点的位置,hight:堆的最后一个元素的位置
    i = low #i是表示父节点,最开始指的根节点
    j = 2*i+1#j是孩子节点,最开始是左孩子
    tmp = a_list[i]
    while j <= high:#只要j小于堆的最后一个节点
        if j+1 <= high and a_list[j+1] > a_list[j]:#如果右孩子有并且比左孩子大
            j += 1 #j指向右孩子
        if a_list[j] > tmp:
            a_list[i] = a_list[j]
            i = j       #往下看一层
            j = 2 *i +1
        else:#tmp更大,把tmp放在i那里
            a_list[i] = tmp #把tmp放到某一领导位置上
            break
    else:
        a_list[i] = tmp#把tmp放到叶子节点上
def heap_sort(a_list):
    n = len(a_list)
    for i in range((n-2)//2,-1,-1):#从最下面的一层逐渐往上调整
        #i表示建堆的时候调整的部分的根的下标
        sift(a_list,i,n-1)#不管是分成的不同根堆还是整个根堆把hight都定位:最大堆的最后一个元素,方便计算
        #建堆完成
        #如何让它升序输出如下:(涉及到之前说的清空堆),以下的是大根堆弹出
    for i in range(n-1,-1,-1):
        #i指向当前堆的最后一个元素
        a_list[0],a_list[i] = a_list[i],a_list[0]
        sift(a_list,0,i-1)#i-1是新的high,节省内存
a_list=[i for i in range(100)]

import random
random.shuffle(a_list)
print(a_list)
heap_sort(a_list)
print(a_list)

 random.shuffle()随机打乱这些数字

 ##运行结果

##时间复杂度 O(n)
  • O(n*logn)

##归并排序

  • 归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为。
##图片展示 

 ##代码
def merge(a_list,low,mid,high):#这个的前提是俩边已经排好序了,如上图9左边和右边的都排好序了
    i = low
    j = mid + 1
    ltmp = []#临时列表ltmp
    while i <= mid and j <= high:#左右两边都有数
        if a_list[i] < a_list[j]:
            ltmp.append(a_list[i])
            i += 1
        else:
            ltmp.append(a_list[j])
            j += 1
    while i <= mid:   
        ltmp.append(a_list[i])
        i += 1
    while j <= mid:
        ltmp.append(a_list[j])
        j += 1
    a_list[low:high+1] = ltmp
a_list = [2,4,5,9,1,3,7]
merge(a_list,0,3,6)
print(a_list)
##时间复杂度O(n)
  • O(n*logn)
  • 空间复杂度O(n)因为归并排序和其它排序(原地排序)不一样,它单独开设了一个itmp的空间,当要存的数据足够多时,它的空间复杂度是o(n)。

##小结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值