堆栈与队列算法

1. 堆栈(Stack)

2. 队列(Queue)

  • 单向队列
  • 双向队列
  • 优先队列

 

 

1. 堆栈(Stack)

介绍

堆栈(stack)也叫“栈”,是一种后进先出(LIFO, Last In First Out)的线性表,它的特点在于只允许在堆栈的一端(top,栈顶)进行插入数据(push)弹出数据(pop)的运算。

栈没有了位置概念,保证任何时候可以访问、删除的元素都是此前最后存入的那个元素,确定了一种默认的访问顺序。

 栈与线性表的区别

  • 栈(和队列):描述的是数据的操作方式。
  • 线性表:描述的是数据的存储方式。

栈结构的实现

栈可以用顺序表实现,也可以用链表实现。

  • 顺序表来实现栈的好处是设计的算法相当简单,但是,如果栈本身的大小是变动的,而顺序表大小只能事先规划和声明好,那么顺序表规划太大了会浪费空间,规划太小了则又不够用。
  • 链表来实现栈的好处是随时可以动态改变链表长度,能有效利用内存资源,不过缺点是设计的算法较为复杂。

代码实现 1:用列表实现栈

  • Stack():创建一个新的空栈。
  • push(item):添加一个新的元素 item 到栈顶。
  • pop():弹出栈顶元素。
  • peek():返回栈顶元素。
  • is_empty():判断栈是否为空。
  • size():返回栈的元素个数。
 1 class Stack:
 2     ""
 3 
 4     def __init__(self):
 5         self.__list = []
 6 
 7     def push(self, item):
 8         "添加一个新的元素item到栈顶"
 9         self.__list.append(item)  # 时间复杂度为O(1)
10         # self.__list.insert(0, item)  # 时间复杂度为O(n)
11         
12     def pop(self):
13         "弹出栈顶元素"
14         return self.__list.pop()  # 接收列表pop方法的返回值
15         # self.__list.pop(0)  # 时间复杂度为O(n)
16         
17     def peek(self):
18         "返回栈顶元素"
19         if self.__list:
20             return self.__list[-1]
21         else:
22             return 
23         
24     def is_empty(self):
25         "判断栈是否为空"
26         return self.__list == []
27         # return not self.__head
28         
29     def size(self):
30         "返回栈的元素个数"
31         return len(self.__list)
32         
33         
34 if __name__ == "__main__":
35     s = Stack()
36     print(s.is_empty())  # True
37     s.push(1)
38     print(s.is_empty())  # False
39     s.push(2)
40     s.push(3)
41     print(s.peek())  # 3
42     s.push(4)
43     s.push("hello")
44     print(s.pop())  # hello
45     print(s.pop())  # 4
46     print(s.size())  # 3

代码实现 2:用链表实现栈

栈的操作

  • Stack():创建一个新的空栈。
  • push(item):添加一个新的元素 item 到栈顶。
  • pop():弹出栈顶元素。
  • is_empty():判断栈是否为空。
 1 class Node:
 2     "堆栈的链表节点类"
 3     
 4     def __init__(self, data):
 5         self.data = data  # 本节点存储的数据
 6         self.next = None  # 指向下一个节点
 7 
 8 class Stack:
 9     "堆栈类"
10 
11     # 初始化栈顶节点变量
12     def __init__(self):
13         self.top = None
14 
15     # 判断堆栈是否为空
16     def is_empty(self):
17         if not self.top:
18             return True
19         else:
20             return False    
21 
22     # 将指定数据压栈
23     def push(self, data):
24         new_add_node = Node(data)
25         new_add_node.next = self.top
26         self.top = new_add_node
27 
28     # 弹栈
29     def pop(self):
30         if self.is_empty():
31             print("===当前堆栈已为空===")
32             return
33         else:
34             cur = self.top.data  # 记录栈顶数据
35             self.top = self.top.next  # 将栈顶节点指向后一个节点
36             return cur
37 
38 # 主程序
39 s = Stack()
40 while 1:
41     i = int(input("压栈请输入【1】,弹栈请输入【0】,停止操作则输入【-1】:"))
42     if i == -1:
43         break
44     elif i == 1:
45         value = input("请输入要压栈的元素值:")
46         s.push(value)
47     elif i == 0:
48         print("弹栈元素为 %s" % s.pop())
49 
50 print("="*40)
51 while not s.is_empty():  # 陆续打印弹栈元素
52     print("弹栈顺序为:%s" % s.pop())
53 print("="*40)

执行结果:

压栈请输入【1】,弹栈请输入【0】,停止操作则输入【-1】:1
请输入要压栈的元素值:7
压栈请输入【1】,弹栈请输入【0】,停止操作则输入【-1】:1
请输入要压栈的元素值:8
压栈请输入【1】,弹栈请输入【0】,停止操作则输入【-1】:1
请输入要压栈的元素值:9
压栈请输入【1】,弹栈请输入【0】,停止操作则输入【-1】:1
请输入要压栈的元素值:10
压栈请输入【1】,弹栈请输入【0】,停止操作则输入【-1】:0
弹栈元素为 10
压栈请输入【1】,弹栈请输入【0】,停止操作则输入【-1】:0
弹栈元素为 9
压栈请输入【1】,弹栈请输入【0】,停止操作则输入【-1】:-1
========================================
弹栈顺序为:8
弹栈顺序为:7
========================================

 

2. 队列(Queue)

介绍

队列是一种先进先出(FIFO, First In First Out)的线性表,只允许在一端进行插入数据(enqueue),在另一端进行删除数据(dequeue)

在队列中,允许插入的一端为队尾,允许删除的一端为队头,队列不允许在中间部位进行操作。

假设队列是 q =(a1,a2,……,an),那么 a1 就是队头元素,而 an 是队尾元素。这样我们就可以删除时,总是从 a1 开始,而插入时,总是在队列最后。这也比较符合我们通常生活中的习惯,排在第一个的优先出列,最后来的当然排在队伍最后。

队列结构的实现

同栈一样,队列也可以用顺序表或者链表实现。

1)单向队列

代码实现 1:用列表实现队列

  • Queue():创建一个空的队列。
  • enqueue(item):往队列中添加一个 item 元素。
  • dequeue():从队列头部删除一个元素。
  • is_empty():判断一个队列是否为空。
  • size():返回队列的大小。
 1 class Queue:
 2     "队列"
 3     
 4     def __init__(self):
 5         self.__list = []
 6         
 7     def enqueue(self, item):
 8         "往队列中添加一个item元素"
 9         # 根据具体的使用频率,决定添加/删除元素的方式
10         self.__list.append(item)  # O(1)
11         # self.__list.insert(0, item)  # O(n)
12     
13     def dequeue(self):
14         "从队列头部删除一个元素"
15         return self.__list.pop(0)  # O(n)
16         # return self.__list.pop()  # O(1)
17     
18     def is_empty(self):
19         "判断一个队列是否为空"
20         return self.__list == []
21         # return not self.__list
22         
23     def size(self):
24         "返回队列的大小"
25         return len(self.__list)
26         
27 
28 if __name__ == "__main__":        
29     q = Queue()
30     print(q.is_empty())  # True
31     q.enqueue("hello")
32     print(q.is_empty())  # False
33     q.enqueue(1)
34     q.enqueue(2)
35     q.enqueue(3)
36     print(q.dequeue())  # hello
37     print(q.dequeue())  # 1
38     print(q.size())  # 2

代码实现 2:用链表实现队列

  • Queue():创建一个空的队列。
  • enqueue(item):往队列中添加一个 item 元素。
  • dequeue():从队列头部删除一个元素。
  • is_empty():判断一个队列是否为空。
 1 class Node:
 2     "队列的链表节点类"
 3     
 4     def __init__(self, data):
 5         self.data = data  # 本节点存储的数据
 6         self.next = None  # 指向下一个节点
 7 
 8 class Queue:
 9     "队列类"
10 
11     # 初始化队列头部和尾部节点变量
12     def __init__(self):
13         self.head = None
14         self.end = None
15 
16     # 判断队列是否为空
17     def is_empty(self):
18         if not self.head:
19             return True
20         else:
21             return False    
22 
23     # 往队列尾部添加元素
24     def enqueue(self, data):
25         new_node = Node(data)
26         # 表明添加的是第一个元素
27         if not self.end:
28             self.head = new_node
29         # 否则将新节点连接到队列末尾
30         else:
31             self.end.next = new_node  
32         # 将新的队列末尾变量指向新节点
33         self.end = new_node
34 
35     # 删除队列头部元素
36     def dequeue(self):
37         if self.is_empty():
38             print("===当前队列已为空===")
39             return
40         else:
41             cur = self.head.data  # 记录队列头部数据
42             self.head = self.head.next  # 将头部节点指向后一个节点
43             return cur
44 
45 # 主程序
46 q = Queue()
47 while 1:
48     i = int(input("添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:"))
49     if i == -1:
50         break
51     elif i == 1:
52         value = input("请输入要添加的元素值:")
53         q.enqueue(value)
54     elif i == 0:
55         print("删除元素:%s" % q.dequeue())
56 
57 print("="*40)
58 while not q.is_empty():
59     print("元素删除顺序为:%s" % q.dequeue())
60 print("="*40)

执行结果:

添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:1
请输入要添加的元素值:5
添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:1
请输入要添加的元素值:6
添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:1
请输入要添加的元素值:7
添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:1
请输入要添加的元素值:8
添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:0
删除元素:5
添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:0
删除元素:6
添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:-1
========================================
元素删除顺序为:7
元素删除顺序为:8
========================================

2)双向列表

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

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

通常在一般的应用上,双向队列可以区分为两种:

  1. 数据只能从一端加入,但可从两端取出。
  2. 数据可从两端加入,但只能从一端取出。

代码实现 1:用列表实现双端列表

  • Deque():创建一个空的双向队列。
  • add_front(item):从队头加入一个 item 元素。
  • add_rear(item):从队尾加入一个 item 元素。
  • remove_front():从队头删除一个 item 元素。
  • remove_rear():从队尾删除一个 item 元素。
  • is_empty():判断双向队列是否为空。
  • size():返回队列的大小。
 1 class Deque:
 2     "双端队列"
 3     
 4     def __init__(self):
 5         self.__list = []
 6         
 7     def add_front(self, item):
 8         "往队列头部添加一个item元素"
 9         self.__list.insert(0, item) 
10     
11     def add_rear(self, item):
12         "往队列尾部添加一个item元素"
13         self.__list.append(item)  
14     
15     def remove_front(self):
16         "从队列头部删除一个元素"
17         return self.__list.pop(0) 
18         
19     def remove_rear(self):
20         "从队列尾部删除一个元素"
21         return self.__list.pop()  
22     
23     def is_empty(self):
24         "判断一个队列是否为空"
25         return self.__list == []
26         # return not self.__list
27         
28     def size(self):
29         "返回队列的大小"
30         return len(self.__list)
31         
32 
33 if __name__ == "__main__":        
34     d = Deque()
35     print(d.is_empty())  # True
36     d.add_front(1)
37     d.add_front(2)
38     d.add_rear(3)
39     d.add_rear(4)
40     print(d.is_empty())  # False
41     print(d.size())  # 4
42     print(d.remove_front())  # 2
43     print(d.remove_rear())  # 4
44     print(d.size())  # 2

代码实现 2:用链表实现双向队列 

  • Deque():创建一个空的双向队列。
  • enqueue(item):从队头加入一个 item 元素。
  • dequeue(action):根据 action 从队头或队尾删除一个元素。
  • is_empty():判断双向队列是否为空。
 1 class Node:
 2     "队列的链表节点类"
 3     
 4     def __init__(self, data):
 5         self.data = data  # 本节点存储的数据
 6         self.next = None  # 指向下一个节点
 7 
 8 class Deque:
 9     "双向队列类"
10 
11     # 初始化队列头部和尾部节点变量
12     def __init__(self):
13         self.head = None
14         self.end = None
15 
16     # 判断队列是否为空
17     def is_empty(self):
18         if not self.head:
19             return True
20         else:
21             return False    
22 
23     # 往队列尾部添加元素
24     def enqueue(self, data):
25         new_node = Node(data)
26         # 表明添加的是第一个元素
27         if not self.end:
28             self.head = new_node
29         # 否则将新节点连接到队列末尾
30         else:
31             self.end.next = new_node  
32         # 将新的队列末尾变量指向新节点
33         self.end = new_node
34 
35     # 可从队列两端删除元素
36     def dequeue(self, action):
37         if self.is_empty():
38             print("===当前队列已为空===")
39             return
40         # 从队列头部删除元素
41         if action == 1:
42             value = self.head.data  # 记录队列头部数据
43             self.head = self.head.next  # 将头部节点指向后一个节点
44             return value
45         # 从队列尾部删除元素
46         elif action == 2:
47             value = self.end.data  # 记录队末数据
48             # 若队列只有一个元素
49             if not self.head.next:
50                 self.head = None
51                 self.end = None
52             # 否则遍历找到倒数第二个节点
53             else:
54                 cur = self.head
55                 while cur.next.next:
56                     cur = cur.next
57                 self.end = cur
58                 self.end.next = None
59             return value
60         
61 # 主程序
62 q = Deque()
63 print("用链表来实现双向队列")
64 print("="*40)
65 while 1:
66     i = int(input("添加元素请输入【1】,删除元素请输入【0】,停止操作则输入【-1】:"))
67     if i == -1:
68         break
69     elif i == 1:
70         value = input("请输入要添加的元素值:")
71         q.enqueue(value)
72     elif i == 0:
73         print("从队头删除元素:%s" % q.dequeue(1))
74         print("从队尾删除元素:%s" % q.dequeue(2))

3)优先队列

优先队列(Priority Queue)是一种不必遵守队列特性(先进先出)的有序线性表,其中的每一个元素都被赋予了一个优先级(Priority),加入元素时可任意加入,但有最高优先级者(Highest Priority Out First,HPOF)则最先输出

像一般医院中的急诊室,当然以最严重的病患优先诊治,跟进入医院挂号的顺序无关。又例如在计算机中 CPU 的作业调度,优先级调度(Priority Scheduling,PS)就是一种按进程优先级”调度算法“(Scheduling Algorithm)进行的调度,这种调度就会使用到优先队列,好比优先级高的用户,就比一般用户拥有较高的权力。

注意,当各个元素按输入先后次序为优先级时,就是一般的队列;若是以输入先后次序的倒序作为优先级,此优先队列即为一个堆栈。

 

posted @ 2020-03-30 15:06  Juno3550  阅读(295)  评论(0编辑  收藏  举报