1. 二叉树
1.1 性质
二叉查找树:左子树小于右
平衡二叉树:左右一样高
二叉树最重要的操作某过于遍历,namely 按照某一顺序访问树中的所有节点。
通常有四种遍历方式:
- 深度优先:
- 前序遍历 (根-左-右)10,6,4,8,14,12,16
用途:1 拷贝树。 2 计算前缀表达式
- 中序遍历 (左-根-右)4,6,8,10,12,14,16
用途:BST(二叉搜索树)的中序遍历以非降序方式输出节点。
- 后序遍历 (右-左-根)4,8,8,12,16,14,10
后序遍历的用途:1 删除树 2 计算后缀表达式
2. 广度优先:
- 层序遍历
二叉树的遍历时间复杂度,无论递归与否,方式与否,都是O(n)
. 这是因为每个算法都要遍历每个节点仅仅一次。
1.2代码
前序遍历 中序遍历 后序遍历 (递归 非递归)
public class BinaryTree {
protected Node root;
public BinaryTree(Node root) {
this.root = root;
}
public Node getRoot() {
return root;
}
/** 构造树 */
public static Node init() {
Node a = new Node('A');
Node b = new Node('B', null, a);
Node c = new Node('C');
Node d = new Node('D', b, c);
Node e = new Node('E');
Node f = new Node('F', e, null);
Node g = new Node('G', null, f);
Node h = new Node('H', d, g);
return h;// root
}
/** 访问节点 */
public static void visit(Node p) {
System.out.print(p.getKey() + " ");
}
/** 递归实现前序遍历 */
protected static void preorder(Node p) {
if (p != null) {
visit(p);
preorder(p.getLeft());
preorder(p.getRight());
}
}
/** 递归实现中序遍历 */
protected static void inorder(Node p) {
if (p != null) {
inorder(p.getLeft());
visit(p);
inorder(p.getRight());
}
}
/** 递归实现后序遍历 */
protected static void postorder(Node p) {
if (p != null) {
postorder(p.getLeft());
postorder(p.getRight());
visit(p);
}
}
/** 非递归实现前序遍历 */
protected static void iterativePreorder(Node p) {
Stack<Node> stack = new Stack<Node>();
if (p != null) {
stack.push(p);
while (!stack.empty()) {
p = stack.pop();
visit(p);
if (p.getRight() != null)
stack.push(p.getRight());
if (p.getLeft() != null)
stack.push(p.getLeft());
}
}
}
/** 非递归实现后序遍历 单栈法*/
protected static void iterativePostorder3(Node p) {
Stack<Node> stack = new Stack<Node>();
Node node = p, prev = p;
while (node != null || stack.size() > 0) {
while (node != null) {
stack.push(node);
node = node.getLeft();
}
if (stack.size() > 0) {
Node temp = stack.peek().getRight();
if (temp == null || temp == prev) {
node = stack.pop();
visit(node);
prev = node;
node = null;
} else {
node = temp;
}
}
}
}
/** 非递归实现中序遍历2 */
protected static void iterativeInorder2(Node p) {
Stack<Node> stack = new Stack<Node>();
Node node = p;
while (node != null || stack.size() > 0) {
while (node != null) {
stack.push(node);
node = node.getLeft();
}
if (stack.size() > 0) {
node = stack.pop();
visit(node);
node = node.getRight();
}
}
}
2 二叉树的题
2.1 线性时间判断一个树是否是平衡二叉树:
平衡二叉树:左右子树的高度相等。
最直接的方法是遍历树的每个节点的时候,调用函数的TreeDepth得到他的左右节点的高度,如果每个节点的左右子树的高度相差不超过 1. 则它就是一颗平衡二叉树。
但是在计算一个节点的深度的时候,就把该节点和该节点level以下的所有节点都遍历了。 因此,一个节点会被重复遍历多次,这种思路的时间效率不高。所以,效率更高的做法是在计算高度的时候,边计算边判断。
private int getHeight(TreeNode root) {
if (root == null) return 0;
int depL = getHeight(root.left);
int depR = getHeight(root.right);
if (depL < 0 || depR < 0 || Math.abs(depL - depR) > 1) return -1; \\返回给该节点自己的value
else return Math.max(depL, depR) + 1; \\ 返回给该节点自己的value
}
public boolean isBalanced(TreeNode root) {
return (getHeight(root) >= 0);
}
2.2 输入两棵二叉树A,B,判断B是不是A的子结构。
//遍历Tree1,查找与Tree2 root相同的节点
boolean HasSubtree(TreeNode root1, TreeNode root2){
boolean result = false;
if(root1 != null && root2 != null){
if(root1.val == root2.val){
//查找到与Tree2 root相同的节点,接着判断二者是否具有相同结构
result = DoesTree1hasTree2(root1,root2);
}
if(result != true)
result = HasSubtree(root1.left, root2);
if(result != true)
result = HasSubtree(root1.right, root2);
}
return result;
}
boolean DoesTree1hasTree2(TreeNode root1, TreeNode root2){
boolean lflag = false;
boolean rflag = false;
//Tree2结束
if(root2==null){
return true;
}
//Tree2有节点时候,Tree1还有,说明肯定不是包含关系
if(root1==null){
return false;
}
if(root1.val != root2.val){
return false;
}
else{
lflag = DoesTree1hasTree2(root1.left,root2.left);
rflag = DoesTree1hasTree2(root1.right,root2.right);
return lflag && rflag;
}
}
2.3 输入某二叉树的前序遍历和中序遍历结果,请重建二叉树 ,假设前序遍历和中序遍历中不含重复数字。
思路: 前序遍历的每一个节点都是当前子树的根节点。同时,以对应的节点为边界,就会把中序遍历的结果分为左子树和右子树。
public static TreeNode buildTree(int[] preOrder,int start, int[] inOrder,
int end,int length){
// 边界验证
if (preOrder == null || preOrder.length == 0 || inOrder == null
|| inOrder.length == 0 || length <= 0) {
return null;
}
//根据 前序遍历的第一个元素建立树根节点
int value = preOrder[start];
TreeNode root = new TreeNode();
root.val = value;
// 递归终止条件:子树只有一个节点
if (length == 1)
return root;
// 根据 前序遍历的第一个元素在中序遍历中的位置分拆树的左子树和右子树
int i = 0;
while (i < length) {
if (value == inOrder[end - i]) {
break;
}
i++;
}
// 建立子树的左子树
root.left = buildTree(preOrder, start + 1, inOrder,
end - i - 1, length - 1 - i);
// 建立子树的右子树
root.right = buildTree(preOrder, start + length - i,
inOrder, end, i);
return root;
}
2.3.1 根据中序+后序遍历结果重构二叉树
public static TreeNode buildTree(int postOrder[], int pend, int inOrder[],int iend, int length){
//boundary test
if(postOrder == null || postOrder.length == 0 || inOrder == null || inOrder.length == 0 || postOrder.length != inOrder.length)
{
System.out.print("te");
return null;
}
//create root;
TreeNode root = new TreeNode();
int value = postOrder[pend];
root.val = value;
if(length ==1)
return root;
// search the index of the root in inorder
int i =0;
while(inOrder[iend-i]!=value){
i++;
}
root.right = buildTree(postOrder, pend-1, inOrder, iend, i);
root.left = buildTree(postOrder, pend-i-1, inOrder, iend-i-1, length-i-1);
return root;
}