Data Structures and Algorithms Week5: Tree
Tree
树
Content
目录
- Binary Trees
二叉树 - Recursive techniques with trees
基于树的递归技术 - Traversals
遍历
What is a Tree (Graph Theory)?
什么是树(图论)
- In graph theory, a tree is an undirected graph that contains no cycles (i.e. no paths that take you back to the node on which you started).
在图论中,树是一种不包含环的无向图(即不存在一条路径能让你回到起始节点)。

What is a Tree (Computer Science)?
什么是树(计算机科学)
- In computer science we generally think of trees as being rooted trees.
在计算机科学中,我们通常讨论的是有根树。- One node is designated as the root.
其中一个节点被指定为根节点。 - The nodes adjacent to it are the roots of its subtrees
与其相邻的节点分别是其子树的根。
- One node is designated as the root.

Example

Example: XML/HTML documents

Binary trees
二叉树
- Binary trees have two sub-trees:
二叉树有两个子树:
![]() | ![]() | ![]() | ![]() |
|---|
Which is Binary tree?
哪个是二叉树?
Basic Terminology for Binary Trees
二叉树基础术语
Terminology 术语


Root: 根节点
Left-subtree: 左子树
Right-subtree: 右子树
leaves: 叶子节点
How do we represent binary trees?
如何表示二叉树呢?
- A (binary) tree is
(二叉)树可以定义为:- either empty,
要么是空树, - or consists of a node, containing some data, along with references to two sub-trees, known as the left and right sub trees.
要么由一个包含数据的节点组成,并带有指向两棵子树的引用,分别称为 left 和 right 子树。
- either empty,
⬆️
Note the recursive definition.
注意这个递归定义。
Represent Binary Trees of Integers
用整数表示二叉树
public class BinaryTree { public Member data; public BinaryTree left; //null if there is no left subtree public BinaryTree right; //null if there is no right subtree public BinaryTree(Member val) { data = val; left = null; right = null; }}BinaryTree t;// A tree can be represented by a reference to its root.// This reference is null if the tree is empty.Recursive Tree Algorithms
递归树算法
Since trees are defined recursively, it is often simplest to process them using recursive algorithms. Here is an example:
由于树本身是递归定义的,通常使用递归算法处理它最简单。示例如下:
/** Count the number of nodes in a binary tree **/public static int count(BinaryTree t) { if (t == null) { return 0; } else { return 1 + count(t.left) + count(t.right); }}Traversals
遍历
Traverse 遍历
- A traversal is a systematic way of going through all the data in a data structure.
遍历是系统化地访问数据结构中全部数据的一种方式。 - So, for example, for a list of type
ArrayList<String>, this is a traversal:
例如,对于ArrayList<String>类型的列表,下面就是一次遍历:
for (String s : list) { // makes s successively each value in list ............}- For trees there are several traversals, including:
对树而言,常见遍历方式包括:- pre-order traversal
先序遍历 - in-order traversal
中序遍历 - post-order traversal
后序遍历
- pre-order traversal
Pre-order traversal
先序遍历
- The pre-order traversal algorithm is recursive:
If tree is not empty:
先序遍历算法是递归的:
若树非空:- visit root
先访问根节点 - then traverse the left subtree
再遍历左子树 - then traverse the right subtree
最后遍历右子树
- visit root
traversals are done recursively, in pre-order fashion
遍历以递归方式按先序执行
It is called a pre-order traversal because “pre” means “before”, and the root is visited before the subtrees.
之所以称为先序遍历,是因为 “pre” 表示“之前”,即根节点在子树之前被访问。
Pre-order Example
先序示例
If tree is not empty:
若树非空:
- visit root
访问根节点 - then traverse the left subtree
遍历左子树 - then traverse the right subtree
遍历右子树

Pre-order traversal of graph:
该图的先序遍历:
A,B,D,H,E,C,F,I,J,G
An implementation of pre-order traversal
先序遍历实现
public static void preOrderTraversal(BinaryTree t) { if (t != null) { System.out.println(t.data); preOrderTraversal(t.left); preOrderTraversal(t.right); }}In-order traversal
中序遍历
- The in-order traversal algorithm is recursive:
中序遍历算法是递归的:- If tree is not empty:
若树非空:- traverse the left subtree
先遍历左子树 - then visit root
再访问根节点 - then traverse the right subtree
最后遍历右子树
- traverse the left subtree
- If tree is not empty:
traversals are done recursively, in in-order fashion
遍历以递归方式按中序执行
For an in-order traversal, the root is visited “in between” the subtrees.
中序遍历中,根节点在两棵子树“之间”被访问。
In-order Example
中序示例
If tree is not empty:
若树非空:
- traverse the left subtree
遍历左子树 - then visit root
访问根节点 - then traverse the right subtree
遍历右子树

In-order traversal of graph:
该图的中序遍历:
H,D,B,E,A,I,F,J,C,G
An implementation of in-order traversal
中序遍历实现
public static void inOrderTraversal(BinaryTree t) { if (t != null) { inOrderTraversal(t.left); System.out.println(t.data); inOrderTraversal(t.right); }}Post-order traversal
后序遍历
- The post-order traversal algorithm is recursive:
后序遍历算法是递归的:- If tree is not empty:
若树非空:- traverse the left subtree
先遍历左子树 - then traverse the right subtree
再遍历右子树 - visit root
最后访问根节点
- traverse the left subtree
- If tree is not empty:
traversals are done recursively, in post-order fashion
遍历以递归方式按后序执行
It is called a post-order traversal because “post” means “after”, and the root is visited after the subtrees.
之所以称为后序遍历,是因为 “post” 表示“之后”,根节点在子树之后被访问。
Post-order Example
后序示例
If tree is not empty:
若树非空:
- traverse the left subtree
遍历左子树 - then traverse the right subtree
遍历右子树 - then visit root
访问根节点

Post-order traversal of graph:
该图的后序遍历:
H,D,E,B,I,J,F,G,C,A
An implementation of post-order traversal
后序遍历实现
public static void postOrderTraversal(BinaryTree t) { if (t != null) { postOrderTraversal(t.left); postOrderTraversal(t.right); System.out.println(t.data); }}Requirements for this week
本周学习要求
You should achieve by the end of this week’s work:
完成本周学习后,你应当能够:
- understand binary trees
理解二叉树 - able to store data in binary trees
能够在二叉树中存储数据 - understand pre-order, post-order and in-order traversals on binary trees
理解二叉树的先序、后序和中序遍历 - able to write recursive functions on trees.
能够在树结构上编写递归函数。
Binary search tree
二叉搜索树
A Binary Search Tree
二叉搜索树示例
- This is an example of Binary Search Tree
这是一个二叉搜索树示例。 - What property does it have that makes it a binary tree?
它具备什么性质使它成为二叉树? - What additional property does it have?
它还具备什么额外性质? - Why might that property be useful?
这个额外性质为什么有用?

Binary Search Trees
二叉搜索树
- These are a special kind of binary tree
它是二叉树的一种特殊类型。- the data they contain must be a comparable type (where you can do
<,>,==, .equals comparisons), e.g. int, char, String
(.compareTo)
其中存储的数据必须是可比较类型(可进行<、>、==、.equals比较),如 int、char、String(可用.compareTo)。 - The data at the root node is greater than all the data in the left subtree
根节点数据大于左子树中所有数据 - The data at the root node is less than all data in the right subtree
根节点数据小于右子树中所有数据 - … and so on, all the way down the tree (recursive definition)
该性质在整棵树上递归成立(递归定义)
- the data they contain must be a comparable type (where you can do
- Usually binary search trees represent a set of values
二叉搜索树通常表示一组集合值。- there are no duplicate values in the tree
树中没有重复值 - Of course, any binary search tree is also a binary tree
当然,任何二叉搜索树也都是二叉树
- there are no duplicate values in the tree
Invented by P.F. Windley, A.D. Booth, A.J.T. Colin, and T.N. Hibbard in 1960 (Wikipedia)
由 P.F. Windley、A.D. Booth、A.J.T. Colin 和 T.N. Hibbard 于 1960 年提出(Wikipedia)。
Is this a Binary Search Tree?
这是二叉搜索树吗?
![]() | ![]() | ![]() |
|---|
Datatype Invariants
数据类型不变式
- A datatype invariant is a property of an abstract data type which holds for all objects of that type.
数据类型不变式是某个抽象数据类型中对所有对象都成立的性质。 - The datatype invariant for binary search trees is:
二叉搜索树的数据类型不变式是:- The data at the root node is greater than all the data in the left sub-tree
根节点数据大于左子树所有数据 - The data at the root node is less than all the data in the right sub-tree
根节点数据小于右子树所有数据 - … and so on, all the way down the tree
该性质在整棵树中逐层递归成立 - no duplicate values in the tree - so any implementation of such a tree is also an implementation of the concept of a set with operations union-with-singleton and membership.
树中无重复值,因此这类树本质上也实现了集合概念(支持单元素并集与成员判断)。
- The data at the root node is greater than all the data in the left sub-tree
A Search Tree class (for integers)
一个搜索树类(用于整数)
public class SearchTree { public int data; // or whatever public SearchTree left; public SearchTree right; // Reminder: // reference-type fields are initialized to null by default. public SearchTree(int data) { this.data = data; }}Insertion into a Search Tree
插入到搜索树中
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } // do nothing -- val is already in tree return tree;}Does it use recursive method?
它是否使用了递归方法?
What is the base case?
它的递归基线条件是什么?
Insertion into a Search Tree: Example
搜索树插入示例
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
We have some variable
SearchTree t. The initial call is t = insert(t,9) wheretpoints to the root of the tree.
设有变量SearchTree t,初始调用为 t = insert(t,9),其中t指向树根。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
9 > 7 so insert on right
9 > 7,因此向右侧插入。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
Start of recursive call
开始递归调用。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
9 < 12 so insert to left
9 < 12,因此向左侧插入。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
Start of recursive call
开始递归调用。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
9 > 8 so insert to right
9 > 8,因此向右侧插入。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
Start of recursive call
开始递归调用。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
Create a new tree and return it
创建新节点(新树)并返回。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
Return to third invocation of
insert()and update right subtree
返回到第三层insert()调用,并更新右子树。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
Return to second invocation of
insert().
The assignment statement does not change location of left subtree
返回到第二层insert()调用。
赋值语句不会改变左子树的位置。
public SearchTree insert( SearchTree tree, int val) { if (tree == null) { tree = new SearchTree(val); } else if (val > tree.data) { tree.right = insert(tree.right, val); } else if (val < tree.data) { tree.left = insert(tree.left, val); } return tree;}
Return to original statement
t=insert(t,9)
The location of tree has not changed, but it has an extra node.
返回最初语句t=insert(t,9)。
树的根位置未改变,但树中新增了一个节点。
Searching a Search Tree
搜索一个搜索树
public static boolean contains(SearchTree tree, int val) { if (tree == null) { return false; } else if (val > tree.data) { return contains(tree.right, val); } else if (val < tree.data) { return contains(tree.left, val); } else { // tree.data==val return true; }}Does it use recursive method?
它是否使用了递归方法?
What is the base case?
它的递归基线条件是什么?
Exercise
- Sketch out the tree that would be produced by inserting the values 4, 5, 10, 25, 39 in that order, using the insert method shown earlier.
使用前面给出的 insert 方法,按顺序插入 4、5、10、25、39,画出最终生成的树。 - Using the search method shown earlier, how many method calls do you need to make in order to find out whether 39 is in the resulting tree?
使用前面的查找方法,要判断结果树中是否存在 39,需要调用多少次方法? - How many method calls do you need to make to find out whether 100 is in the tree?
若判断 100 是否在树中,需要调用多少次方法?
Special situation with binary search trees
二叉搜索树的特殊情况
If a tree
is lopsided,
a search can still
take up to n steps,
for a tree with n nodes.
Said to have degenerated into a linked list.
如果一棵树
是偏斜的,
那么查找仍可能
需要最多 n 步,
(对于有 n 个节点的树)。
这种情况称为退化为链表。

Best to make the class contain a tree, not be one
最好让类“包含”一棵树,而不是“本身就是”一棵树
- Make a class BinarySearchTree contain a class Node.
让 BinarySearchTree 类内部“包含” Node 类。 - BinarySearchTree can then have as fields:
BinarySearchTree 可包含如下字段:- root - a Node
root - 一个节点引用 - numEntries - an integer
numEntries - 整数计数
- root - a Node
A Search Tree class (for integers)
一个搜索树类(用于整数)
public class BinarySearchTree {
private class Node { private int data; private Node left; private Node right;
public Node(int data) { this.data = data; } }
private Node root; private int numEntries;
public BinarySearchTree() { root = null; numEntries = 0; }…Recursive addNode
递归版 addNode
public Node addNode(Node tree, int val) { if (tree == null) { tree = new Node(val); } else if (val > tree.data) { tree.right = addNode(tree.right, val); } else if (val < tree.data) { tree.left = addNode(tree.left, val); } // val is already in tree; take action return tree;}BinarySearchTree.insert using recursive addNode
使用递归 addNode 的 BinarySearchTree.insert
public void insert (int val) { root = addNode(root, val); numEntries++;} // BinarySearchTree.insertIterative addNode
循环版 addNode
public void addNode(int key, String name) { // does not use recursive way Node p, parent; p = root; while (p != null && p.key != key) { parent = p; if (key < p.key) p = p.left; else p = p.right; } if (p == null) { Node newNode = new Node(key, name); if (p == root) root = newNode; else if (p == parent.left) parent.left = newNode; else parent.right = newNode; numEntries++; } else // p != null - duplicate node detected! Take appropriate action}BinarySearchTree.insert using iterative addNode
使用循环 addNode 的 BinarySearchTree.insert
public void insert (int val) { addNode(val);} // BinarySearchTree.insertPerfectly balanced trees
完全平衡树
A perfectly balanced tree with nodes, will have so searches take at most steps
对于一个包含 个节点的完全平衡树,其深度(高度)为 ,因此查找最多只需 步。

- The depth of a tree defined to be
树的深度定义为:- the length of the longest path from the root to a leaf,
从根到叶子最长路径的长度; - so a tree consisting only of the root node has a depth of 0.
因此只有根节点的树深度为 0; - The depth of an empty tree can be taken to be -1.
空树深度可定义为 -1。
- the length of the longest path from the root to a leaf,
A perfectly balanced tree
一棵完全平衡树

For n nodes, searches take no more than steps.
对于 n 个节点,查找步数不超过 。
- This is described as a algorithm and it is a lot faster than an algorithm with steps, which would be .
这被称为 级别算法,明显快于需要 步的 算法。
Self-balancing trees
自平衡树
- A balanced tree offers better access time, but re-balancing a tree takes time, so it is a trade-off.
平衡树查询更快,但维护平衡本身也需要成本,这是一种权衡。 - This has led to a variety of engineering solutions for self-balancing trees.These include:
因此工程上出现了多种自平衡树方案,包括:- “AVL” trees: G. M. Adelson-Velskii and E. M. Landis, 1962
“AVL 树”:G. M. Adelson-Velskii 与 E. M. Landis(1962) - “Red-black” trees: Rudolf Bayer (1972).
“红黑树”:Rudolf Bayer(1972) - “Symmetric binary B-Trees: Data structure and maintenance algorithms”. Acta Informatica. 1 (4): 290-306
“对称二叉 B 树:数据结构与维护算法”,发表于 Acta Informatica 1(4): 290-306
- “AVL” trees: G. M. Adelson-Velskii and E. M. Landis, 1962
TreeMap
- The provided Java collection class
TreeMapimplements the interfaceMap.
Java 提供的集合类TreeMap实现了Map接口。 - It uses a Red-Black tree and offers access time.
它基于红黑树,访问复杂度为 。- Note: HashMap can offer access time.
注意:HashMap 访问可达 级别。
- Note: HashMap can offer access time.
- But it is possible to iterate through a
TreeMapaccessing the values in order; this is not possible with aHashMap.
但TreeMap可以按有序顺序迭代访问值,而HashMap通常无法做到这一点。
Access Time
访问时间
- Searching is faster in a binary search tree, just as binary search in a sorted array is faster than linear search.
在二叉搜索树中查找通常更快,正如有序数组中的二分查找快于线性查找。 - There is a trade-off: cost of keeping tree balanced versus cost of searches.
存在权衡:维护平衡的代价与查找代价之间需要取舍。 - It is easy to sort numbers using a binary search tree - why and how?
用二叉搜索树做排序很自然,为什么?怎么做?
Requirements
- What you want to achieve by the end of this week’s work:
本周结束时你应达成:- understand binary trees
理解二叉树 - understand binary search trees
理解二叉搜索树 - know the difference!
明确两者差异 - be able to write recursive functions on trees.
能在树结构上编写递归函数
- understand binary trees
以下二叉树的区别:
完美二叉树(perfect binary tree)
完全二叉树(complete binary tree)
完满二叉树(full binary tree)
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!






