Python全栈(二)数据结构和算法之6.队列的实现和冒泡排序的实现

一、队列和队列的实现

1.定义

特点:先进者先出。
操作:
入队enqueue:放一个数据到队列尾部;
出队dequeue:从队列头部取一个元素。
队列也是一种操作受限制的线性表数据结构。
包括一些额外特性的队列,比如循环队列、阻塞队列、并发队列等,在很多偏底层系统、框架、中间件的开发中起着关键作用。

2.队列的实现

队列的实现也是用顺序表list来实现,相关操作与list的操作相关,实现如下,

class Queue(object):
    def __init__(self):
        '''初始化'''
        self.__items = []

    def enqueue(self,item):
        '''
        :param item:向队列中添加一个item元素
        :return: None
        '''
        self.__items.append(item)

    def dequeue(self):
        '''
        :param item
        :return:第一个元素
        '''
        self.__items.pop(0)

    def is_empty(self):
        '''
        判断一个队列是否为空
        :return: True or False
        '''
        return self.__items == []

    def size(self):
        '''
        :return:队列中的元素个数
        '''
        return len(self.__items)

    def travel(self):
        '''
        遍历
        :return:
        '''
        for item in self.__items:
            print(item)

q = Queue()
print(q.is_empty())
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.travel()
q.dequeue()
q.dequeue()
q.travel()
print(q.size())

并进行测试,打印结果为,

1
2
3
3
1

3.双端队列及其实现

全名double-ended queue,一种具有队列和栈的性质的数据结构。
结构如图,
双端队列示意
元素可以从两端弹出,其限定插入和删除操作在队列的两端进行,可以在队列任意一端入队和出队。

实现与队列类似,

class Deque(object):
    '''双端队列'''
    def __init__(self):
        self.__items = []

    def is_empty(self):
        '''判断双端队列是否为空'''
        return self.__items == []

    def size(self):
        '''
        返回队列中的元素个数
        :return:
        '''
        return len(self.__items)

    def add_front(self,item):
        '''从队头添加一个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 travel(self):
        '''
        遍历
        :return:
        '''
        for item in self.__items:
            print(item)

if __name__ == '__main__':
    d = Deque()
    print(d.is_empty())
    d.add_front(1)
    d.add_rear(2)
    d.travel()
    d.remove_front()
    print(d.remove_rear())
    print(d.size())

进行测试,打印结果为,

True
1
2
2
0

4.阻塞队列:

input()函数调用时等待输入,即为阻塞。
阻塞队列在队列基础上增加了阻塞操作:
在队列为空的的时候,从队头取数据会被阻塞,因为此时还没有数据可取,直到队列中有了数据才能返回;
如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。
应用:CPU资源分配:
CPU资源是有限的,任务的处理速度与线程个数并不是线性正相关。相反,过多的线程反而会导致CPU频繁切换,处理性能下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置的。
如果采用链表:无限排队的无界队列 响应时间,比较敏感不合适;
如果采用顺序表:有界的队列 如果超过 直接拒绝 响应时间 敏感比较合适。

二、排序算法的分析

1.排序算法简介

最经典的排序算法:
冒泡排序
插入排序
选择排序
归并排序
快速排序
希尔排序

分析排序算法的角度:
※算法的执行效率:
▲时间复杂度;
▲比较次数和交换(移动)次数。
※排序算法的内存消耗:
原地排序算法:特指时间复杂度为O(1)的排序算法。
※排序算法的稳定性:
稳定性是指,如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。

2.递归回顾

递归是一种算法,很对数据结构和算法的实现都要用到算法,如DFS深度优先搜索、前中后序二叉树遍历等。
递归的三个条件:

  1. 一个问题的解可以分成几个子问题的解;
  2. 这个问题与分解之后的子问题,除了数据规模,求解思路完全一样;
  3. 存在递归终止条件。
    在Python中,递归最大深度为1000,超过1000会抛出异常。
    简单举例:
def f(n):
    if n == 1:
        return 1
    else:
        return f(n - 1) + 1

print(f(5))

打印5
再如

def factorial(n):
    '''阶乘'''
    if n == 1:
        return 1
    else:
        return factorial(n - 1) * n

print(factorial(20))

打印2432902008176640000
用递归的方式输出l=[‘jack’,(‘tom’,23),27,‘rose’,(14,55,67)]列表中内的每一个元素输出:

def list_p(l):
    if isinstance(l,(str,int)):
        print(l)
    else:
        for item in l:
            list_p(item)

l=['jack',('tom',23),27,'rose',(14,55,67)]
list_p(l)

会打印

jack
tom
23
27
rose
14
55
67

3.冒泡排序:

冒泡排序(Bubble Sort),重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
实现思路:

  • 比较相邻的元素,如果第一个比第二个大(升序),就交换他们两个;
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数;
  • 针对所有的元素重复以上的步骤,除了最后一个;
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
    用直观的图形分析如下,
    冒泡原理
    进行一次循环后,会对除了最后一个元素的其他元素进行第二次循环,再比较,直到完全有序。
    用更形象的动图分析,
    冒泡排序动态分析

实现:

方法一:

def bubble_sort_1(alist):
    '''用循环控制,冒泡排序'''
    for o in range(len(alist)-1,0,-1):
        for i in range(o):
            if alist[i] > alist[i+1]:
                alist[i],alist[i+1]=alist[i+1],alist[i]

if __name__ == '__main__':
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    bubble_sort_1(li)
    print(li)

打印[17, 20, 26, 31, 44, 54, 55, 77, 93]
方法二:

def bubble_sort_2(alist):
    n = len(alist)
    #外层循环控制循环的次数
    for o in range(len(alist)-1):
        #内层循环为当前循环,i为所需比较的次数
        for i in range(n - 1 - o):
            if alist[i] > alist[i+1]:
                alist[i],alist[i+1]=alist[i+1],alist[i]

if __name__ == '__main__':
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    bubble_sort_2(li)
    print(li)

结果与方法一相同。
方法三:用递归实现

def bubble_sort_3(alist):
    '''递归实现'''
    n = len(alist)
    flag = True
    for i in range(n - 1):
        if alist[i] > alist[i + 1]:
            flag = False
            break
        else:
            continue
    if flag:
        return alist
    else:
        for i in range(n - 1):
            if alist[i] > alist[i+1]:
                alist[i],alist[i+1]=alist[i+1],alist[i]
    return bubble_sort_3(alist[:-1]) + [alist[-1]]

if __name__ == '__main__':
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    new_list = bubble_sort_3(li)
    print(new_list)

打印结果为[17, 20, 26, 31, 44, 54, 55, 77, 93],与前两种方法相同,但是前两种方法是直接对原列表进行操作,使列表成为有序,原列表被改变,不带返回值,用递归实现的方法使被操作后的列表返回,返回有序的列表,是带有返回值的。

冒泡排序的分析:

  • 冒泡排序是稳定的
    在冒泡排序中,只有交换才可以改变两个元素的前后顺序。为了保证冒泡排序算法的稳定性,当有相邻的两个元素大小相等的时候,我们不做交换,相同大小的数据在排序前后不会改变顺序,所以冒泡排序是稳定的排序算法。
  • 时间复杂度
    最好情况下,要排序的数据已经是有序的了,只需要进行一次冒泡操作进行比较,就结束了,所以最好情况时间复杂度是O(n);而最坏的情况是,要排序的数据刚好是倒序排列的,我们需要进行n次冒泡操作,所以最坏情况时间复杂度为O(n2)。
    公众号二维码
    大家也可以关注我的公众号:Python极客社区,在我的公众号里,经常会分享很多Python的文章,而且也分享了很多工具、学习资源等。另外回复“电子书”还可以获取十本我精心收集的Python电子书
发布了51 篇原创文章 · 获赞 184 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/CUFEECR/article/details/103113864