(1)下面二叉树的构建是通过一个数组来构造二叉树
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]
{ [1, 2, 3, 4, 5, 6, 7, 8, 9 ]}
[1,2,3,4,5,6,7,8,9]
- 将数组元素转换成n个Node节点(n为数组大小,这里是9)。
- 用LinkedList数据结构存储成n个Node节点。
- 先处理前 ( n / 2 − 1 ) = 3 (n/2-1)=3 (n/2−1)=3个父节点,因为数组是从下标0开始的,所以这里的3个父节点,即是下标为0、1、2的节点,然后处理最后1个父节点即下标为3的节点,因为最后一个父节点可能没有右孩子,当n为奇数时才有右孩子,为偶数时只有左孩子。
- 父节点索引与左右孩子索引的关系:左孩子索引= ( n / 2 + 1 ) (n/2+1) (n/2+1);右孩子索引= ( n / 2 + 2 ) (n/2+2) (n/2+2),这里如果按 n / 2 n/2 n/2和 n / 2 + 1 n/2+1 n/2+1来表示的话,由于下标是从0开始的,这样下标为0的父节点的子节点就是0和1,很明显是有问题的,所以才要分别加1。
(2)遍历
遍历是对树的一种最基本的运算,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。
设L、D、R分别表示遍历左子树、访问根结点和遍历右子树, 则对一棵二叉树的遍历有三种情况:DLR(称为先根次序遍历),LDR(称为中根次序遍历),LRD (称为后根次序遍历)。即遍历的次序是根据父节点的位置而定的。
例子1:
import java.util.LinkedList;
import java.util.List;
/**
* 功能:把一个数组的值存入二叉树中,然后进行3种方式的遍历
*/
public class TestBinaryTree {
public static void main(String[] args) {
TestBinaryTree binTree = new TestBinaryTree();
binTree.createBinTree();
Node root = nodeList.get(0); // nodeList中第0个索引处的值即为根节点
System.out.println("先序遍历:");
preOrderTraverse(root);
System.out.println();
System.out.println("中序遍历:");
inOrderTraverse(root);
System.out.println();
System.out.println("后序遍历:");
postOrderTraverse(root);
}
private int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
private static List<Node> nodeList = null;
// 内部静态类:节点类
private static class Node {
Node leftChild;
Node rightChild;
int data;
Node(int newData) {
leftChild = null;
rightChild = null;
data = newData;
}
}
public void createBinTree() {
nodeList = new LinkedList<Node> ();
// 将一个数组的值依次转换为Node节点
for (int nodeIndex = 0; nodeIndex < array.length; nodeIndex++) {
nodeList.add(new Node(array[nodeIndex]));
}
// 对前lastParentIndex-1个父节点按照父节点与孩子节点的数字关系建立二叉树
for (int parentIndex = 0; parentIndex < array.length / 2 - 1; parentIndex++) {
nodeList.get(parentIndex).leftChild = nodeList.get(parentIndex * 2 + 1); // 左孩子
nodeList.get(parentIndex).rightChild = nodeList.get(parentIndex * 2 + 2); // 右孩子
}
// 最后一个父节点:因为最后一个父节点可能没有右孩子,所以单独拿出来处理
int lastParentIndex = array.length / 2 - 1;
nodeList.get(lastParentIndex).leftChild = nodeList.get(lastParentIndex * 2 + 1); // 左孩子
if (array.length % 2 == 1) {
nodeList.get(lastParentIndex).rightChild = nodeList.get(lastParentIndex * 2 + 2); // 右孩子,如果数组的长度为奇数才建立右孩子
}
}
// 先序遍历(传入的是根节点)
public static void preOrderTraverse(Node node) {
if (node == null)
return;
System.out.print(node.data + " ");
preOrderTraverse(node.leftChild);
preOrderTraverse(node.rightChild);
}
// 中序遍历 ((传入的是根节点))
public static void inOrderTraverse(Node node) {
if (node == null)
return;
inOrderTraverse(node.leftChild);
System.out.print(node.data + " ");
inOrderTraverse(node.rightChild);
}
// 后序遍历((传入的是根节点))
public static void postOrderTraverse(Node node) {
if (node == null)
return;
postOrderTraverse(node.leftChild);
postOrderTraverse(node.rightChild);
System.out.print(node.data + " ");
}
}
运行:
先序遍历:
1 2 4 8 9 5 3 6 7
中序遍历:
8 4 9 2 5 1 6 3 7
后序遍历:
8 9 4 5 2 6 7 3 1
例子2:
下面这个例子对二叉树的学习更全面
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
public class TestBinaryTree {
public static void main(String[] args) {
// TODO Auto-generated method stub
TestBinaryTree tree = new TestBinaryTree();
int[] datas = new int[]{1,2,3,4,5,6,7,8,9};
List<Node> nodelist = new LinkedList<>();
tree.creatBinaryTree(datas, nodelist);
Node root = nodelist.get(0); // nodelist中的0索引存储的是根节点
//System.out.println("递归先序遍历:");
//tree.preOrderTraversal(root);
//System.out.println();
System.out.println("非递归先序遍历:");
tree.preOrderTraversalbyLoop(root);
System.out.println();
System.out.println("递归中序遍历:");
tree.inOrderTraversal(root);
System.out.println();
System.out.println("非递归中序遍历:");
tree.inOrderTraversalbyLoop(root);
System.out.println();
System.out.println("递归后序遍历:");
tree.postOrderTraversal(root);
System.out.println();
System.out.println("非递归后序遍历:");
tree.postOrderTraversalbyLoop(root);
System.out.println();
System.out.println("广度优先遍历:");
tree.bfs(root);
System.out.println();
System.out.println("深度优先遍历:");
List<List<Integer>> rst = new ArrayList<>();
List<Integer> list = new ArrayList<>();
tree.dfs(root,rst,list);
System.out.println(rst);
}
/**
*
* @param datas 实现二叉树各节点值的数组
* @param nodelist 二叉树list
*/
private void creatBinaryTree(int[] datas,List<Node> nodelist){
//将数组变成node节点
for(int nodeindex=0;nodeindex<datas.length;nodeindex++){
Node node = new Node(datas[nodeindex]);
nodelist.add(node);
}
//给所有父节点设定子节点
for(int index=0;index<nodelist.size()/2-1;index++){
//编号为n的节点他的左子节点编号为2*n 右子节点编号为2*n+1 但是因为list从0开始编号,所以还要+1
//这里父节点有1(2,3),2(4,5),3(6,7),4(8,9) 但是最后一个父节点有可能没有右子节点 需要单独处理
nodelist.get(index).setLeft(nodelist.get(index*2+1));
nodelist.get(index).setRight(nodelist.get(index*2+2));
}
//单独处理最后一个父节点 因为它有可能没有右子节点
int index = nodelist.size()/2-1;
nodelist.get(index).setLeft(nodelist.get(index*2+1)); //先设置左子节点
if(nodelist.size() % 2 == 1){ //如果有奇数个节点,最后一个父节点才有右子节点
nodelist.get(index).setRight(nodelist.get(index*2+2));
}
}
/**
* 遍历当前节点的值
* @param nodelist
* @param node
*/
public void checkCurrentNode(Node node){
System.out.print(node.getVar()+" ");
}
/**
* 非递归前序遍历
* @param node
*/
public void preOrderTraversalbyLoop(Node node){
Stack<Node> stack = new Stack(); // 栈:FILO
Node p = node;
while(p!=null || !stack.isEmpty()){
while(p!=null){ //当p不为空时,就读取p的值,并不断更新p为其左子节点,即不断读取左子节点
checkCurrentNode(p); //打印输出p节点的值
stack.push(p); //将p入栈
p = p.getLeft(); // 更新p的左子节点
}
if(!stack.isEmpty()){
p = stack.pop();
p = p.getRight();
}
}
}
/**
* 非递归中序遍历
* @param node
*/
public void inOrderTraversalbyLoop(Node node){
Stack<Node> stack = new Stack();
Node p = node;
while(p!=null || !stack.isEmpty()){
while(p!=null){
stack.push(p);
p = p.getLeft();
}
if(!stack.isEmpty()){
p = stack.pop();
checkCurrentNode(p);
p = p.getRight();
}
}
}
/**
* 非递归后序遍历
* @param node
*/
public void postOrderTraversalbyLoop(Node node){
Stack<Node> stack = new Stack<>();
Node p = node,prev = node;
while(p!=null || !stack.isEmpty()){
while(p!=null){
stack.push(p);
p = p.getLeft();
}
if(!stack.isEmpty()){
Node temp = stack.peek().getRight(); // peek()查看栈顶的对象而不移除它。
if(temp == null||temp == prev){
p = stack.pop();
checkCurrentNode(p);
prev = p;
p = null;
}else{
p = temp;
}
}
}
}
/**
* 先序遍历二叉树
* @param root 二叉树根节点
*/
public void preOrderTraversal(Node node){
if (node == null) //很重要,必须加上 当遇到叶子节点用来停止向下遍历
return;
checkCurrentNode(node);
preOrderTraversal(node.getLeft());
preOrderTraversal(node.getRight());
}
/**
* 中序遍历二叉树
* @param root 根节点
*/
public void inOrderTraversal(Node node){
if (node == null) //很重要,必须加上
return;
inOrderTraversal(node.getLeft());
checkCurrentNode(node);
inOrderTraversal(node.getRight());
}
/**
* 后序遍历二叉树
* @param root 根节点
*/
public void postOrderTraversal(Node node){
if (node == null) //很重要,必须加上
return;
postOrderTraversal(node.getLeft());
postOrderTraversal(node.getRight());
checkCurrentNode(node);
}
/**
* 广度优先遍历(从上到下遍历二叉树)
* @param root
*/
public void bfs(Node root){
if(root == null) return;
LinkedList<Node> queue = new LinkedList<Node>();
queue.offer(root); //首先将根节点存入队列
//当队列里有值时,每次取出队首的node打印,打印之后判断node是否有子节点,若有,则将子节点加入队列
while(queue.size() > 0){
Node node = queue.peek();
queue.poll(); //取出队首元素并打印
System.out.print(node.var+" ");
if(node.left != null){ //如果有左子节点,则将其存入队列
queue.offer(node.left);
}
if(node.right != null){ //如果有右子节点,则将其存入队列
queue.offer(node.right);
}
}
}
/**
* 深度优先遍历
* @param node
* @param rst
* @param list
*/
public void dfs(Node node,List<List<Integer>> rst,List<Integer> list){
if(node == null) return;
if(node.left == null && node.right == null){
list.add(node.var);
/* 这里将list存入rst中时,不能直接将list存入,而是通过新建一个list来实现,
* 因为如果直接用list的话,后面remove的时候也会将其最后一个存的节点删掉*/
rst.add(new ArrayList<>(list));
list.remove(list.size()-1);
}
list.add(node.var);
dfs(node.left,rst,list);
dfs(node.right,rst,list);
list.remove(list.size()-1);
}
/**
* 节点类
* var 节点值
* left 节点左子节点
* right 右子节点
*/
class Node{
int var;
Node left;
Node right;
public Node(int var){
this.var = var;
this.left = null;
this.right = null;
}
public void setLeft(Node left) {
this.left = left;
}
public void setRight(Node right) {
this.right = right;
}
public int getVar() {
return var;
}
public void setVar(int var) {
this.var = var;
}
public Node getLeft() {
return left;
}
public Node getRight() {
return right;
}
}
}
运行:
非递归先序遍历:
1 2 4 8 9 5 3 6 7
递归中序遍历:
8 4 9 2 5 1 6 3 7
非递归中序遍历:
8 4 9 2 5 1 6 3 7
递归后序遍历:
8 9 4 5 2 6 7 3 1
非递归后序遍历:
8 9 4 5 2 6 7 3 1
广度优先遍历:
1 2 3 4 5 6 7 8 9
深度优先遍历:
[[1, 2, 4, 8], [1, 2, 4, 9], [1, 2, 5], [1, 3, 6], [1, 3, 7]]
参考:
https://blog.youkuaiyun.com/ls5718/article/details/51226674