一、二叉树的概念
1、定义
每个节点最多只有2个子节点的树叫做二叉树。
2、特性
A、在二叉树的第i层上最多有2^(i-1)个结点(i>=1)。
B、高度为k的二叉树,最多有2^k-1个结点(k>=0)。
C、对任何一棵二叉树,如果其叶结点有n个,度为2的非叶子结点有m个,则n = m + 1。
D、具有n个结点的完全二叉树的高度为logn + 1
E、对于有n个结点的完全二叉树,按层次对结点进行编号(从上到下,从左到右),对于任意编号为i的结点:
二、二叉树构建
1、二叉树的存储实现
java
public class BinaryNode<T> {
/**
* 数据
*/
T data;
/**
* 左节点
*/
private BinaryNode left;
/***
* 右节点
*/
private BinaryNode right;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public BinaryNode getLeft() {
return left;
}
public void setLeft(BinaryNode left) {
this.left = left;
}
public BinaryNode getRight() {
return right;
}
public void setRight(BinaryNode right) {
this.right = right;
}
}
2、二叉树的创建
java
public class BinaryTree<T> {
private BinaryNode<T> root;
// 树节点总数
private int size ;
public BinaryTree(T data) {
root = new BinaryNode<T>();
root.setData(data);
size = 1;
}
/**
* 给节点添加左节点
* @param parent
* @param data
* @return
*/
public BinaryNode addLeftNode(BinaryNode parent,T data) {
BinaryNode<T> left = new BinaryNode<T>();
left.setData(data);
parent.setLeft(left);
size++;
return left;
}
/**
* 给节点添加右节点
* @param parent
* @param data
* @return
*/
public BinaryNode addRightNode(BinaryNode parent,T data) {
BinaryNode<T> right = new BinaryNode<T>();
right.setData(data);
parent.setRight(right);
size++;
return right;
}
public BinaryNode<T> getRoot() {
return root;
}
public int getSize() {
return size;
}
}
构造一颗二叉树,如下图
代码如下:
ini
BinaryTree<String> binaryTree = new BinaryTree<String>("A");
BinaryNode n2 = binaryTree.addLeftNode(binaryTree.getRoot(), "B");
BinaryNode n3 = binaryTree.addRightNode(binaryTree.getRoot(), "C");
BinaryNode n4 = binaryTree.addLeftNode(n2, "D");
BinaryNode n5 = binaryTree.addRightNode(n2, "E");
BinaryNode n6 = binaryTree.addLeftNode(n3, "F");
BinaryNode n7 = binaryTree.addRightNode(n3, "G");
三、二叉树遍历
1、前序遍历
前序遍历(根左右): 访问根结点,再访问左子树、再访问右子树。 前序遍历: ABDECFG
递归实现
scss
public List<T> preorderTraversal () {
ArrayList<T> ts = new ArrayList<>();
preorderTraversal(root,ts);
return ts;
}
/**
* 前序遍历
* @param binaryNode 节点
* @param result 结果的list
*/
private void preorderTraversal (BinaryNode<T> binaryNode, List<T> result) {
// 节点不为空,
if (binaryNode != null) {
result.add(binaryNode.getData());
}
if (binaryNode.getLeft() != null) {
preorderTraversal(binaryNode.getLeft(),result);
}
if (binaryNode.getRight() != null) {
preorderTraversal(binaryNode.getRight(),result);
}
}
非递归实现
用到栈(FILO 先进后出的特性)
过程:
arduino
/**
* 非递归的前序遍历
* @return
*/
public List<T> noRecursivePreTraversal () {
ArrayList<T> ts = new ArrayList<>();
noRecursivePreTraversal(root,ts);
return ts;
}
/***
* 非递归的前序遍历
* @param binaryNode
* @param result
*/
private void noRecursivePreTraversal (BinaryNode<T> binaryNode,List<T> result) {
// 借助stack来实现
Stack<BinaryNode> stack = new Stack<>();
// 迭代的停止条件
while (binaryNode != null || stack.size() > 0) {
// 一直向左边进行迭代
while (binaryNode != null) {
// 把当前节点添加到结果集中
result.add(binaryNode.getData());
// 节点进栈,为了后面好,进行回溯
stack.add(binaryNode);
// 向左进行迭代
binaryNode = binaryNode.getLeft();
}
// 循环走完了,要进行出栈
if (stack.size() > 0) {
binaryNode = stack.pop();
// 向右边走
binaryNode = binaryNode.getRight();
}
}
}
2、中序遍历
中序遍历(左根右): 先访问左子树,再访问根结点、再访问右子树。 中序遍历: DBEAFCG
递归实现
scss
/**
* 中序遍历
* @return
*/
public List<T> midTraversal() {
ArrayList<T> ts = new ArrayList<>();
midTraversal(root,ts);
return ts;
}
private void midTraversal(BinaryNode<T> binaryNode,List<T> result) {
if (binaryNode != null) {
if (binaryNode.getLeft() != null) {
midTraversal(binaryNode.getLeft(),result);
}
result.add(binaryNode.getData());
if (binaryNode.getRight() != null) {
midTraversal(binaryNode.getRight(),result);
}
}
}
非递归实现
csharp
/**
* 非递归中序遍历
* @return
*/
public List<T> noRecursiveMidTraversal() {
ArrayList<T> ts = new ArrayList<>();
noRecursiveMidTraversal(root,ts);
return ts;
}
private void noRecursiveMidTraversal(BinaryNode<T> binaryNode,List<T> result) {
// 借助栈
Stack<BinaryNode> stack = new Stack<>();
// 栈不为空
while (!stack.isEmpty() || binaryNode !=null) {
// 一直向左迭代
while (binaryNode != null) {
stack.add(binaryNode);
binaryNode = binaryNode.getLeft();
}
// 出栈
binaryNode = stack.pop();
// 保存结果集
result.add(binaryNode.getData());
binaryNode = binaryNode.getRight();
}
}
3、后序遍历
后续遍历(左右根): 先访问左子树,再访问右子树,再访问根结点。 后续遍历: DEBFGCA
递归实现
scss
/**
* 后序遍历
* @return
*/
public List<T> afterTraversal() {
ArrayList<T> ts = new ArrayList<>();
afterTraversal(root,ts);
return ts;
}
private void afterTraversal(BinaryNode<T> binaryNode,List<T> result) {
if (binaryNode != null) {
// 先左节点
if (binaryNode.getLeft() != null) {
afterTraversal(binaryNode.getLeft(),result);
}
// 再右节点
if (binaryNode.getRight() != null) {
afterTraversal(binaryNode.getRight(),result);
}
// 父节点
result.add(binaryNode.getData());
}
}
非递归实现
scss
/**
* 后序遍历
* @return
*/
public List<T> noRecursiveAfterTraversal() {
ArrayList<T> ts = new ArrayList<>();
noRecursiveAfterTraversal(root,ts);
return ts;
}
private void noRecursiveAfterTraversal(BinaryNode<T> binaryNode,List<T> result) {
Stack<BinaryNode> stackA = new Stack<>();
Stack<BinaryNode> stackB = new Stack<>();
stackA.push(binaryNode);
while (!stackA.isEmpty()) {
binaryNode = stackA.pop();
stackB.push(binaryNode);
// 左边有节点
if (binaryNode.getLeft() != null) {
stackA.push(binaryNode.getLeft());
}
// 右边有节点
if (binaryNode.getRight() != null) {
stackA.push(binaryNode.getRight());
}
}
while (!stackB.isEmpty()) {
BinaryNode<T> pop = stackB.pop();
result.add(pop.getData());
}
}
4、二叉树层序遍历(BFS)
用到队列(FIFO 先进先出的特性)代码后有队列和其中元素的关系具体过程,建议静下心来慢慢看,有助于理解代码如何运行
scss
/**
* 层序遍历
* @return
*/
public List<T> bfsTraversal() {
ArrayList<T> ts = new ArrayList<>();
bfsTraversal(root,ts);
return ts;
}
private void bfsTraversal(BinaryNode<T> binaryNode,List<T> result) {
// 借助队列
Queue<BinaryNode> queue = new ArrayDeque();
if (binaryNode != null) {
queue.add(binaryNode);
}
// 队列不为空
while (!queue.isEmpty() ) {
// 出队
binaryNode = queue.poll();
result.add(binaryNode.getData());
// 左边有节点
if (binaryNode.getLeft() != null) {
queue.add(binaryNode.getLeft());
}
// 右边有节点
if (binaryNode.getRight() != null) {
queue.add(binaryNode.getRight());
}
}
}