from queue import SQueue as queue
from list_stack import Stack as stack
import time
class BinTNode():
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def count_BinTNode(t):
if t is None:
return 0
else:
total = 1 + count_BinTNode(t.left) + count_BinTNode(t.right)
return total
total = 0
def count_BinTNode2(t):
if t is None: # 边界条件
return
else:
global total
total += 1
count_BinTNode2(t.left)
count_BinTNode2(t.right)
def sum_BinTNode(t):
if t is None:
return 0
else:
return t.data + sum_BinTNode(t.letf) + sum_BinTNode(t.right)
def pre_order(t, op):
if t is None:
return
op(t.data, end=' ')
pre_order(t.left, op)
pre_order(t.right, op)
return
def mid_order(t, op):
if t is None:
return
mid_order(t.left, op) # 处理左子树
op(t.data, end=' ') # 处理自己
mid_order(t.right, op) # 处理右子树
return
def last_order(t, op):
if t is None:
return
last_order(t.left, op)
last_order(t.right, op)
op(t.data, end=' ')
return
def level_order(t, op):
if t is None:
return
q = queue()
q.enqueue(t)
while not q.is_empty():
temp = q.dequeue()
op(temp.data, end=' ')
if temp.left is not None:
q.enqueue(temp.left)
if temp.right is not None:
q.enqueue(temp.right)
return
def preorder_elements(t):
s= stack()
s.push(t)
while not s.is_empty():
t = s.pop()
while t is not None:
yield t.data
s.push(t.right)
t = t.left
def preorder_nonrec(t, op):
s = stack()
s.push(t) # 开头
while not s.is_empty(): # 非空栈
t = s.pop()
while t is not None:
op(t.data, end=' ')
s.push(t.right) # 先进后出
t = t.left
def midorder_nonrec(t, op):
"""见左捣底"""
"""回退访右"""
s = stack()
while t is not None or not s.is_empty():
while t is not None:
s.push(t)
t = t.left
node = s.pop()
op(node.data, end=' ')
if node.right is not None:
t = node.right
else:
t = None
def postorder_nonrec(t, op):
"""见左捣底(末端可右)"""
"""先右后根"""
s = stack()
while t is not None or not s.is_empty(): # 第一个条件只在第一次循环必要
while t is not None: # 从本节点到偏左子孙节点入栈(如果某节点左节点为空,只能入栈右节点)
s.push(t) # 0-1-3
t = t.left if t.left is not None else t.right
t = s.pop() # 处理栈顶节点
op(t.data, end=' ')
if not s.is_empty() and s.top().left == t: # 不判空最后一步top会出错
t = s.top().right # 已经处理过左节点,转到右节点
else:
t = None # 退出循环,下次弹出父节点
root = BinTNode(0, BinTNode(1, BinTNode(3, BinTNode(7), BinTNode(8)),
BinTNode(4, BinTNode(9), None)), BinTNode(2, BinTNode(5),
BinTNode(6)))
print('深度优先:', end='')
level_order(root, print)
print('')
print('先序遍历:', end='')
pre_order(root, print)
print('')
print('先序遍历:', end='')
preorder_nonrec(root, print)
print('')
print('中序遍历:', end='')
mid_order(root, print)
print('')
print('中序遍历:', end='')
midorder_nonrec(root, print)
print('')
print('后序遍历:', end='')
last_order(root, print)
print('')
print('后序遍历:', end='')
postorder_nonrec(root, print)
print('')
输出:
深度优先:0 1 2 3 4 5 6 7 8 9
先序遍历:0 1 3 7 8 4 9 2 5 6
先序遍历:0 1 3 7 8 4 9 2 5 6
中序遍历:7 3 8 1 9 4 0 5 2 6
中序遍历:7 3 8 1 9 4 0 5 2 6
后序遍历:7 8 3 9 4 1 5 6 2 0
后序遍历:7 8 3 9 4 1 5 6 2 0
6.3 优先队列
6.3.1 概念
问题:完全二叉树:为什么要求在左呢?:
得益于完全二叉树的结构,使得父子节点具有确定的关系,便于和线性表一一对应。
6.3.2 基于线性表的实现
方案一:插入时就排序,这样入队O(N),出队O(1);
方案二:出队时排序,入队O(1),出队O(N)。当然方案二可以配合栈将最优先的元素放到栈里,这样每次从栈中访问。
基于线性表实现的考虑:
1、首先确定要插入的位置,进行下标合法性检查,然后调用insert插入;
2、初始化参数拷贝,避免共享;
3、位置确定过程:初始化为由大到小,从最后一个元素开始逐个检查。
6.3.3 树形结构和堆
堆与性质:
结构上,堆由完全二叉树实现,但是数据的存储要满足特殊的堆序:根结点的优先级必须不小于子结点的优先级。所以优先级最高的就是根结点。另外,不同路径上的元素不交叉考虑。这样就构造出了大顶堆,与之对应的是小顶堆。
堆的四个性质::
1、在堆的最后面增加元素,得到的仍然是完全二叉树,但不一定满足堆序。
2、弹出顶端元素,形成两个子堆
3、在两个子堆上增加根结点构成完全二叉树,但不一定满足堆序。
4、去掉最后一个元素,仍然是堆。
堆与优先队列
在堆中可以用O(1)的时间访问最优先元素,但是要解决两个问题:
1、插入新元素后堆的重构
2、弹出元素后,子堆的融合
6.3.4 优先队列的堆实现
插入元素与上筛(i-1//2):
先将元素插入到堆最后一个位置,然后依次和父结点比较,直到优先级较小。
弹出元素与下筛:
弹出元素后,将最后一个元素移到根结点,然后逐个和子结点比较,直到优先级较高。
一行两种操作的时间复杂度不会超过log n。
6.3.5 堆排序
# 基于堆实现优先队列
class PrioQueueError(ValueError):
pass
class PrioQue():
def __init__(self, elist):
"""设置小优先级高"""
self._elements = list(elist)
self._len = len(self._elements)
if elist:
self.buildheap()
def buildheap(self):
end = self._len
for i in range(end//2-1, -1, -1): # 中间的-1取不到,是左闭右开区间
self.sift_down(self._elements[i], i, end)
print('初始队列:', self._elements)
def is_empty(self):
return self._len == 0
def enqueue(self, data):
# 为第一次交换做准备
self._elements.append(None)
self._len += 1
self.sift_up(data, self._len-1)
return
def sift_up(self, data, last_index):
"""要插入的元素和队列最后一个位置的索引"""
"""位置合法 + 数值合适"""
base_pos, parent_pos = last_index, (last_index - 1) // 2
while parent_pos >= 0 and data < self._elements[parent_pos]:
self._elements[base_pos] = self._elements[parent_pos]
base_pos, parent_pos = parent_pos, (parent_pos - 1) // 2
self._elements[base_pos] = data
return
def dequeue(self):
if self.is_empty():
raise PrioQueueError
first = self._elements[0]
last = self._elements.pop()
self._len -= 1
if self._len > 0:
self.sift_down(last, 0, self._len)
return first
def sift_down(self, data, begin, end):
"""begin可以视作根节点,从该处三角下筛"""
"""下筛的时候就不考虑原根节点了"""
base_pos, target = begin, 2 * begin + 1
while target < end: # 确定最小的→交换
if target + 1 < end and self._elements[target] > self._elements[1 + target]:
target += 1
if data < self._elements[target]:
break # 是三值中最小的,位置对
else:
self._elements[base_pos] = self._elements[target]
base_pos, target = target, target * 2 + 1
self._elements[base_pos] = data
return
def order(self):
temp = [0]*self._len
for i in range(self._len):
temp[i] = self.dequeue()
return temp
def content(self):
print(self._elements)
pq = PrioQue([5, 6, 9, 2, 4, 7])
pq.enqueue(10)
print("队列加10: ", end='')
pq.content()
pq.dequeue()
print("dequeue后: ", end='')
pq.content()
print("排序后: ", end='')
print(pq.order())