zoukankan      html  css  js  c++  java
  • 算法漫游指北(第六篇)双端队列、排序算法分类、排序算法的稳定性、排序算法复杂度

    一、双端队列

    双端队列

    双端队列(deque,全名double-ended queue),是一种具有队列和栈的性质的数据结构。

    双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。双端队列可以在队列任意一端入队和出队。

     

    双端队列(Deque),是一种类似于队列的元素的有序集合。它拥有两端,队首和队尾,并且元素保持在当前的位置。双端队列的一个不同点就是,添加和删除元素的位置不受限制。新元素可以在队首或者队尾添加。同样地,双端队列中的元素可以从两端弹出。在某种意义上,这种混合的线性结构同时具有栈和队列的性质。

     

     

    方法

    • Deque() 创建一个空的双端队列

    • add_front(item) 从队头加入一个item元素

    • add_rear(item) 从队尾加入一个item元素

    • remove_front() 从队头删除一个item元素

    • remove_rear() 从队尾删除一个item元素

    • is_empty() 判断双端队列是否为空

    • size() 返回队列的大小

     

    Python实现双端队列

    在Python中,有两种方式可以实现上述的双端队列ADT:使用内建类型list、使用标准库collections.deque(其实collections.deque就是Python中双端队列的标准实现)。

    两种方式的不同主要体现在性能上(具体参考 collections.deque | TimeComplexity):

    用列表实现双端队列

    class Deque(object):
        """双端队列"""
        def __init__(self):
            self.items = []
     
        def is_empty(self):
            """判断队列是否为空"""
            return self.items == []
     
        def add_front(self, item):
            """在队头添加元素"""
            self.items.insert(0,item)
     
        def add_rear(self, item):
            """在队尾添加元素"""
            self.items.append(item)
     
        def remove_front(self):
            """从队头删除元素"""
            return self.items.pop(0)
     
        def remove_rear(self):
            """从队尾删除元素"""
            return self.items.pop()
     
        def size(self):
            """返回队列大小"""
            return len(self.items)
     
    if __name__ == "__main__":
        deque = Deque()
        deque.add_front(1)
        deque.add_front(2)
        deque.add_rear(3)
        deque.add_rear(4)
        print('队列长度',deque.size())
        print(deque.remove_front())
        print(deque.remove_front())
        print(deque.remove_rear())
        print(deque.remove_rear())
        print('队列长度',deque.size())
    ​
    

      

    分析:list作为容器,在双端队列的任何一端执行添加(append)和删除(pop)操作,时间复杂度为O(1),

    但是当list遇到pop(0)和insert(0, v)这样既改变了列表的长度又改变其元素位置的操作时,其时间复杂度变为O(n)了。

     

    使用标准库collections.deque

    Python的deque模块,它是collections库的一部分。deque实现了双端队列,意味着你可以从队列的两端加入和删除元素。

    append(x):把元素x添加到队列的右端
    appendleft(x):把元素x添加到队列的左端
    pop():移除并返回deque右端的元素,如果没有元素抛IndexError
    popleft():移除并返回deque左端的元素,如果没有元素抛IndexError
    ​
    ​
    index(x[, start[, stop]]):返回x元素在队列中的索引,放回第一个匹配,如果没有找到抛ValueError
    insert(i, x):在队列的i索引处,插入x元素
    remove(value):删除第一个匹配value的元素,如果没有找到抛ValueError
    ​
    ​
    clear():清空队列中所有元素
    copy():创建队列的浅拷贝
    count(x):计算队列中等于x元素的个数
    ​
    extend(iterable):在队列右端通过添加元素扩展
    extendleft(iterable):在队列左端通过添加元素扩展
    ​
    reverse():在原地反转队列中的元素
    rotate(n):把队列左端n个元素放到右端,如果为负值,右端到左端。如果n为1,等同d.appendleft(d.pop())
    maxlen:只读属性,队列中的最大元素数
    

      

    简单示例



    from collections import deque
    ​
    # 实例化一个deque对象
    d = deque()
    ​
    # 在队列尾插入元素
    d.append(1)
    d.append(2)
    d.append(3)
    d.append(4)
    print(d)  #deque([1, 2, 3, 4])
    ​
    # 在队列头插入元素
    d.appendleft(5)
    print(d)      # deque([5, 1, 2, 3, 4])
    ​
    # 长度
    print(len(d)) #5
    # 队列按索引取值
    print(d[0])   #5
    print(d[-1])  #4
    ​
    print('-'*10)
    ​
    #在右侧删除一个元素
    d.pop()
    print(d)      # deque([5, 1, 2, 3])
    ​
    # 在左侧删除一个元素
    d.popleft()
    print(d)      # deque([1, 2, 3])
    ​
    # 扩展元素,类似列表的扩展,直接通过列表进行扩展,在deque右端插入多个元素
    d.extend([4,5,6])
    print(d)    # deque([1, 2, 3, 4, 5, 6])
    ​
    # 扩展元素,在deque左端插入多个元素,注意最后插入元素的顺序与插入的列表相反
    d.extendleft([7,8,9])
    print(d)    # deque([9, 8, 7, 1, 2, 3, 4, 5, 6])
    

      


    也可以重写,实现用列表方式调用形式的统一

    # 使用标准库collections.deque
    from collections import deque
    class Deque:
    ​
      def __init__(self):
        self.items = deque()
    ​
      def addFront(self, item):
        self.items.appendleft(item)
    ​
      def addRear(self, item):
        self.items.append(item)
    ​
      def removeFront(self):
        return self.items.popleft()
    ​
      def removeRear(self):
        return self.items.pop()
    ​
      def is_empty(self):
        return self.size() == 0
    ​
      def size(self):
        return len(self.items)
        
    

      

     

     

    deque各操作时间复杂度

     

    注意:pop/popleft/append/appendleft 四种操作的时间复杂度均是 O(1)。

     

     

    二、排序

    排序算法(英语:Sorting algorithm)是一种能将一串数据依照特定顺序进行排列的一种算法。

     

    排序算法分类

    1.比较类排序 通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序

    2.非比较类排序(一般用于整型相关的数据类型) 不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

     

     

    排序算法的稳定性

    稳定性:稳定排序算法会让原本有相等键值的纪录维持相对次序。也就是如果一个排序算法是稳定的,当有两个相等键值的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前。

    稳定性的意思是对于序列中键值(Key value)相同的元素,它们在排序前后的相对关系保持不变。对于int这样的基本数据类型,稳定性基本上是没有意义的,因为它的键值就是元素本身,两个元素的键值相同他们就可以被认为是相同的。但对于复杂的数据类型,数据的键值相同,数据不一定相同,比如一个Student类,包括Name和Score两个属性,以Score为键值排序,这时候键值相同元素间的相对关系就有意义了。

     

    当相等的元素是无法分辨的,比如像是整数,稳定性并不是一个问题。然而,假设以下的数对将要以他们的第一个数字来排序。

    (4, 1)  (3, 1)  (3, 7)(5, 6)

    在这个状况下,有可能产生两种不同的结果,一个是让相等键值的纪录维持相对的次序,而另外一个则没有:

    (3, 1)  (3, 7)  (4, 1)  (5, 6)  (维持次序)
    (3, 7) (3, 1) (4, 1) (5, 6) (次序被改变)

    不稳定排序算法可能会在相等的键值中改变纪录的相对次序,但是稳定排序算法从来不会如此。不稳定排序算法可以被特别地实现为稳定。做这件事情的一个方式是人工扩充键值的比较,如此在其他方面相同键值的两个对象间之比较,(比如上面的比较中加入第二个标准:第二个键值的大小)就会被决定使用在原先数据次序中的条目,当作一个同分决赛。然而,要记住这种次序通常牵涉到额外的空间负担。

     

     

     

    排序算法复杂度

     

    初级排序(O(n^2)时间复杂度)

    1.选择排序

    每次找最小值,然后放到待排序数组的起始位置

    2.插入排序 从前到后 逐步构建有序序列;对于未排序的数列,在已排序序列中从后向前扫描,找到相应位置并插入。

    3.冒泡排序 嵌套循环,每次查看相邻元素,如果逆序,那么交换、

     

     

    高级排序(O(nlogn)时间复杂度,重点考察)

    • 快速排序 数组取标杆pivot,将小元素放pivot左边,大元素放右边,然后,依次对左边和右边的子数组继续快排;以到达这个序列有序。

    • 归并排序(分治思想) 1.把长度为n的输入序列分成两个长度为n/2的子序列 2.对这两个子序列分别采用归并排序 3.将两个排序好的子序列合并成一个最终的排序序列。

    归并与快排具有相似性,但步骤顺序相反。

    归并:先排序左右子数组,然后合并两个有序子数组 快排:先调配出左右子数组,然后对于左右子数组进行排序

     

    • 堆排序

      • 堆插入O(logN),取最大/小值O(1) 1.数组元素依次建立小顶堆 2.依次取栈顶元素,并删除

     

    其他排序(了解)

    • 计数排序 计数排序要求输入的数据必须是有确定范围的整数。将输入的数据值转化为键存储在额外开辟的数组空间中;然后依次把计数大于1的填充回原数组。

    • 桶排序 桶排序的工作原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里面,每个桶在分别排序。

    • 基数排序 基数排序是按照低为先排序,然后收集,再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序的,再按高优先级排序。

       

       

     

    参考资料

    [1]https://www.cnblogs.com/onepixel/p/7674659.html

  • 相关阅读:
    VS2012 未找到与约束ContractName Microsoft.VisualStudio.Text.ITextDocumentFactoryService
    什么是 Cookie
    Memcached的简介和使用
    visual studio 2012打开提示 未能将网站×××配置为使用 ASP.NET 4.5 和 尚未在Web服务器上注册,您需要手动将Web服务器配置为使用ASP.NET 4.5
    SQLServer中char、varchar、nchar、nvarchar的区别:
    Microsoft Visual Studio 2012旗舰版(VS2012中文版下载)官方中文版
    C#操作sql通用类 SQLHelper
    用C#写入Excel表并保存
    C# TreeView 控件的综合使用方法
    ADODB.Connection、ADODB.RecordSet
  • 原文地址:https://www.cnblogs.com/Nicholas0707/p/13040246.html
Copyright © 2011-2022 走看看