[java] 树基础

这篇文章就是对树的知识做一个整理,主要以二叉树为例

1、定义

	public class TreeNode {
	   int val;
	   TreeNode left;
	   TreeNode right;
	   TreeNode(int x) { val = x; }
	}

2、遍历

2.1、先序遍历

递归实现

    public void preOrder(TreeNode root) {
        if(root!=null){
			System.out.print(root.val);
			preOrder(root.left);
			preOrder(root.right);
		}
    }

非递归实现

    public void preOrder(TreeNode root){
        Stack<TreeNode> stack=new Stack<TreeNode>();
        while(true){
            while(root!=null){
                System.out.print(root.val);
                stack.push(root);
                root=root.left;
            }
            if(stack.isEmpty()) break;
            root=stack.pop();
            root=root.right;
        }
    }

2.2、中序遍历

递归实现

    public void inOrder(TreeNode root) {
        if(root!=null){
			inOrder(root.left);
			System.out.print(root.val);
			inOrder(root.right);
		}
    }

非递归实现

    public void inOrder(TreeNode root){
        Stack<TreeNode> stack=new Stack<TreeNode>();
        while(true){
            while(root!=null){
                stack.push(root);
                root=root.left;
            }
            if(stack.isEmpty())break;
            root=stack.pop();
            System.out.print(root.val);
            root=root.right;
        }
    }

2.3、后序遍历

递归实现

    public void postOrder(TreeNode root) {
        if(root!=null){
			postOrder(root.left);
			postOrder(root.right);
			System.out.print(root.val);
		}
    }

非递归实现

    public void postOrder(TreeNode root){
        Stack<TreeNode> stack=new Stack<TreeNode>();
        while(true){
            if(root!=null){
                stack.push(root);
                root=root.left;
            }else{
                if(stack.isEmpty()) return;

                if(null==stack.peek().right){
                    root=stack.pop();
                    System.out.print(root.val);
                    while(root==stack.peek().right){
                        System.out.print(stack.peek().val);
                        root=stack.pop();
                        if(stack.isEmpty()){
                            break;
                        }
                    }
                }
                if(!stack.isEmpty())
                    root=stack.peek().right;
                else
                    root=null;
            }
        }
    }

tips: 妙用前序遍历的非递归可以实现后序遍历的非递归实现,这里需要注意几点改变:后序时,先遍历右,再遍历左,最后将得到的结果反向就好了。

2.4、层序遍历

    public void levelOrder(TreeNode root){
        TreeNode temp;
        Queue<TreeNode> queue=new LinkedList<TreeNode>();
        queue.offer(root);
        while(!queue.isEmpty()){
            temp=queue.poll();
            System.out.print(temp.val);
            if(temp.left!=null) queue.offer(temp.left);
            if(temp.right!=null) queue.offer(temp.right);
        }
    }

3、树的构造(一维数组转二叉树)

在这里插入图片描述
基本方法,二叉树里面的空节点全部用null代替

	Integer[] array=new Integer[]{2,null,4,null,null,9,8,null,null,null,null,null,null,4};
    TreeNode root = createTreeNode(array, 1);
	// 因为数组中含有null,所以必须用Integer类型,如果是完全二叉树,则可以用int
    private static TreeNode createTreeNode(int[] array, int index) {
        if(index > array.length){
            return null;
        }
        int value = array[index - 1];
        if(value == null){
            return null;
        }
        TreeNode node = new TreeNode(value);
        node.left = createTreeNode(array, index * 2);
        node.right = createTreeNode(array, index * 2 + 1);
        return node;
    }

上述算法同样适用于完全二叉树,但是在非完全二叉树时,大部分情况下使用的是下面的优化的一维数组

	Integer[] array=new Integer[]{2,null,4,9,8,null,null,4};
	TreeNode root = arrayToTreeNode(array);
	
    public static TreeNode arrayToTreeNode(Integer[] array){
        if(array.length == 0){
            return null;
        }
        TreeNode root = new TreeNode(array[0]);
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        boolean isLeft = true;
        for(int i = 1; i < array.length; i++){
            TreeNode node = queue.peek();
            if(isLeft){
                if(array[i] != null){
                    node.left = new TreeNode(array[i]);
                    queue.offer(node.left);
                }
                isLeft = false;
            }else {
                if(array[i] != null){
                    node.right = new TreeNode(array[i]);
                    queue.offer(node.right);
                }
                queue.poll();
                isLeft = true;
            }
        }
        return root;
    }

4、树的深度

	public static int deep(TreeNode root){
		if(root == null)
			return 0;
		return 1+Math.max(deep(root.left),deep(root.right));
	}

5、二叉搜索树

5.1、插入节点

	int[] array=new int[]{2,1,5,3,4,6,9,7,8};
    TreeNode root=null;
    for(int a:array){
        root=insert(a,root);
    }
    
    public TreeNode insert(int key,TreeNode root){    
        TreeNode p=new TreeNode(); 
        p.val=key;
        if(root==null) return p;
        TreeNode parent=root;
        while(true){
            if(key>parent.val){
                if(parent.right==null){
                    parent.right=p;
                    break;
                }
                else parent=parent.right;
            }
            else{
                if(parent.left==null){
                    parent.left=p;
                    break;
                }
                else parent=parent.left;
            }
        }
        return root;
    }

5.2、查找节点

5.2.1、查找某个值

    public TreeNode find(int key){
        TreeNode cur = root;
        while (cur.val != key){
            if (key > cur.val)
                cur = cur.right;
            else
                cur = cur.left;
            if (cur == null) return null;
        }
        return current;
    }

5.2.2、查找前驱节点

前驱节点代表的是中序遍历序列的前一个节点。先取当前节点的左节点,然后取该节点的右节点,直到右节点为空,则最后指向的节点为前驱节点。

    public TreeNode getPre(TreeNode root) {
		root = root.left;
		while (root.right != null) root = root.right;
		return root;
    } 

5.2.3、查找后继节点

后继节点代表的是中序遍历序列的下一个节点。先取当前节点的右节点,然后一直取该节点的左节点,直到左节点为空,则最后指向的节点为后继节点。

    public TreeNode getNext(TreeNode root) {
        root = root.right;
        while (root.left != null) root = root.left;
        return root;
    } 

5.3、删除节点(难点)

删除节点复杂一点,主要可以分为三种情况:

  1. 删除的节点是叶子节点:删除节点23
  2. 删除的节点有一个孩子:删除节点24
  3. 删除的节点有两个孩子(这个里面包含两种情况)
    • 后继节点为删除节点的右孩子:删除节点30
    • 后继节点为删除节点的右孩子的左子树:删除节点24

在这里插入图片描述
非递归实现

    public TreeNode delete(int key,TreeNode root){
        TreeNode cur = root;
        TreeNode parent = new TreeNode();
        boolean isRightChild = true;
        while (cur.val!= key){
            parent = cur;
            if (key > cur.val){
                cur = cur.right;
                isRightChild = true;
            }
            else{
                cur = cur.left;
                isRightChild = false;
            }
            if (cur == null) return root; // 没有找到要删除的结点
        }

        // 要删除结点为叶子结点
        if (cur.right == null && cur.left == null){
            if (cur == root) root = null;
            else{
                if (isRightChild) parent.right = null;
                else parent.left = null;
            }
        }

        //要删除结点有一个子结点
        else if(cur.left==null){
            if(cur==root) root=cur.right;
            else if(isRightChild) parent.right=cur.right;
            else parent.left=cur.right;
        }
        else if(cur.right==null){
            if(cur==root) root=cur.left;
            else if(isRightChild) parent.right=cur.left;
            else parent.left=cur.left;
        }

        //要删除结点有两个子结点
        else{
            TreeNode next=getNext(cur);    //找到要删除结点的后继结点
            // 树的预处理
            if(next!=cur.right)
                cur.right.left=next.right;
                next.right=cur.right;
            
            if(cur==root)
                root=next;
            else if(isRightChild)
                parent.right=next;
            else
                parent.left=next;
                
            next.left=cur.left;   
        }
        return root;
    }

递归实现

  • 如果 key > root.val,说明要删除的节点在右子树,root.right = delete(root.right, key)。
  • 如果 key < root.val,说明要删除的节点在左子树,root.left = delete(root.left, key)。
  • 如果key == root.val,则该节点就是我们要删除的节点,则:
    • 如果该节点是叶子节点,则直接删除它:root = null。
    • 如果该节点不是叶子节点且有右节点,则用它的后继节点的值替代 root.val = next.val,然后删除后继节点。
    • 如果该节点不是叶子节点且只有左节点,则用它的前驱节点的值替代 root.val = pre.val,然后删除前驱节点。
  • 返回 root。

递归实现和非递归实现不一样,在非递归中,删除的就是需要删除的节点,其他节点的位置会跟着变化,但是在递归中,节点位置本身没变,只是节点中的值在变化,所以这个里面的前驱结点和后继结点的函数返回的是节点的值而不是节点

	public TreeNode delete(TreeNode root, int key) {
	    if (root == null) return null;
	    if (key > root.val) root.right = delete(root.right, key);
	    else if (key < root.val) root.left = delete(root.left, key);
	    else {
	      if (root.left == null && root.right == null) root = null;
	      else if (root.right != null) {
	        root.val = getNext(root);
	        root.right = delete(root.right, root.val);
	      }
	      else {
	        root.val = getPre(root);
	        root.left = delete(root.left, root.val);
	      }
	    }
	    return root;
	}

6、平衡二叉搜索树

6.1、有序数组转平衡二叉搜索树

选择中点作为根节点,根节点左侧的作为左子树,右侧的作为右子树即可

    public TreeNode sortedArrayToBST(int[] nums) {
        TreeNode root=creat(nums,0,nums.length-1);
        return root;
    }
    public TreeNode creat(int[] nums,int low,int high){
        if(low>high) return null;
        int mid=(low+high)/2;
        TreeNode p=new TreeNode(nums[mid]);
        p.left=creat(nums,low,mid-1);
        p.right=creat(nums,mid+1,high);
        return p;
    }

6.2、二叉搜索树转平衡二叉搜索树

先中序遍历为有序数组再构建二叉搜索树,代码略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值