数据结构-5-二叉树、堆

二叉树

介绍:二叉搜索树

  1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 任意节点的左、右子树也分别为二叉查找树;
  4. 没有键值相等的节点。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# coding: utf-8

class Node(object):
def __init__(self, val):
self.left = None
self.right = None
self.val = val

class BST(object):
def __init__(self):
self.root = None

# 插入
def insert(self, val):
if self.root is None:
self.root = Node(val)
else:
self.insert_node(self.root, val)

def insert_node(self, current_node, val):
if val < current_node.val:
if current_node.left is None:
current_node.left = Node(val)
else:
self.insert_node(current_node.left, val)
else:
if current_node.right is None:
current_node.right = Node(val)
else:
self.insert_node(current_node.right, val)

# 查找
def find(self, val):
if self.root is None:
print("树为空,返回False")
return False
self.find_node(self.root, val)

def find_node(self, node, val):
if node is None:
print("未找到,返回False")
return False
if val == node.val:
print("找到了,返回True")
return True
elif val < node.val:
self.find_node(node.left, val)
else:
self.find_node(node.right, val)

# 查找某个节点的前驱
def find_left_node(self, node):
return node.left

# 查找某个节点的后继
def find_right_node(self, node):
return node.right

# 前序遍历:中 左 右
def pre_order(self):
print("=========前序遍历=========")
if self.root is None:
return
self.pre_order_node(self.root)

def pre_order_node(self, node):
if node is None:
return
print(node.val)
self.pre_order_node(node.left)
self.pre_order_node(node.right)

# 中序遍历:左 中 右
def in_order(self):
print("=========中序遍历=========")
if self.root is None:
return
self.in_order_node(self.root)

def in_order_node(self, node):
if node is None:
return
self.in_order_node(node.left)
print(node.val)
self.in_order_node(node.right)

# 后序遍历:左 右 中
def post_order(self):
print("=========后序遍历=========")
if self.root is None:
return
self.post_order_node(self.root)

def post_order_node(self, node):
if node is None:
return
self.post_order_node(node.left)
self.post_order_node(node.right)
print(node.val)

# 按层遍历
# 借助队列:将二叉树的节点加入队列,出队的同时将其非空左右孩子依次入队,出队到队列为空即完成遍历
def breadth_first(self):
print("=========按层遍历=========")
if self.root is None:
return
self.breadth_first_node(self.root)

def breadth_first_node(self, node):
queue = [node]
while node and len(queue) != 0:
print(queue[0].val)
if queue[0].left is not None:
queue.append(queue[0].left)
if queue[0].right is not None:
queue.append(queue[0].right)
queue.pop(0)


bst = BST()
bst.find(3)
bst.insert(7)
bst.insert(1)
bst.find(4)
bst.insert(5)
bst.pre_order()
bst.in_order()
bst.post_order()
bst.breadth_first()

介绍:

支持的基本操作

操作 描述 时间复杂度
build 创建一个空堆 {\displaystyle O(n)}O(n)
insert 向堆中插入一个新元素 {\displaystyle O(\log n)}O(\log n)
update 将新元素提升使其匹配堆的性质
get 获取当前堆顶元素的值 {\displaystyle O(1)}O(1)
delete 删除堆顶元素 {\displaystyle O(\log n)}O(\log n)
heapify 使删除堆顶元素的堆再次成为堆

小顶堆及排序

使用list模拟

  1. 根节点位置:根节点的数据总是在数组的位置[0]
  2. 节点的父节点位置:假设一个非根节点的数据在数组中的位置[i],那么它的父节点总是在位置[(i-1)/2]
  3. 节点的孩子节点位置:假设一个节点的数据在数组中的位置为[i],那么它的孩子(如果有)总是在下面的这两个位置:左孩子在[2i+1],右孩子在[2i+2]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# coding:utf-8
# 堆与最小堆排序:从小到大排序。节点编号从1开始。

# 向下调整,每次跟孩子中较小的那个值交换
def siftdown(i): #i为要调整的节点的编号
while i*2<=n: #要调整的节点至少有左孩子
temp=i*2 if h[i*2]<h[i] else i #temp标记值最小的节点编号
if i*2+1<=n and h[i*2+1]<h[temp]:
temp=i*2+1
if temp!=i:
h[i],h[temp]=h[temp],h[i]
i=temp
else:
break #已经是堆,不需要调整


# 向上调整,每次跟父节点比较
def siftup(i):
while i/2>0:
if h[i]<h[i/2]:
h[i],h[i/2]=h[i/2],h[i]
i=i/2
else:
break

# 创建堆:调整二叉树中的节点,从n/2节点开始
def creat():
for i in range(1,n/2+1)[::-1]:
siftdown(i)

# 删除顶部元素,尾部元素放到顶部调整,堆大小-1
def deletetop():
global n
temp=h[1]
h[1]=h[n]
n-=1
siftdown(1)
return temp

num=int(raw_input())
h=map(int,raw_input().split()) #放入完全二叉树
h.insert(0,0) #为了方便计算,输入值从1开始编号

n=num

creat() #创建堆
print h

# 排序,每次删除顶部(最小),将尾部的元素放到顶部,向下调整
for i in range(num):
print deletetop(),

'''
input:
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92
output:
[0, 1, 2, 17, 5, 19, 28, 46, 12, 7, 22, 25, 99, 36, 92]
1 2 5 7 12 17 19 22 25 28 36 46 92 99
每次删除最小,插入一个数再删除最小:删除堆顶,将元素插入堆顶向下调整。
每次增加一个元素:插入末尾,向上调整。
建立堆:1.每次siftup,O(NlogN);2.放入一个完全二叉树再调整,O(N)
堆排序:O(NlogN),和快排一样
'''

大顶堆及排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# coding:utf-8
# 最大堆排序:从小到大排序。节点编号从1开始。

# 向下调整,每次跟孩子中较小的那个值交换
def siftdown(i): #i为要调整的节点的编号
while i*2<=n: #要调整的节点至少有左孩子
temp=i*2 if h[i*2]>h[i] else i #temp标记值最小的节点编号
if i*2+1<=n and h[i*2+1]>h[temp]:
temp=i*2+1
if temp!=i:
h[i],h[temp]=h[temp],h[i]
i=temp
else:
break #已经是堆,不需要调整

# 创建堆:调整二叉树中的节点,从n/2节点开始
def creat():
for i in range(1,n/2+1)[::-1]:
siftdown(i)

# 每次将最大的(堆顶)调整到最后,堆大小-1
def heapsort():
global n
for i in range(num-1):
h[1],h[n]=h[n],h[1]
n-=1
siftdown(1)

num=int(raw_input())
h=map(int,raw_input().split()) #放入完全二叉树
h.insert(0,0) #为了方便计算,输入值从1开始编号

n=num

creat() #创建堆
print h

heapsort()
print h[1:]

'''
input:
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92
output:
[0, 99, 25, 92, 12, 22, 28, 46, 7, 2, 19, 5, 17, 1, 36]
[1, 2, 5, 7, 12, 17, 19, 22, 25, 28, 36, 46, 92, 99]
'''

优先级队列

优先级队列和通常的栈和队列一样,只不过里面的每一个元素都有一个”优先级”,在处理的时候,首先处理优先级最高的。如果两个元素具有相同的优先级,则按照他们插入到队列中的先后顺序处理。

优先级队列可以通过链表,数组,堆或者其他数据结构实现。

· 如果使用无序数组,那么每一次插入的时候,直接在数组末尾插入即可,时间复杂度为O(1),但是如果要获取最大值,或者最小值返回的话,则需要进行查找,这时时间复杂度为O(n)。

· 如果使用有序数组,那么每一次插入的时候,通过插入排序将元素放到正确的位置,时间复杂度为O(n),但是如果要获取最大值的话,由于元阿苏已经有序,直接返回数组末尾的 元素即可,所以时间复杂度为O(1).

所以采用普通的数组或者链表实现,无法使得插入和排序都达到比较好的时间复杂度。所以我们需要采用新的数据结构来实现。下面就开始介绍如何采用二叉堆(binary heap)来实现优先级队列

参考:浅谈算法和数据结构: 五 优先级队列与堆排序

利用优先级队列合并 K 个有序数组

求一组动态数据集合的最大 Top K

对应的 LeetCode 练习题

Invert Binary Tree(翻转二叉树)

英文版:https://leetcode.com/problems/invert-binary-tree/

中文版:https://leetcode-cn.com/problems/invert-binary-tree/

Maximum Depth of Binary Tree(二叉树的最大深度)

英文版:https://leetcode.com/problems/maximum-depth-of-binary-tree/

中文版:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/

Validate Binary Search Tree(验证二叉查找树)

英文版:https://leetcode.com/problems/validate-binary-search-tree/

中文版:https://leetcode-cn.com/problems/validate-binary-search-tree/

Path Sum(路径总和)

英文版:https://leetcode.com/problems/path-sum/

中文版:https://leetcode-cn.com/problems/path-sum/