树是 n个节点的有限集,当n = 0时,称为空树.在任意一个非空树中,有如下特点:
如上图:
A即为根节点,B、C分别为 A的叶子节点. B、C 分别又有自己的子节点,所以B、C也称为A节点的子树.
二叉树是树的一种特殊形式,顾名思义,这种树的每个节点最多有2个孩子节点,当然也可能只有一个,也可能没有.二叉树的两个孩子节点,一个被称为左孩子,一个被称为右孩子,两个孩子节点的顺序位置是固定的.
一个二叉树的所有非叶子节点都存在左右孩子,并且所有叶子节点都在同一级上,那么这个树就是满二叉树.
对于有n个节点的二叉树,按层级顺序编号,则所有节点编号为从1到n.如果这个树所有节点和同样深度的满二叉树的编号为1到n的节点位置相同,则这个二叉树为完全二叉树
换句话说:完全二叉树从根结点到倒数第二层满足满二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐。
使用数组存储时,会按照层级顺序吧二叉树放到数组中对应的位置上.如果某一个节点的左孩子或右孩子空缺,则数组的相应位置也空出来.
2:链式存储:
与链表类似,一个节点而已最多指向两个左右孩子节点,所以二叉树每一个节点包含3部分: 1. data 2.左孩子指针 3.右孩子指针 ,这样的结果是最直观的存储方式.
所以对于一个"节点分布相对均匀"的二叉树来说,如果节点总数是n,那么搜索节点的时间复杂度就是O(logn)
不同与数组和链表的遍历,二叉树从节点关系的角度来看,分为四种:
#####深度优先遍历:
偏向于纵深,一头到底的方式,又具体细分为:
代码示例:
public static class TreeNode {
public TreeNode(int data) {
this.data = data;
}
int data;
TreeNode leftChild;
TreeNode rightChild;
}
/**
* 构建树
*/
public static TreeNode createBinaryTree(List<Integer> input) {
if (input == null || input.isEmpty()) {
return null;
}
TreeNode treeNode = null;
Integer data = input.get(0);
input.remove(0);
if (data != null) {
treeNode = new TreeNode(data);
treeNode.leftChild = createBinaryTree(input);
treeNode.rightChild = createBinaryTree(input);
}
return treeNode;
}
/**
* 前序遍历
*/
public static void preOrderTraveral(TreeNode treeNode) {
if (null == treeNode) {
return;
}
System.out.println(treeNode.data);
preOrderTraveral(treeNode.leftChild);
preOrderTraveral(treeNode.rightChild);
}
/**
* 中序遍历
*/
public static void inOrderTraveral(TreeNode treeNode) {
if (null == treeNode) {
return;
}
preOrderTraveral(treeNode.leftChild);
System.out.println(treeNode.data);
preOrderTraveral(treeNode.rightChild);
}
/**
* 后序遍历
*/
public static void postOrderTraveral(TreeNode treeNode) {
if (null == treeNode) {
return;
}
preOrderTraveral(treeNode.leftChild);
preOrderTraveral(treeNode.rightChild);
System.out.println(treeNode.data);
}
public static void main(String[] args) {
List<Integer> inputList = new ArrayList<>(Arrays.asList(3, 2, 9, null, null, 10, null, null, 8, null, 4));
TreeNode treeNode = createBinaryTree(inputList);
// preOrderTraveral(treeNode); //3 2 9 10 8 4
// inOrderTraveral(treeNode); // 2 9 10 3 8 4
postOrderTraveral(treeNode); // 2 9 10 8 4 3
}
当然,绝大部分可以用递归解决的问题,一般都可以用栈来解决,利用栈可回溯的特性,我们来实现一个前序遍历.
public static void preOderTraverWithStack(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode treeNode = root;
while (treeNode != null || !stack.isEmpty()) {
//当前节点不为空一直输出,遍历完所有左元素
while (treeNode != null) {
System.out.println(treeNode.data);
//遍历完以后将该节点入栈,便于回溯的时候找右元素
stack.push(treeNode);
treeNode = treeNode.leftChild;
}
//开始回溯找右元素
if (!stack.isEmpty()) {
treeNode = stack.pop();
treeNode = treeNode.rightChild;
}
}
}