算法 排序
算法 排序
一、概述
排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。本文主要内容是介绍这八种排序算法。
二、冒泡排序:O( n^2)
三、优先队列:O( n^2):选择排序、插入排序;O(nlogn):堆排序
1、基础知识
一、位置列表
1、位置列表的抽象数据类型,可以定位到序列中任何元素的方法,并且能够执行任何位置的插入和删除操作的数据结构。位置的表示可以是数字索引(基于数组的序列)或者节点的引用(链表)!
2、面对对象需要提供强壮的数据结构和更灵活地设计数据结构,使得封装的ADT有良好的抽象!将位置抽象为一个非数字的位置的概念,即使是使用基于数组的序列!抽象的ADT(封装双向链表)实现中的位置抽象,基于数组的序列和链表的任意位置的插入和删除操作时间复杂度都是O(1)!L.first( ).element( ):将位置作为返回值,然后在获取位置的元素。
3、双向链表实现位置列表类:PositionalList( )
3.1、确认位置 :在内部,一个位置为链表的相关节点维护这引用信息,并且引用这包含指定节点的列表实例。检测到:发送不属于指定列表的位置实例;属于列表但是其指向节点不再是列表一部分的位置实例。首先,判断链表节点的位置实例是否属于指定列表,然后,判断指向节点的引用的位置实例是够属于指定列表(不是None),两个判断都为True,该节点才是指定列表一部分!
3.2、访问和更新方法:这些方法通过调整底层的双向链表实现,以支持位置列表ADT的公共接口。基于_validate( )来“解包”发送的任何位置,或者通过_make_position( )来“包装”节点作为Position实例即新创建节点的位置返回给用户,确保不要返回一个引用哨兵的位置。
3.3、位置列表L实现插入排序:插入排序是将列表分成两部分,一部分是已经排序,一部分尚未排序。尚未排序部分最左边的位置为变量pivot,维护一个maker变量,maker表示一个列表当前排序部分的最右边的位置。维护另一个变量 waker,waker 从 maker 向左移动,只要还有一个前驱元素的值大于位置 pivot 的元素,waker 就继续向左移动。
3.4、Python代码实现:
二、优先级队列
1、队列是“FIFO”策略。“FIFO”策略可以运用于公司和客户服务中心等影响因素单一的实际运用场景,但是却无法满足机场管制中为即将降落的飞机清理跑道这种需要考虑多种因素如:每个飞机跑道之间的距离、着陆过程中所用的时间、燃料的余量等共同影响决策结果的情况。优先级队列中优先级根据影响因素的组合设定不同的优先级,根据优先级进行对应的决策。
2、优先级队列是一种抽象数据类型,是一个包含优先级元素的集合,这个集合允许插入任意的元素,并允许删除拥有最高优先级的元素。
3、当一个元素被插入优先级队列中时,通过提供一个关联键来为该元素赋予一定的优先级!设定键值越小,优先级越高,即键值为1的元素的优先级比键值为2的优先级。键值最小的元素将是队列进行下一次操作的元素。如果对象的实例 a、b满足 a<b,也可以表示相同的优先级释义!这种普遍性,可以不用通过提供关联键设定优先级,而是更具对象实例的大小关系设定优先级高低!
4、Python对象中的实例 a、b,如果a<b,也可以表示 a 的优先级比 b 的优先级高!Python对象就可以用于定义键的自然顺序。应用程序可以为每个元素(对象)定义自己的优先级概念!如:不同的金融分析师可以给特定的资产设定不同的评级(即优先级),如股票的份额!
5、优先级队列实现:元素和关联建使用一个 key-value 建模,优先级队列的元素是包含 key-value 的元组(k,v)
6、优先级队列的ADT:
- P.add(k,v):优先级队列P中插入一个拥有键k和值v的元组(k,v);
- P.min( ):返回优先级队列P中拥有最小键值的元组,没有移除;
- P.remove_min( ):从优先级队列P中删除拥有最小键的元组,并且返回该元组。如果优先级队列P为空,返回错误。
- P.is_empty( ):如果优先级队列P没有任何元组,返回True;len(P):返回优先级队列P中元组的个数。
注:如何拓展ADT,允许用户更新优先级队列中的元素的键,即元素的优先级
7、位置列表L中的条目排序,实现优先级队列。基于列表L保存条目时,是否按键排序,两种实现优先级队列情况:一、使用未排序列表实现优先级队列(代码的简洁性:非公开的方法_find_min,可以获取到位置列表L的最小条目的位置,在P.min( )、P.remove_min( )可以直接调用最小条目的位置进行操作实现P.min( )、P.remove_min( )的操作时间复杂度为O(n);P.add(k,v)、P.is_empty( )操作时间复杂度为O(1))二、使用排序的位置列表L实现优先级队列,并且以键值的递增的顺序排序。第一个元组包含键最小的值也就是优先级最高的元素(P.min( )、P.remove_min( )、P.is_empty( )的操作时间复杂度为O(1);P.add(k,v)操作时间复杂度为O(n)))。位置列表L是双向链表实现,则空间复杂度都是O(n)。
三、数据结构 堆
1、使用二进制堆的数据结构实现的优先级队列的插入和删除操作的时间复杂度是O(logn)。
2、元组存储在堆的树节点中,而且堆的树中的节点满足两个附加属性:一、Head-Order属性:非根节点的p中的存储的元组中的键值大于等于其父节点中存储的键值>>T从根节点到叶子节点的路径上的键值是非递减顺序的,即T中根节点存储优先级序列的最小键值。P.min( )、P.remove_min( )的操作时间复杂度是O(1)。二、完全二叉树属性:高度为h的堆T是一颗完全二叉树。P.is_empty( )和len方法是基于树的检测实现。
3、P.add(k,v)-在堆中增加一个元组 > 首先:元组的位置在最后一层的最右节点的相邻位置;如果树为空或者在最后一层已经满足2i,新增加一层,将新增加的元组添加到新一层最左的位置。然后:插入的元组根据Head-Order属性向上冒泡!
4、P.remove(k,v)在堆中删除一个元组,堆删除删除节点的目的是使该节点的(k,v)在树上不存在。专注于该目的,不关注删除节点时是否真是想删除的那个节点,同时也不需考虑树结构的变化,因为树的结构本身就会因为自动平衡机制而经常进行调整。堆删除的节点都是树中最后位置的节点!删除节点之前键包含(k,v)的节点的值替换成最后位置的节点包含的(k,v),此时两者都是一样的!然后删除最后节点!如果不满足Head-Order属性,进行调整!
5、由于堆是完全二叉树,基于数组的二叉树表示非常适合堆!使用数字索引,所以索引 f(p) ∈【0,n-1】。
6、基于数组的二叉树表示堆的空间复杂度O(n),但是通过数字索引,P.add(k,v)、P.remove(k,v)可以通过数字索引操作时间复杂度为O(1),在二叉树的最后插入元组,然后根据Head-Order属性向上冒泡,操作的时间复杂度的最坏情况是O(logn),插入n个元素的时间复杂度是O(nlogn)。如果原来给定的键值对,堆是基于数组的二叉树,列表初始化不是空列表而是包含给定的元组组合集合的初始化列表,根据索引 f(p)可以从下向上构建堆,在堆算法的第一阶段可以选择运行时间复杂度为O(n)的自下而上的方法构建堆!但是删除的操作还是需要O(nlogn)。
# 7、Python代码实现: