数据结构与算法系列(三)—— 经典知识点总结

引入

程序设计语言基本数据类型:int,float,char
Python内置数据结构:list,dict,tuple.
Python扩展数据结构:栈,队列
存储一个student的name,age,hometown的方式

列表+元组:[
    ('zhangsan',24,'beijing'),
    ('zhangsan',24,'beijing'),
    ('zhangsan',24,'beijing'),
    ]
    for stu in stus:
    if stu[0] == 'zhangsan':
  列表+字典 [
    {'name':'zhangsan'},
   {'age':23},
    {'hometown':'beijjing'},
    ]
  字典:{
    'zhangsan':{
    'age':24,
    'hometown':'beijing',
   }
    }
    stu['zhangsan']

经典知识点总结

1.算法与数据结构的概念

    算法是指要解决问题的思路
    数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。

2.数据结构和算法的区别

    (1).数据结构只是静态的描述了数据元素之间的关系
    (2).高效的程序需要在数据结构的基础上设计和选择算法
        程序 = 算法 + 数据结构

    总结:算法是为了解决实际问题而设计的,数据结构是算法需要处理的问题载体

3.抽象数据类型(Abstract Data Type)

    ADT概念:是指一个数学模型以及定义在这个数学模型上的一组操作的集合,即把数据类型和数据类型上的运算绑定在一起,
             进行封装。对外只声明存储方式和调用方法,具体实现隐藏。
    
    引入抽象数据类型的目的是把数据类型的表示和数据类型上的运算的实现与这些数据类型和运算在程序中的引用隔开,使他们相互独立。

    常用的数据运算有五种:
        插入
        删除
        修改
        查找
        排序

4.时间复杂度

    衡量程序执行效率的快慢,称为时间复杂度。
        算法完成任务最少需要多少基本操作,即最优时间复杂度。(最乐观最理想,价值不大)
        算法完成任务最多需要多少基本操作,即最差时间复杂度。(提供了一种保证,主要关注)
        算法完成任务平均需要多少基本操作,即平均时间复杂度。(算法的全面评价)
        
    时间复杂度的几条基本计算规则
        1.基本操作,即只有常数项,认为其时间复杂度为〇(1)
        2.顺序结构,时间复杂度按加法计算
        3.循环结构,时间复杂度按乘法计算
        4.分支结构,时间复杂度取最大值
        5.判断一个算法的效率时,往往只需关注操作数量的最高次频,其他次要项和常数项可以忽略
        6.在没有特殊说明时,我们所分析的算法的时间复杂度,通常指的都是最坏时间复杂度。
        
    常见时间复杂度比较:〇(1)<〇(logn)<〇(n)<〇(nlogn)<〇(n^2)<〇(n^3)<〇(2^n)<〇(n!)<〇(n^n)

5.线性表:

线性表:一组序列元素的组织形式,一个线性表是某类元素的集合,还记录着元素之间的一种顺序关系,线性表是最基本的数据结构之一。 
顺序表:将元素顺序的存放在一块连续的存储区里,元素间的顺序关系由他们的存储顺序自然表示。 链表:将元素存放在通过链接构造起来的一系列存储块中。

6.顺序表

顺序表:数据元素本身连续存储,每个元素所占的存储单元大小固定相同,元素的下标是其逻辑地址,而元素存储的物理地址(实际内存地址)可以通过存储区的起始地址Loc(e0)加上逻辑地址
    (第i个元素)与存储单元大小(c)的乘积计算而得,即:Loc(ei) = Loc(e0)+c*i    故,访问指定元素无需从头遍历,通过计算便可获得对应地址,其时间复杂度为〇(1) 注:操作系统最小寻址单位是字节。int类型占四个字节,char类型占1个字节。计算机存储索引从0开始,表示偏移量。 顺序表的存储方式:顺序表的基本方式,顺序表的外置元素方式 顺序表的结构:一部分是表中元素的集合,另一部分是为实现正确操作而需记录的信息 表头信息(表头存储区的容量,当前表中已有元素个数),数据区 顺序表存储结构:一体式,分离式。区别:增加容量时,分离式表头不变,一体式表头改变

7.链表的提出

    顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活。
    链表结构可以充分利用计算机的内存空间,实现灵活的内存动态管理。

8.链表

8.1-定义:链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每一个节点(数据存储单元)里存放
          下一个节点的位置信息(即地址)。

8.2-单向链表:也叫单链表,是链表中最简单的一种形式。它的每个节点包含两个域,一个信息域(元素域),一个链接域。这个链接指向链表中的
              下一个结点,而最后一个节点的链接则指向一个空值。
      表元素域elem用来存放具体的数据
      链接域next用来存放下一个节点的位置(python中的标识)
      变量p指向链表的头结点(首节点)的位置,从p出发能找到表中的任意节点。
          
8.3 python变量标识的本质
    a = 10,首先会在内存中开启两个内存块,一个存储10,一个存储地址,这个地址指向10,给这个地址取名a。
    a,b = b,a 交换的是a,b原来维护的指向内存块的地址。
    
8.4 双向链表:每个节点有两个链接,一个指向前一个节点(前驱),一个指向后一个节点(后继)。当此节点为第一个节点时,前驱为空,
              当此节点为尾节点时,后继为空。

9.链表和顺序表的对比

9.1 顺序表     
        优点:(1) 方法简单,各种高级语言中都有数组,容易实现。
               (2) 不用为表示结点间的逻辑关系而增加额外的存储开销。
               (3) 顺序表具有按元素序号随机访问的特点。
               
        缺点:(1) 在顺序表中做插入删除操作时,需要对所有元素进行前后移位操作,只能通过拷贝和覆盖的方式,
                  平均移动大约表中一半的元素,因此对n较大的顺序表效率低。
              (2) 需要预先分配足够大的存储空间,估计过大,可能会导致顺序表后部大量闲置;预先分配过小,
                  又会造成溢出。                                       
9.2 链表  
        优点:(1) 在链表中做插入删除操作时,不会影响前面和后面的节点,因此对n较大的链表效率高。
              (2) 不需要预先分配足够大的存储空间,避免造成空间闲置或溢出的情况。
              
        缺点:(1) 需要为表示结点间的逻辑关系(指针变量)而增加额外的存储开销。
              (2) 只能通过遍历找到某个节点,不能使用下标直接定位节点。
              
              
9.3 选择合适的数据结构
    9.3.1 基于存储的考虑   
    顺序表的存储空间是静态分配的,在程序执行之前必须明确规定它的存储规模,也就是说事先对"MAXSIZE"要有合适的设定,过大造成浪费,
    过小造成溢出。可见对线性表的长度或存储规模难以估计时,不宜采用顺序表;链表不用事先估计存储规模,但链表的存储密度较低
   (存储密度是指一个结点中数据元素所占的存储单元和整个结点所占的存储单元之比,显然链式存储结构的存储密度是小于1的)。

    9.3.2 基于运算的考虑
    在顺序表中按序号访问 ai的时间性能时O(1),而链表中按序号访问的时间性能O(n),所以如果经常做的运算是按序号访问数据元素,
    显然顺序表优于链表;而在顺序表中做插入、删除时平均移动表中一半的元素,当数据元素的信息量较大且表较长时,这一点是不应忽视的;
    在链表中作插入、删除,虽然也要找插入位置,但操作主要是比较操作,从这个角度考虑显然后者优于前者。

    9.3.3 基于环境的考虑
    顺序表容易实现,任何高级语言中都有数组类型,链表的操作是基于指针的,相对来讲前者简单些,也是用户考虑的一个因素。

    注:顺序表和链表抓主要用于存储,栈和队列主要用于对数据的操作。

10.栈:只允许在表的一端进行操作的线性表,FILO(先进后出)
11.队列:只允许在表尾插入,表头删除的线性表,FIFO(先进先出)
12.双端队列:具有栈和队列的性质的数据结构,可以在队列的任意一端入队和出队

13:栈(Stack)和队列(Queue)是两种操作受限的线性表。

线性表:线性表是一种线性结构,它是一个含有n≥0个结点的有限序列,同一个线性表中的数据元素数据类型相同并且满足“一对一”的逻辑关系。
        “一对一”的逻辑关系指的是对于其中的结点,有且仅有一个开始结点没有前驱但有一个后继结点,有且仅有一个终端结点没有后继但有
        一个前驱结点,其它的结点都有且仅有一个前驱和一个后继结点。

这种受限表现在:栈的插入和删除操作只允许在表的尾端进行(在栈中成为“栈顶”),满足“FIFO:First In Last Out”;
                队列只允许在表尾插入数据元素,在表头删除数据元素,满足“First In First Out”。

栈与队列的相同点:
    1.都是线性结构。
    2.插入操作都是限定在表尾进行。
    3.都可以通过顺序结构和链式结构实现。、
    4.插入与删除的时间复杂度都是O(1),在空间复杂度上两者也一样。
    5.多链栈和多链队列的管理模式可以相同。

栈与队列的不同点:
    1.删除数据元素的位置不同,栈的删除操作在表尾进行,队列的删除操作在表头进行。 2.应用场景不同;常见栈的应用场景包括括号问题的求解,表达式的转换和求值,函数调用和递归实现,深度优先搜索遍历等;常见的队列的应用场景包括计算机系统中各种资源的管理,消息缓冲器的管理和广度优先搜索遍历等。
    3.顺序栈能够实现多栈空间共享,而顺序队列不能。

14.排序

稳定性:两个相同的元素在排序之后相对位置不发生改变称之为稳定排序。
14.1 交换排序:
    冒泡排序:每次循环找出最大值或最小值
          时间复杂度:最优 〇(n)    序列有序
                      最差 〇(n2)
                      稳定性:稳定
    快速排序:划分交换排序(partition-exchange sort),通过一趟偶排序将要排序的数据分割成独立的两部分,一部分的元素比另一部分的元素都要小。
              然后在按此方法对两部分数据分别进行快速排序,整个排序可以递归进行,以此达到整个数据变成有序序列
    
        时间复杂度:最优 〇(nlogn)
                      最差 〇(n2)
                      稳定性:不稳定    
14.2 选择排序:每次从无序序列中选择一个最大值或最小值放在一端。
        时间复杂度:最优 〇(n2)
                     最差 〇(n2)
                     稳定性:不稳定(考虑升序每次选择最大的情况)    2,2,1
                     
14.3 插入排序
    14.3.1 直接插入排序:每次从无序序列中按顺序拿一个元素插入有序序列中的正确位置。
        时间复杂度:最优 〇(n)  升序排序,序列已经处于升序状态
                     最差 〇(n2)
                     稳定性:稳定
                     
    14.3.2 希尔排序:缩小增量排序
        时间复杂度:最优 根据步长序列的不同而不同
                     最差 〇(n2)
                     稳定性:不稳定    2,2,1
                     
                     
14.4 归并排序:分治法,先递归分解数组,再合并数组
        时间复杂度: 最优:〇(nlogn)
                     最差: 〇(nlogn)
                     稳定性:稳定   

 


15.搜索

    二分查找法:又称折半查找。优点是比较次数少,查找速度快,平均性能好。缺点是要求待查表为有序表,且插入删除困难。因此,折半查找适用于不经常变            动而查找频繁的有序列表。
    时间复杂度: 最优:〇(1)
                 最差: 〇(logn)

16.树:由n(n>=1)个有限节点组成一个具有层次关系的集合。一对多。

特点:(1)每个节点有零个或多个子节点
     (2)没有父节点的节点称为根节点
     (3)每一个非根节点有且只有一个父节点
     (4)除了根节点外,每个子节点可以分为多个不相交的子树。
     
术语:节点的度:一个节点含有子树的个数
     树的度:一棵树中,最大节点的度称为树的度。
     叶节点或终端节点:度为零的节点。
     父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点。
     子节点:一个节点含有的子树的根节点称为该节点的子节点。
     兄弟节点:具有相同的父节点的节点互称为兄弟节点。
     节点的层次:从根节点开始定义,根为第一层,根的子节点为第2层,以此类推。
     树的深度或高度:树中节点的最大层次。
     堂兄弟节点:父节点在同一层次的节点互称为堂兄弟。
     节点的祖先:从根到该节点所经分支上的所有节点。
     子孙:以某节点为根的子树中任一节点成为该节点的子孙。
     森林:由m(m>=0)棵互不相交的树的集合称为森林。
树的种类:
      无序树:树中任意节点的子节点之间没有顺序关系,这种关系成为无序树。
      有序树:树中任意节点的子节点之间有顺序关系。这种关系称为有序树。
            二叉树:每个节点最多含有两个子树的树称为二叉树。
                完全二叉树:对于一个二叉树,假设其深度为d.除了第d层外,其他各层的节点数目均已达最大值,且第d层所有节点
                            从左到右连续紧密排列。对于这样的二叉树称为完全二叉树。
                            满二叉树:所有叶节点都在最底层的完全二叉树。
                平衡二叉树(AVL树):当且仅当任何节点的两颗子树的高度差不大于1的二叉树。
                排序二叉树(二叉查找树,二叉搜索树,有序二叉树)
      哈夫曼树(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树。
      B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。

17.树的存储方式

       顺序存储:速度快,所占空间大。
       链式存储:可以存储。缺点:指针域个数不定。

18.应用场景:

(1)xml,html
(2)路由协议
(3)mysql数据库索引
(4)文件系统的目录结构
(5)很多经典的AI算法,比如机器学习的(decision tree)决策树也是树结构                       

19.二叉树:每个节点最多含有两个子树的树称为二叉树。

    性质1:二叉树第i层最多只有2^(i-1)个节点。
    性质2:深度为k的二叉树最多有(2^k)-1个节点
    性质3:叶子节点的个数与度为2的节点的个数满足n0=n2+1
    性质4: 具有n个节点的完全二叉树的深度必为log2(n+1)
    性质5:完全二叉树,从上到下,从左到右,编号为i的节点,其左孩子编号为2i,右孩子编号为2i+1,其父节点为i/2(根节点除外)

20.二叉树的遍历

广度遍历:队列:先进先出
深度优先:栈:后进先出
          前序遍历:根左右
          中序遍历:左根右
          后序遍历:左右根
          
        先序:0 1 3 7 8 4 9 2 5 6 
        中序:7 3 8 1 9 4 0 5 2 6 
        后序:7 8 3 9 4 1 5 6 2 0 
        
          0
       1      2
     3   4  5   6
   7  89

算法实现(基于Python)

# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/6
@Author: Zhang Yafei
"""
"""
时间复杂度:衡量算法执行效率的快慢
a+b+c=1000,且a^2+b^2=c^2(a,b,c为自然数),如何求出所有a,b,c的自然数组合?
a,b,c
总时间=基本运算数量*执行次数
每台机器执行的总时间不同
但是执行基本运算数量大体相同
"""
import time

start_time = time.time()
# for a in range(1001):
#     for b in range(1001):
#         for c in range(1001):
#             if a+b+c==1000 and a**2+b**2==c**2:
#                 print('a,b,c:',a,b,c)      #222秒
"""
T = 1000 * 1000 * 1000 * 2
T = 2000 * 2000 * 2000 * 2
T = N * N * N *2
T(n) = n^3 * 2
T(n) = n^3 * 10
T(n) = n^3 * k
从数学上,T(n) = k*g(n),在现实中,省去细枝末叶,只剩下最显著特征
T(n) = g(n)
g(n) = n^3
"""
#顺序
#条件
#循环

for a in range(1001):
    for b in range(1001):
        c = 1000-a-b
        if a**2+b**2==c**2:
            print('a,b,c:',a,b,c)    #1秒
end_time = time.time()

print('times:',end_time-start_time)
print('finished')
"""
T(n) = n * n *(1+max(1,0))
     = n^2*2
     = 〇(n^2)
"""
1.算法基础
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/6
@Author: Zhang Yafei
"""
from timeit import Timer
"""
li1 = [1,2]
li2 = [23,5]
li = li1 + li2
li = [i for i in range(10000)]
li = list(range(10000))
"""


def t1():
    li = []
    for i in range(10000):
        li.append(i)


def t2():
    li = []
    for i in range(10000):
        # li = li + [i]   #218秒
        li += [i]  #1.04


def t3():
    li = [i for i in range(10000)]


def t4():
    li = list(range(10000))


def t5():
    li = []
    for i in range(10000):
        li.extend([i])


timer1 = Timer("t1()","from __main__ import t1")
print('append:',timer1.timeit(1000))
timer2 = Timer("t2()","from __main__ import t2")
print('+:',timer2.timeit(1000))
timer3 = Timer("t3()","from __main__ import t3")
print('[i for i in range]:',timer3.timeit(1000))
timer4 = Timer("t4()","from __main__ import t4")
print('list(range())',timer4.timeit(1000))
timer5 = Timer("t5()","from __main__ import t5")
print('extend:',timer5.timeit(1000))


def t6():
    li = []
    for i in range(10000):
        li.append(i)


def t7():
    li = []
    for i in range(10000):
        li.insert(0,i)


# timer6 = Timer("t6()","from __main__ import t6")
# print('append:',timer6.timeit(1000))
timer7 = Timer("t7()","from __main__ import t7")
print('insert:',timer7.timeit(1000))
2.list效率对比
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/11
@Author: Zhang Yafei
"""
import sys
from timeit import Timer

reps = 1000
size = 10000


def forStatement():
    res = []
    for x in range(size):
        res.append(abs(x))


# 列表解析
def listComprehension():
    res = [abs(x) for x in range(size)]


# map
def mapFunction():
    res = list(map(abs, range(size)))


# 生成器表达式
def generatorExpression():
    res = list(abs(x) for x in range(size))


if __name__ == '__main__':
    print(sys.version)
    timer1 = Timer("forStatement()", "from __main__ import forStatement")
    print('for循环append', timer1.timeit(reps))
    timer2 = Timer("listComprehension()", "from __main__ import listComprehension")
    print('列表生成式', timer2.timeit(reps))
    timer3 = Timer("mapFunction()", "from __main__ import mapFunction")
    print('map:', timer3.timeit(reps))
    timer4 = Timer("generatorExpression()", "from __main__ import generatorExpression")
    print('生成器表达式', timer4.timeit(reps))
3.for_map_list生成式_生成器表达式
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/8
@Author: Zhang Yafei
"""


class Node(object):
    """节点"""
    def __init__(self,elem):
        self.elem = elem
        self.next = None


class SingleLinkList(object):
    """单链表"""
    def __init__(self,node=None):
        self.__head = node

    def __str__(self):
        if not self.__head:
            return 'None'
        else:
            cur = self.__head
            list_str = ''
            while cur:
                cur_str = '{}->'.format(cur.elem) if cur.next else str(cur.elem)
                list_str += cur_str
                cur = cur.next
            return list_str

    def is_empty(self):
        """链表是否为空"""
        return self.__head is None

    def length(self):
        """链表长度"""
        #cur游标,用来移动遍历节点
        cur = self.__head
        #count记录数量
        count = 0
        while cur:  #cur.next == None
            cur = cur.next
            count += 1
        return count

    def travel(self):
        """遍历整个链表"""
        cur = self.__head
        while cur:
            print(cur.elem,end=" ")
            cur = cur.next
        print('')

    def add(self,item):
        """链表头部添加元素,头插法"""
        node = Node(item)
        node.next = self.__head
        self.__head = node

    def append(self,item):
        """链表尾部添加元素,尾插法"""
        node = Node(item)
        if self.is_empty():#链表判空
            self.__head = node
        else:
            cur = self.__head
            while cur.next:
                cur = cur.next
            cur.next = node

    def insert(self,pos,item): #insert(2,100)
        """指定位置添加元素
        :param pos 从0开始
        """
        if pos<=0:
            self.add(item)
        elif pos>(self.length()-1):
            self.append(item)
        else:
            node = Node(item)
            pre = self.__head
            count = 0
            while count < pos-1:
                pre = pre.next
                count += 1
            #当循环退出后,pre指向pos-1位置
            node.next = pre.next
            pre.next = node

    def remove(self,item):
        """删除节点"""
        cur = self.__head
        prev = None
        while cur:
            if cur.elem == item:
                #先判断此节点是否是头结点
                if cur == self.__head:
                    self.__head = cur.next
                else:
                    prev.next = cur.next
                break
            else:
                prev = cur
                cur = cur.next

    def search(self,item):
        """查询节点是否存在"""
        cur = self.__head
        while cur:
            if cur.elem == item:
                return True
            cur = cur.next
        return False

    def index(self,item):
        """查询节点是否存在"""
        cur = self.__head
        count = 0
        while cur:
            if cur.elem == item:
                return count
            cur = cur.next
            count += 1
        return '{} is not in linklist'.format(item)

    def value(self,index):
        """查找指定位置的值"""
        if self.is_empty():
            return None
        elif index>self.length()-1:
            return 'linklist index out of range'
        cur = self.__head
        count = 0
        while count<index:
            count += 1
            cur = cur.next
        return cur.elem


if __name__ == '__main__':
    ll = SingleLinkList()
    print(ll.is_empty())
    print(ll.length())
    # print(ll)
    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    # print(ll)
    ll.insert(-1,9) #9,8,123456
    ll.travel()
    ll.insert(3,100) #9,8,1,100,23456
    ll.travel()
    ll.insert(10,200) #9,8,1,100,23456,200
    ll.travel()
    ll.remove(9)
    ll.travel()
    ll.remove(100)
    ll.travel()
    ll.remove(200)
    ll.travel()
    print(ll.value(3))
    print(ll.index(7))
4.单链表
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/9
@Author: Zhang Yafei
"""


class Node(object):
    """结点"""
    def __init__(self,item):
        self.elem = item
        self.prev = None
        self.next = None


class DoubleLinkList(object):
    """双链表"""
    def __init__(self,node=None):#默认为None第一次创建空链表
        self.__head = node

    def __str__(self):
        self.travel()
        return

    def is_empty(self):
        """链表是否为空"""
        return self.__head is None

    def length(self):
        """链表长度"""
        #cur游标,用来移动遍历节点
        cur = self.__head
        #count记录数量
        count = 0
        while cur:  #cur.next == None
            cur = cur.next
            count += 1
        return count

    def travel(self):
        """遍历整个链表"""
        cur = self.__head
        while cur:
            print(cur.elem,end=" ")
            cur = cur.next
        print('')

    def add(self,item):
        """链表头部添加元素,头插法"""
        node = Node(item)
        node.next = self.__head
        self.__head = node
        node.prev = None

    def append(self,item):
        """链表尾部添加元素,尾插法"""
        node = Node(item)
        if self.is_empty():#链表判空
            self.__head = node
        else:
            cur = self.__head
            while cur.next:
                cur = cur.next
            cur.next = node
            node.prev = cur

    def insert(self,pos,item): #insert(2,100)
        """指定位置添加元素
        :param pos 从0开始
        """
        if pos<=0:
            self.add(item)
        elif pos>(self.length()-1):
            self.append(item)
        else:
            cur = self.__head
            count = 0
            while count < pos:
                cur = cur.next
                count += 1
            #当循环退出后,pre指向pos-1位置
            node = Node(item)
            node.next = cur
            node.prev = cur.prev
            cur.prev.next = node
            cur.prev = node

    def remove(self,item):
        """删除节点"""
        if self.is_empty():
            return
        cur = self.__head
        while cur:
            if cur.elem == item:
                #先判断此节点是否是头结点
                if cur == self.__head:
                    self.__head = cur.next
                    if cur.next:
                        #判断链表是否只有一个节点
                        cur.next.prev = None
                else:
                    cur.prev.next = cur.next
                    if cur.next:
                        #判断链表是否是尾节点
                        cur.next.prev = cur.prev
                break
            else:
                cur = cur.next

    def search(self,item):
        """查询节点是否存在"""
        cur = self.__head
        while cur:
            if cur.elem == item:
                return True
            cur = cur.next
        return False

    def index(self,item):
        """查询节点是否存在"""
        cur = self.__head
        count = 0
        while cur:
            if cur.elem == item:
                return count
            cur = cur.next
            count += 1
        return '{} is not in linklist'.format(item)

    def value(self,index):
        """查找指定位置的值"""
        if self.is_empty():
            return None
        elif index>self.length()-1:
            return 'linklist index out of range'
        cur = self.__head
        count = 0
        while count<index:
            count += 1
            cur = cur.next
        return cur.elem


if __name__ == '__main__':
    dll = DoubleLinkList()
    print(dll.is_empty())
    print(dll.length())
    # print(dll)
    dll.append(1)
    print(dll.is_empty())
    print(dll.length())

    dll.append(2)
    dll.add(8)
    dll.append(3)
    dll.append(4)
    dll.append(5)
    dll.append(6)
    # print(dll)
    dll.insert(-1, 9)  # 9,8,123456
    dll.travel()
    dll.insert(3, 100)  # 9,8,1,100,23456
    dll.travel()
    dll.insert(10, 200)  # 9,8,1,100,23456,200
    dll.travel()
    dll.remove(9)
    dll.travel()
    dll.remove(100)
    dll.travel()
    dll.remove(200)
    dll.travel()
    print(dll.value(0))
    print(dll.index(8))
    print(dll.search(4))
5.双链表
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/8
@Author: Zhang Yafei
"""


class Node(object):
    """节点"""
    def __init__(self,elem):
        self.elem = elem
        self.next = None


class SingleCycleLinkList(object):
    """单向循环链表"""
    def __init__(self,node=None):
        self.__head = node
        if node:
            node.next = node

    def __str__(self):
        if not self.__head:
            return 'None'
        else:
            cur = self.__head
            list_str = ''
            while cur:
                cur_str = '{}->'.format(cur.elem) if cur.next else str(cur.elem)
                list_str += cur_str
                cur = cur.next
            return list_str

    def is_empty(self):
        """链表是否为空"""
        return self.__head is None

    def length(self):
        """链表长度"""
        #cur游标,用来移动遍历节点
        if self.is_empty():
            return 0
        cur = self.__head
        #count记录数量
        count = 1
        while cur.next != self.__head:  #cur.next == None
            cur = cur.next
            count += 1
        return count

    def travel(self):
        """遍历整个链表"""
        if self.is_empty():
            return
        cur = self.__head
        while cur.next != self.__head:
            print(cur.elem,end=" ")
            cur = cur.next
        #退出循环,cur指向尾节点,但尾节点的元素未打印
        print(cur.elem)

    def add(self,item):
        """链表头部添加元素,头插法"""
        node = Node(item)
        if self.is_empty():
            self.__head = node
            node.next = node
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            node.next = self.__head
            self.__head = node
            # cur.next = node
            cur.next = self.__head

    def append(self,item):
        """链表尾部添加元素,尾插法"""
        node = Node(item)
        if self.is_empty():#链表判空
            self.__head = node
            node.next = node
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            node.next = cur.next
            cur.next = node

    def insert(self,pos,item): #insert(2,100)
        """指定位置添加元素
        :param pos 从0开始
        """
        if pos<=0:
            self.add(item)
        elif pos>(self.length()-1):
            self.append(item)
        else:
            node = Node(item)
            pre = self.__head
            count = 0
            while count < pos-1:
                pre = pre.next
                count += 1
            #当循环退出后,pre指向pos-1位置
            node.next = pre.next
            pre.next = node

    def remove(self,item):
        """删除节点"""
        if self.is_empty():
            return
        cur = self.__head
        prev = None
        while cur.next != self.__head:
            if cur.elem== item:
                #先判断此节点是否是头结点
                if cur == self.__head:
                    #头结点的情况
                    #找尾节点
                    rear = self.__head
                    while rear.next != self.__head:
                        rear = rear.next
                    self.__head = cur.next
                    rear.next = self.__head
                else:
                    #中间节点
                    prev.next = cur.next
                return
            else:
                prev = cur
                cur = cur.next
        #退出循环,cur指向尾节点
        if cur.elem == item:
            if cur == self.__head:
               #链表中只有一个节点
                self.__head = None
            else:
                # prev.next = cur.next
                prev.next = self.__head

    def search(self,item):
        """查询节点是否存在"""
        if self.is_empty():
            return False
        cur = self.__head
        while cur.next != self.__head:
            if cur.elem == item:
                return True
            cur = cur.next
        #退出循环,cur指向尾节点
        if cur.elem == item:
            return True
        return False

    def index(self,item):
        """查询节点是否存在"""
        if self.is_empty():
            return None
        cur = self.__head
        count = 0
        while cur.next != self.__head:
            if cur.elem == item:
                return count
            cur = cur.next
            count += 1
        if cur.elem == item:
            return count
        raise ValueError('{} is not in linkCyclelist'.format(item))

    def value(self,index):
        """查找指定位置的值"""
        if self.is_empty():
            raise AttributeError('LinkCycleList is None')
        elif index>self.length()-1:
            raise IndexError('linklist index out of range')
        cur = self.__head
        count = 0
        while count<index:
            count += 1
            cur = cur.next
        return cur.elem


if __name__ == '__main__':
    ll = SingleCycleLinkList()
    print(ll.is_empty())
    print(ll.length())
    # print(ll)
    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    # print(ll)
    ll.insert(-1,9) #9,8,123456
    ll.travel()
    ll.insert(3,100) #9,8,1,100,23456
    ll.travel()
    ll.insert(10,200) #9,8,1,100,23456,200
    ll.travel()
    ll.remove(9)
    ll.travel()
    ll.remove(100)
    ll.travel()
    ll.remove(200)
    ll.travel()
    print(ll.value(0))
    print(ll.index(8))
    # help(SingleCycleLinkList)
6.单向循环链表
 -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/8
@Author: Zhang Yafei
"""


class Node(object):
    """结点"""
    def __init__(self,item):
        self.elem = item
        self.prev = None
        self.next = None


class DoubleCycleLinkList(object):
    """双链表"""
    def __init__(self,node=None):#默认为None第一次创建空链表
        self.__head = Node('HEAD')
        self.__head.next = node
        self.__head.prev = node
        if node is not None:
            node.next = self.__head
            node.prev = self.__head

    def __str__(self):
        self.travel()
        return

    def is_empty(self):
        """链表是否为空"""
        return self.__head.next is None

    def length(self):
        """链表长度"""
        #cur游标,用来移动遍历节点
        if self.is_empty():
            return 0
        cur = self.__head.next
        #count记录数量
        count = 0
        while cur != self.__head:  #cur.next == None
            cur = cur.next
            count += 1
        return count

    def travel(self):
        """遍历整个链表"""
        cur = self.__head.next
        while cur != self.__head:
            print(cur.elem,end=" ")
            cur = cur.next
        print('')

    def add(self,item):
        """链表头部添加元素,头插法"""
        node = Node(item)
        if self.is_empty():
             #空链表
            node.next = self.__head
            node.prev = self.__head
            self.__head.next = node
            self.__head.prev = node
        else:
            #非空链表
            node.next = self.__head.next
            self.__head.next = node
            node.prev = self.__head
            node.next.prev = node

    def append(self,item):
        """链表尾部添加元素,尾插法"""
        node = Node(item)
        if self.is_empty():#链表判空
            node.next = self.__head
            node.prev = self.__head
            self.__head.next = node
            self.__head.prev = node
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            node.next = cur.next
            node.prev = cur
            cur.next = node
            self.__head.prev = node

    def insert(self,pos,item): #insert(2,100)
        """指定位置添加元素
        :param pos 从0开始
        """
        if pos<=0:
            self.add(item)
        elif pos>(self.length()-1):
            self.append(item)
        else:
            cur = self.__head.next
            count = 0
            while count < pos:
                cur = cur.next
                count += 1
            #当循环退出后,pre指向pos-1位置
            node = Node(item)
            node.next = cur
            node.prev = cur.prev
            cur.prev.next = node
            cur.prev = node

    def remove(self,item):
        """删除节点"""
        if self.is_empty():
            return
        cur = self.__head.next
        while cur:
            if cur.elem == item:
                #先判断此节点是否是头结点
                if cur == self.__head.next:
                    self.__head.next = cur.next
                    if cur.next == self.__head:
                        #判断链表是否只有一个节点
                        self.__head.next = None
                        self.__head.prev = None
                    else:
                        cur.next.prev = self.__head
                else:
                    cur.prev.next = cur.next
                    if cur.next != self.__head:
                        #判断链表是否是尾节点
                        cur.next.prev = cur.prev
                    else:
                        self.__head.prev = cur.prev
                break
            else:
                cur = cur.next

    def search(self,item):
        """查询节点是否存在"""
        if self.is_empty():
            return False
        cur = self.__head.next
        while cur:
            if cur.elem == item:
                return True
            cur = cur.next
        return False

    def index(self,item):
        """查询指定元素的索引"""
        cur = self.__head.next
        count = 0
        while cur:
            if cur.elem == item:
                return count
            cur = cur.next
            count += 1
        return '{} is not in linklist'.format(item)

    def value(self,index):
        """查找指定位置的值"""
        if self.is_empty():
            return None
        elif index>self.length()-1:
            return 'linklist index out of range'
        cur = self.__head.next
        count = 0
        while count<index:
            count += 1
            cur = cur.next
        return cur.elem


if __name__ == '__main__':
    dcl = DoubleCycleLinkList()
    print(dcl.is_empty())
    print(dcl.length())
    # print(dcl)
    dcl.append(1)
    print(dcl.is_empty())
    print(dcl.length())

    dcl.append(2)
    dcl.add(8)
    dcl.append(3)
    dcl.append(4)
    dcl.append(5)
    dcl.append(6)
    # print(dcl)
    dcl.insert(-1, 9)  # 9,8,123456
    dcl.travel()
    dcl.insert(3, 100)  # 9,8,1,100,23456
    dcl.travel()
    dcl.insert(10, 200)  # 9,8,1,100,23456,200
    dcl.travel()
    dcl.remove(9)
    dcl.travel()
    dcl.remove(100)
    dcl.travel()
    dcl.remove(200)
    dcl.travel()
    print(dcl.value(0))
    print(dcl.index(5))
    print(dcl.search(4))
7.双向循环链表
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/10
@Author: Zhang Yafei
"""


class Stack(object):
    """"""
    def __init__(self):
        self.__list = []  #私有变量,不允许外部调用者对其进行操作

    def push(self,item):
        """添加一个新的元素item到栈顶"""
        self.__list.append(item)   #顺序表尾部插入时间复杂度O(1),头部插入O(n),故尾部方便
        #self.__list.insert(0,item)   #链表表尾部插入时间复杂度O(n),头部插入O(1),故链表用头插方便

    def pop(self):
        """弹出栈顶元素"""
        return self.__list.pop()

    def peek(self):
        """返回栈顶元素"""
        if self.__list:
            return self.__list[-1]
        return None

    def is_empty(self):
        """判断栈是否为空"""
        return self.__list == []
        # return not self.__list       #0,{},[],(),'',None在python中都是False,

    def size(self):
        """返回栈的元素个数"""
        return self.__list.__len__()


if __name__ == '__main__':
    s = Stack()
    s.push(1)
    s.push(2)
    s.push(3)
    s.push(4)

    print(s.pop())
    print(s.pop())
    print(s.pop())
8.栈的实现
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/10
@Author: Zhang Yafei
"""


class Queue(object):
    """队列"""
    def __init__(qelf):
        qelf.__list = []

    def enqueue(qelf,item):
        """往队列添加一个item元素"""
        qelf.__list.append(item)   #O(1)  入队频繁时,用队尾插入,队头删除
        # qelf.__list.insert(0,item)   #O(n),出队频繁时,队头插入,队尾删除

    def dequeue(qelf):
        """往队列头部删除一个元素"""
        return qelf.__list.pop(0)  #O(n)
        # return qelf.__list.pop()  #O(1)    当出队比较频繁时,用pop比较方便

    def iq_empty(self):
        """判断队是否为空"""
        return qelf.__list == []

    def qize(self):
        """返回队列的大小"""
        return len(qelf.__list)


if __name__ == '__main__':
    q = Queue()
    q.enqueue(1)
    q.enqueue(2)
    q.enqueue(3)
    q.enqueue(4)

    print(q.dequeue())
    print(q.dequeue())
    print(q.dequeue())
    print(q.dequeue())
9.队列的实现
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/10
@Author: Zhang Yafei
"""


class Dqueue(object):
    """双端队列"""
    def __init__(self):
        self.__list = []

    def __str__(self):
        queue = list(map(str,self.__list))
        if not self.__list:
            return 'None'
        else:
            return ' '.join(queue)

    def add_front(self,item):
        """往队列头部插入元素"""
        self.__list.insert(0,item)

    def add_rear(self,item):
        """往队列尾部插入元素"""
        self.__list.append(item)

    def pop_front(self):
        """队列头部删除元素"""
        self.__list.pop(0)

    def pop_rear(self):
        """队列尾部删除元素"""
        self.__list.pop()

    def is_empty(self):
        """判断队列是否为空"""
        return not self.__list

    def size(self):
        """返回队列的大小"""
        return len(self.__list)


if __name__ == '__main__':
    dqueue = Dqueue()
    print(dqueue)
    dqueue.add_front(1)
    print(dqueue)
    dqueue.add_front(2)
    print(dqueue)
    dqueue.add_front(3)
    print(dqueue)
    dqueue.add_rear(4)
    print(dqueue)
    dqueue.add_rear(5)
    print(dqueue)
    dqueue.add_rear(6)
    print(dqueue)
    dqueue.pop_front()
    print(dqueue)
    dqueue.pop_rear()
    print(dqueue)
    dqueue.pop_front()
    print(dqueue)
    dqueue.pop_rear()
    print(dqueue)
    dqueue.pop_front()
    print(dqueue)
    dqueue.pop_rear()
    print(dqueue)
10.双端队列
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/10
@Author: Zhang Yafei
"""


def bubble_sort(alist):
    """冒泡排序,每一次循环找出最大值"""
    n = len(alist)
    for i in range(n-1):
        count = 0
        for j in range(n-i-1):
            if alist[j] > alist[j+1]:
                alist[j],alist[j+1] = alist[j+1],alist[j]
                count += 1
        if not count:
            return


def bubble_sort2(alist):
    """冒泡排序,每一次循环找出最小值"""
    n = len(alist)
    for i in range(n-1,0,-1):
        for j in range(i):
            if alist[j] > alist[j+1]:
                alist[j],alist[j+1] = alist[j+1],alist[j]


if __name__ == '__main__':
    li = [54,26,93,17,77,31,44,55,20]
    print(li)
    bubble_sort(li)
    # bubble_sort2(li)
    print(li)
11.冒泡排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/10
@Author: Zhang Yafei
"""
import time


def select_sort1(alist):
    start = time.clock()
    for i in range(0,len(alist)):
        min_v = min(alist[i:len(alist)])
        j = alist.index(min_v)
        alist[i],alist[j] = alist[j],alist[i]
    use_time = time.clock()-start
    print(use_time)


def select_sort(alist):
    start = time.clock()
    n = len(alist)
    for j in range(n-1):  #j:0~n-2
        min_index = j
        for i in range(j+1,n):
            if alist[min_index] > alist[i]:
                min_index = i
        alist[j],alist[min_index] = alist[min_index],alist[j]
    use_time = time.clock()-start
    print(use_time)


if __name__ == '__main__':
    li = [54,226,93,17,77,31,44,55,20]
    print(li)
    select_sort(li)
    # select_sort1(li)
    print(li)
12.简单选择排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/10
@Author: Zhang Yafei
"""
from timeit import Timer

# alist = [54, 226, 93, 17, 77, 31, 44, 55, 20]
# alist1 = [54, 226, 93, 17, 77, 31, 44, 55, 20]


def insert_sort(alist):
    """插入排序"""
    #从第二个位置,即下标为1的元素开始向前插入
    for i in range(1,len(alist)):
        for j in range(i,0,-1):
            #从第i个元素开始向前比较,如果小于前一个,则交换,否则终止循环
            if alist[j]<alist[j-1]:
                alist[j-1],alist[j] = alist[j],alist[j-1]
            else:
                break


def insert_sort1(alist1):
    """插入排序"""
    #从右边的无序序列中
    for j in range(1,len(alist1)):
        #i代表内层循环起始值
        i = j
        #执行从右边的无序序列中取出第一个元素,即i位置的元素,然后将其插入到前面的正确的位置中
        while i > 0:
            if alist1[i]<alist1[i-1]:
                alist1[i],alist1[i-1] = alist1[i-1],alist1[i]
                i -= 1
            else:
                break


if __name__ == '__main__':
    li = [54, 226, 93, 17, 77, 31, 44, 55, 20]
    print(li)
    # insert_sort(li)
    insert_sort1(li)
    print(li)
    # for_time = Timer('insert_sort()','from __main__ import insert_sort')
    # while_time = Timer("insert_sort1()","from __main__ import insert_sort1")
    # print('for_time',for_time.timeit(1000))
    # print('while_time',while_time.timeit(1000))
13.插入排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/10
@Author: Zhang Yafei
"""


def shell_sort(alist):
    """希尔排序"""
    gap = len(alist) // 2
    # gap变化到0之前,插入算法执行的次数
    while gap > 0 :
        #插入算法,与普通插入算法的区别就是gap步长
        for i in range(gap,len(alist)):
            # i=[gap,gap+1,gap+2,...]
            j = i
            while j > 0:
                if alist[j] < alist[j-gap]:
                    alist[j],alist[j-gap] = alist[j-gap],alist[j]
                    j -= gap
                else:
                    break
        gap //= 2


if __name__ == '__main__':
    li = [54, 226, 93, 17, 77, 31, 44, 55, 20]
    print(li)
    shell_sort(li)
    print(li)
14.希尔排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/11
@Author: Zhang Yafei
"""


def quick_sort(alist,first,last):
    """快速排序"""
    if first >= last:
        return
    mid_value = alist[first]
    low = first
    high = last
    #游标从右往左一直走,知道遇到比当前mid_value小的元素,将该元素赋值给low所指的位置
    while low < high:
        #high左移
        while low < high and alist[high] >= mid_value:
            high -= 1
        alist[low] = alist[high]

        while low<high and alist[low] < mid_value:
            low += 1
        alist[high] = alist[low]
    # 从循环退出,low=high
    alist[low] = mid_value

    #对low左边的列表执行快速排序
    quick_sort(alist,first,low-1)
    #对low右边的列表排序
    quick_sort(alist,low+1,last)


if __name__ == '__main__':
    li = [54, 226, 93, 17, 77, 31, 44, 55, 20]
    print(li)
    quick_sort(li,0,len(li)-1)
    print(li)
15.快速排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/11
@Author: Zhang Yafei
"""


def merge_sort(alist):
    """归并排序"""
    n = len(alist)
    if n<=1:
        return alist
    mid = n//2

    left_li = merge_sort(alist[:mid])
    right_li = merge_sort(alist[mid:])

    left_pointer,right_pointer = 0,0
    result = []
    while left_pointer < len(left_li) and right_pointer < len(right_li):
        if left_li[left_pointer] < right_li[right_pointer]:
            result.append(left_li[left_pointer])
            left_pointer += 1
        else:
            result.append(right_li[right_pointer])
            right_pointer += 1

    result += left_li[left_pointer:]
    result += right_li[right_pointer:]

    return result


if __name__ == '__main__':
    li = [54, 226, 93, 17, 77, 31, 44, 55, 20]
    print(li)
    sorted_list = merge_sort(li)
    print(li)
    print(sorted_list)
16.归并排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/11
@Author: Zhang Yafei
"""


def binary_search(alist,item):
    """二分查找"""
    n = len(alist)
    if n > 0:
        mid = n//2
        if alist[mid] == item:
            return True
        elif item < alist[mid]:
            return binary_search(alist[:mid],item)
        else:
            return binary_search(alist[mid+1:],item)
    return False


def binary_search_2(alist,item):
    """二分查找非递归查版本"""
    n = len(alist)
    first = 0
    last = n-1
    while first <= last:
        mid = (first + last) // 2
        if alist[mid] == item:
            return True
        elif item < alist[mid]:
            last = mid - 1
        else:
            first = mid + 1
    return False


if __name__ == '__main__':
    li = [17, 20, 31, 44, 54, 55, 77, 93, 226]
    print(binary_search(li,31))
    print(binary_search_2(li,31))
17.二分查找
# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/11
@Author: Zhang Yafei
"""


class Node(object):
    """树节点"""
    def __init__(self, item=None):
        self.elem = item
        self.lchild = None
        self.rchild = None


class Tree(object):
    """二叉树"""
    def __init__(self):
        self.root = None

    def add(self, item):
        node = Node(item)
        if self.root is None:
            self.root = Node(item)
            return
        queue = [self.root]
        while queue:
            cur_node = queue.pop(0)
            if cur_node.lchild is None:
                cur_node.lchild = node
                return
            else:
                queue.append(cur_node.lchild)
            if cur_node.rchild is None:
                cur_node.rchild = node
                return
            else:
                queue.append(cur_node.rchild)

    def breadth_travel(self):
        """广度遍历"""
        if not self.root:
            return
        queue = [self.root]
        while queue:
            cur_node = queue.pop(0)
            print(cur_node.elem,end=' ')
            if cur_node.lchild:
                queue.append(cur_node.lchild)
            if cur_node.rchild:
                queue.append(cur_node.rchild)

    def preorder(self, node):
        """先序遍历"""
        if not node:
            return
        print(node.elem, end=' ')
        self.preorder(node.lchild)
        self.preorder(node.rchild)

    def inorder(self,node):
        """中序遍历"""
        if not node:
            return
        self.inorder(node.lchild)
        print(node.elem,end=' ')
        self.inorder(node.rchild)

    def postorder(self,node):
        """后序遍历"""
        if not node:
            return
        self.postorder(node.lchild)
        self.postorder(node.rchild)
        print(node.elem,end=' ')


if __name__ == '__main__':
    tree = Tree()
    tree.add(0)
    tree.add(1)
    tree.add(2)
    tree.add(3)
    tree.add(4)
    tree.add(5)
    tree.add(6)
    tree.add(7)
    tree.add(8)
    tree.add(9)
    tree.breadth_travel()
    print('')
    tree.preorder(tree.root)
    print('')
    tree.inorder(tree.root)
    print('')
    tree.postorder(tree.root)
    print('')
18.二叉树的广度、深度优先及先、中、后序遍历
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/4/10
@Author: Zhang Yafei

桶排序(Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶里。
每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是
鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间

桶排序以下列程序进行:
    1. 设置一个定量的数组当作空桶子。
    2. 寻访序列,并且把项目一个一个放到对应的桶子去。
    3. 对每个不是空的桶子进行排序。
    4. 从不是空的桶子里把项目再放回原来的序列中。
"""


def bucket_sort2(array):
    # 1.创建n个空桶
    n = len(array)
    new_list = [[] for _ in range(n)]

    # 2.把arr[i] 插入到bucket[n*array[i]]
    for data in array:
        index = int(data * n)
        new_list[index].append(data)

    # 3.桶内排序
    for i in range(n):
        new_list[i].sort()

    # 4.产生新的排序后的列表
    index = 0
    for i in range(n):
        for j in range(len(new_list[i])):
            array[index] = new_list[i][j]
            index += 1
    return array


def bucket_sort(nums):
    # 选择一个最大的数
    max_num = max(nums)
    # 创建一个元素全是0的列表, 当做桶
    bucket = [0] * (max_num + 1)
    # 把所有元素放入桶中, 即把对应元素个数加一
    for i in nums:
        bucket[i] += 1

    # 存储排序好的元素
    sort_nums = []
    # 取出桶中的元素
    for j in range(len(bucket)):
        if bucket[j] != 0:
            for y in range(bucket[j]):
                sort_nums.append(j)
    return sort_nums


if __name__ == '__main__':
    data = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    data2 = [0.897, 0.565, 0.656, 0.1234, 0.665, 0.3434]
    data3 = data2.copy()
    sort_data = bucket_sort(data)
    sort_data2 = bucket_sort2(data2)
    print(data)
    print(sort_data)
    print(data3)
    print(sort_data2)
19.桶排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/4/10
@Author: Zhang Yafei
"""


def swap(data, root, last):
    data[root], data[last] = data[last], data[root]


def adjust_heap(data, par_node, high):
    """
    调整父节点 与孩子大小, 制作大顶堆
    :param data:
    :param par_node:
    :param high:
    :return:
    """
    new_par_node = par_node
    j = 2 * par_node + 1  # 取根节点的左孩子, 如果只有一个孩子 high就是左孩子,如果有两个孩子 high 就是右孩子

    while j <= high:  # 如果 j = high 说明没有右孩子,high就是左孩子
        if j < high and data[j] < data[j + 1]:  # 如果这儿不判断 j < high 可能超出索引
            # 一个根节点下,如果有两个孩子,将 j  指向值大的那个孩子
            j += 1
        if data[j] > data[new_par_node]:  # 如果子节点值大于父节点,就互相交换
            data[new_par_node], data[j] = data[j], data[new_par_node]
            new_par_node = j  # 将当前节点,作为父节点,查找他的子树
            j = j * 2 + 1

        else:
            # 因为调整是从上到下,所以下面的所有子树肯定是排序好了的,
            # 如果调整的父节点依然比下面最大的子节点大,就直接打断循环,堆已经调整好了的
            break


# 索引计算: 0 -->1 --->....
#    父节点 i   左子节点:偶数:2i +1  右子节点:基数:2i +2  注意:当用长度表示最后一个叶子节点时 记得 -1

# 从第一个非叶子节点(即最后一个父节点)开始,即 list_.length//2 -1(len(list_)//2 - 1)
# 开始循环到 root 索引为:0 的第一个根节点, 将所有的根-叶子 调整好,成为一个 大顶堆
def heap_sort(lst):
    """
    根据列表长度,找到最后一个非叶子节点,开始循化到 root 根节点,制作 大顶堆
    :param lst: 将列表传入
    :return:
    """
    length = len(lst)
    last = length - 1  # 最后一个元素的 索引
    last_par_node = length // 2 - 1

    while last_par_node >= 0:
        adjust_heap(lst, last_par_node, length - 1)
        last_par_node -= 1  # 每调整好一个节点,从后往前移动一个节点
    while last > 0:
        # swap(lst, 0, last)
        lst[0], lst[last] = lst[last], lst[0]
        # 调整堆少让 adjust 处理最后已经排好序的数,就不处理了
        adjust_heap(lst, 0, last - 1)
        last -= 1

    return lst  # 将列表返回


if __name__ == '__main__':
    data = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print(data)
    heap_sort(data)
    print(data)
20.堆排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/4/10
@Author: Zhang Yafei
"""

def cycle_sort(array):
    """
    圈排序(不稳定排序)
    默认升序
    计数排序是另开辟空间存放排序结果,圈排序是不开辟空间,直接进行组内交换
    """
    ans = 0  # 记录发生交换的次数,不用在意,无关紧要,可删

    # 外层循环处理列表中的每一个圈,一次遍历出现一个圈
    for cycleStart in range(0, len(array) - 1):
        item = array[cycleStart]  # 取出待排序的元素,item一直记录一圈的元素

        # 找到item应该放的位置,跟计数排序相似,统计列表中有多少个元素比item小
        pos = cycleStart  # 记录位置
        for i in range(cycleStart + 1, len(array)):
            if array[i] < item:
                pos += 1

        # item也存在圈但这个圈只有它自己,表示item已经归位,不用改变
        if pos == cycleStart:
            continue  # 跳过本次循环继续下次循环

        # 这四行代码是处理列表中相同的元素
        while item == array[pos]:  # 如果相同,就直接放在pos的后一个位置
            pos += 1
        array[pos], item = item, array[pos]  # item最终归位,并把item赋新值
        ans += 1

        # 处理新的item
        while pos != cycleStart:  # pos=cycleStart就代表又回到了起点,表示已有元素归到
            # cycleStart位置上,一圈处理完毕

            # 以下代码跟上面思路一样
            pos = cycleStart
            for i in range(cycleStart + 1, len(array)):
                if array[i] < item:
                    pos += 1

            while item == array[pos]:
                pos += 1
            array[pos], item = item, array[pos]
            ans += 1


if __name__ == '__main__':
    data = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print(data)
    cycle_sort(data)
    print(data)
21.圈排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/4/10
@Author: Zhang Yafei
"""


def comb_sort(the_list):
    the_len = len(the_list)
    if the_len <2:#0和1
        print("无需排序")
        return the_list
    else:
        i = int(the_len/1.3)
        while i >= 1:
            for j in range(the_len):
                if i + j >= the_len:
                    i = int(i/1.3)
                    break
                else:
                    if the_list[j] >= the_list[j+i]:
                        the_list[j], the_list[j+i] = the_list[j+i], the_list[j]


if __name__ == '__main__':
    data = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print(data)
    comb_sort(data)
    print(data)
22.梳排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/4/10
@Author: Zhang Yafei
"""
import math


def radix_sort(the_list, radix=10):
    """
    基数排序
    :param the_list:
    :param radix:
    :return:
    """
    i = int(math.ceil(math.log(max(the_list), radix)))
    bucket = [[] for i in range(radix)]
    for i in range(1, i + 1):  # i次循环
        for val in the_list:
            bucket[int(val % (radix ** i) / (radix ** (i - 1)))].append(val)  # 析取整数第K位数字 (从低到高)
        print(f'{i}: {bucket}')
        del the_list[:]  # 把列表清空但列表还在,效果同the_list=[]一样
        for each in bucket:
            the_list.extend(each)  # 桶合并
        bucket = [[] for _ in range(radix)]


if __name__ == '__main__':
    data = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('排序前:', data)
    radix_sort(data, 2)
    print('排序后:', data)
23.基数排序
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/4/10
@Author: Zhang Yafei
"""
import random


def monkey_sort_test(num_list):
    """
    猴子排序,思想是:每次都随机打乱数组,直到有序为止
    """
    while True:
        i = 1
        while i < len(data):
            # 判断是否有序
            if data[i-1] < data[i]:
                i += 1
            else:
                break
        if i == len(data):
            break
        random.shuffle(num_list)


if __name__ == '__main__':
    data = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('排序前:', data)
    monkey_sort_test(data)
    print('排序后:', data)
24.猴子排序

 

扩展

# -*- coding: utf-8 -*-

"""
@Datetime: 2019/1/8
@Author: Zhang Yafei
"""
import redis


class FifoQueue(object):
    def __init__(self):
        """
        先进先出队列:利用redis中的列表,双端队列改为先进先出队列
        """
        self.server = redis.Redis(host='127.0.0.1', port=6379)

    def push(self, request):
        """Push a request"""
        self.server.lpush('USERS', request)

    def pop(self, timeout=0):
        """Pop a request"""
        data = self.server.rpop('USERS')
        return data


if __name__ == '__main__':
    q = FifoQueue()
    q.push(11)
    q.push(22)
    q.push(33)

    print(q.pop())
    print(q.pop())
    print(q.pop())
1.基于redis实现先进先出队列
# -*- coding: utf-8 -*-

"""
@Datetime: 2019/1/8
@Author: Zhang Yafei
"""
import redis


class LifoQueue(object):
    """Per-spider LIFO queue."""
    def __init__(self):
        self.server = redis.Redis(host='127.0.0.1', port=6379)

    def push(self, request):
        """Push a request"""
        self.server.lpush("USERS", request)

    def pop(self, timeout=0):
        """Pop a request"""
        data = self.server.lpop('USERS')
        return data


if __name__ == '__main__':
    q = LifoQueue()
    q.push(11)
    q.push(22)
    q.push(33)

    print(q.pop())
    print(q.pop())
    print(q.pop())
2.基于redis实现后进先出栈
 -*- coding: utf-8 -*-

"""
@Datetime: 2019/1/8
@Author: Zhang Yafei
"""
import redis


class PriorityQueue(object):
    """Per-spider priority queue abstraction using redis' sorted set"""
    def __init__(self):
        self.server = redis.Redis(host='127.0.0.1', port=6379)

    def push(self, request,score):
        """Push a request"""
        # data = self._encode_request(request)
        # score = -request.priority
        # We don't use zadd method as the order of arguments change depending on
        # whether the class is Redis or StrictRedis, and the option of using
        # kwargs only accepts strings, not bytes.
        self.server.execute_command('ZADD', 'xxxxxx', score, request)

    def pop(self, timeout=0):
        """
        Pop a request
        timeout not support in this queue class
        """
        # use atomic range/remove using multi/exec
        pipe = self.server.pipeline()
        pipe.multi()
        pipe.zrange('xxxxxx', 0, 0).zremrangebyrank('xxxxxx', 0, 0)
        results, count = pipe.execute()
        if results:
            return results[0]


if __name__ == '__main__':
    q = PriorityQueue()

    # q.push('alex',99)    # 广度优先:分值小的优先
    # q.push('oldboy',56)
    # q.push('eric',77)

    q.push('alex',-99)       # 深度优先:分值大的优先
    q.push('oldboy',-56)
    q.push('eric',-77)

    v1 = q.pop()
    print(v1)
    v2 = q.pop()
    print(v2)
    v3 = q.pop()
    print(v3)
3.基于redis实现优先级队列

 

posted @ 2019-01-06 22:59  DreamBoy_张亚飞  阅读(642)  评论(0编辑  收藏  举报