站在巨人的肩膀上,风景这边独好;
亲自爬上巨人的肩膀,才知风景为什么这么美。
最近专项复习一下数据结构的知识,准备把剑指offer上的题按数据结构刷一遍。抽空刷了几道二叉树的编程题,二叉树的核心知识和基础基本上每道题都需要。网上查看了一些代码,受用不少,但是分散在各个链接中。作为做二叉树题的核心,我把我的受用收集整理到这个文章中。
1. 二叉树的定义
当初学数据结构的时候,最怕碰到的就是二叉树了。有自己的结构体,还有好多乱七八糟的二叉树变种,什么一般的二叉树,平衡二叉树,满二叉树,完全二叉树,红黑树,2-3树… 望而生畏。慢慢做下来发现,万变不离其宗。就像面粉袋上封口的细绳,找准线头,轻轻一拽~~~
什么是二叉树?
百度百科说:二叉树是每个节点最多有两个子树的树结构。
图片说:
代码说:
class TreeNode:
def __init__(self, x, leftNode, rightNode):
self.left = leftNode
self.right = rightNode
self.val = x
不管别的有的没的,这就是最常见最常用的二叉树。其他的二叉树的变种等这种普通的二叉树玩腻了再说。
2. 二叉树的获取
第一种方式,手动生成二叉树:
class TreeNode:
def __init__(self, x, leftNum, rightNum):
self.left = leftNum
self.right = rightNum
self.val = x
class Solution:
def generateNewTree(self):
x = TreeNode("X",None, None)
y = TreeNode("Y",None, None)
d = TreeNode("D", x, y)
e = TreeNode("E", None, None)
f = TreeNode("F", None, None)
c = TreeNode("C", e, f)
b = TreeNode("B", d, None)
a = TreeNode("A", b, c)
return a
第二种,给定某两种遍历结果,获取原二叉树(的根节点):
class TreeNode:
def __init__(self, x, leftNode, rightNode):
self.left = leftNode
self.right = rightNode
self.val = x
class Solution:
# 给定前序遍历和中序遍历,获得二叉树
def getBSTwithPreTin(self, pre, tin):
if len(pre)==0 | len(tin)==0:
return None
root = TreeNode(pre[0],None, None)
for order,item in enumerate(tin):
if root .val == item:
root.left = self.getBSTwithPreTin(pre[1:order+1], tin[:order])
root.right = self.getBSTwithPreTin(pre[order+1:], tin[order+1:])
return root
3. 二叉树的三种遍历方式
基于该博客的Python版实现:
class TreeNode:
def __init__(self, x, leftNode, rightNode):
self.left = leftNode
self.right = rightNode
self.val = x
class Solution:
def __init__(self):
self.array = []
# 前序遍历
def preOrder(self, root):
if not root:
return self.array
self.array.append(root.val)
self.preOrder(root.left)
self.preOrder(root.right)
# 中序遍历
def midOrder(self, root):
if not root:
return self.array
self.midOrder(root.left)
self.array.append(root.val)
self.midOrder(root.right)
# 后序遍历
def postOrder(self, root):
if not root:
return self.array
self.postOrder(root.left)
self.postOrder(root.right)
self.array.append(root.val)
4. 二叉树存在的价值
现在已经知道了什么是二叉树,怎么构建、获取二叉树,以及二叉树的三种遍历方式; 我为什么要知道这些?二叉树到底干什么用?它凭什么需要我花这么多时间学它?
二叉树的存在是折中链表和数组的优势和劣势的结果。
也许这样说不够直观,来给个表格,通过对比三种数据结构在三种操作上的时间复杂度来说明问题:
– | 查找 | 插入 | 删除 |
---|---|---|---|
链表 | O(N) | O(1) | O(1) |
数组 | O(1) | O(N) | O(N) |
二叉树 | O(logN) | O(logN) | O(logN) |
可以看到,链表不适合频繁查找,数组不适合频繁插入/删除;而二叉树(事实上为排序二叉树),查找成本O(logN)。插入成本O(logN)。O(logN)的时间复杂度在N越大时体现越明显。
因此二叉树的应用是在频繁查找+增删的情形下优势相较于链表和数组非常明显。具体频繁应用在存储操作中,例如由排序二叉树(由于普通排序二叉树可能会有不平衡的情况)引申出来的: 红黑树—-linux中ext3文件系统管理,avl树—-windows对进程地址空间的管理(引自知乎)。
结语
最后,给出一个较为完整的二叉树的实现,内容包括:
1. 根据前序、中序遍历序列获取二叉树;
2. 根据获取到的二叉树的根节点实现三种遍历。
class TreeNode:
def __init__(self, x, leftNode, rightNode):
self.left = leftNode
self.right = rightNode
self.val = x
class Solution:
def __init__(self):
self.array = []
# 给定前序遍历和中序遍历,获得二叉树
def getBSTwithPreTin(self, pre, tin):
if len(pre)==0 | len(tin)==0:
return None
root = TreeNode(pre[0], None, None)
for order,item in enumerate(tin):
if root .val == item:
root.left = self.getBSTwithPreTin(pre[1:order+1], tin[:order])
root.right = self.getBSTwithPreTin(pre[order+1:], tin[order+1:])
return root
# 前序遍历
def preOrder(self, root):
if not root:
return self.array
self.array.append(root.val)
self.preOrder(root.left)
self.preOrder(root.right)
# 中序遍历
def midOrder(self, root):
if not root:
return self.array
self.midOrder(root.left)
self.array.append(root.val)
self.midOrder(root.right)
# 后序遍历
def postOrder(self, root):
if not root:
return self.array
self.postOrder(root.left)
self.postOrder(root.right)
self.array.append(root.val)
if __name__ == '__main__':
solution = Solution()
preorder_seq = [1, 2, 4, 7, 3, 5, 10]
middleorder_seq = [4, 7, 2, 1, 5, 3,10]
treeRoot1 = solution.getBSTwithPreTin(preorder_seq, middleorder_seq)
# solution.preOrder(treeRoot1)
# solution.midOrder(treeRoot1)
solution.postOrder(treeRoot1)
print(solution.array)