二叉树及其遍历

1.什么是二叉树

  • 概念
    二叉树是节点最多可以包含两个子节点的树,每一个节点都可以区分为左子节点和右子节点,二叉树的一个重要性质就是叶节点的数量<=2,如图所示就是一棵简单的二叉树
    在这里插入图片描述

2.二叉树的相关概念

  • 路径
    指的是从根节点到叶节点的路径。
  • 节点的度
    一个节点含有的子树的个数称为该节点的度
  • 节点的权
    节点中有意义的值(通常是数值)

  • 从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 高度
    是由根结点出发,到子结点的最长路径长度。
  • 结点深度
    是指对应结点到根结点路径长度。
  • 森林
    多棵树构成森林

3.二叉树的几种形态

  1. 空树
    在这里插入图片描述
  2. 左斜树和右斜树
    在这里插入图片描述
  3. 不知道怎么描述

在这里插入图片描述
几种特殊的二叉树

  • 1.满二叉树
    一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
    在这里插入图片描述
    性质:

    • 1.一个层数为k 的满二叉树总结点数为:(2^k) -1 。因此满二叉树的节点数一定是奇数
    • 2.第 i 层上的节点个数是:2^ (i -1) 个
  • 2.完全二叉树
    完全二叉树是由满二叉树而引出来的。对于一个树高为h的二叉树,如果其第0层至第h-1层的节点都满。如果最下面一层节点不满,又如果其所有的节点在左边的连续排列,空位都在右边。这样的二叉树就是一棵完全二叉树。
    在这里插入图片描述
    性质:

    • 所有的叶结点都出现在第k层或k-l层(层次最大的两层)所以,对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
    • 一棵具有n个结点的深度为k的二叉树,它的每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应

二叉树的构建

  • 顺序存储结构实现 — 基于数组

    按照完全二叉树的节点按层依次自左向右编号,若节点为空,则在数组中留空。 在这里插入图片描述

	
public class ArrayTree<T> {
    private T[] data;
    private int deep;
    private int MaxSize;
    private int size = 0;
    public ArrayTree(int deep){
        this.deep = deep;
        //根据二叉树的深度计算元素的最大个数
        MaxSize = (int)Math.pow(2, deep)-1;
        data = (T[]) new Object[MaxSize];
    }
    /**
     * 
     * @param value 根节点的值
     */
    public void creatRoot(T value){
        data[0] = value;
        size++;
    }
    /**
     * @param value 添加的节点的值
     * @param index 父节点的索引
     * @param left 是否添加到左节点,否则为右节点
     * */
    public void add(T value,int index,boolean left){
        if(data[index] == null){
            throw new RuntimeException(index + ": null");
        }
        if(2*index+1 >= MaxSize){
            throw new RuntimeException("array full");
        }
        if(left){
            data[2*index+1] = value;
            size++;
        }
        else{
            data[2*index+2] =  value;
            size++;
        }
    }
    //根据根节点是否为空判断二叉树是否为空
    public boolean isEmpty(){
        return data[0]==null;
    }

    //二叉树中元素的个数
    public int size(){
        return this.size;
    }
    //打印二叉树
    public String toString(){
        String str = "";
        for(int i=0; i<MaxSize; i++){
            str = str + data[i] + " ";
        }
        return str;
    }
    public static void main(String args[]){
        ArrayTree<String> at = new ArrayTree<String>(3);
        /**
         *                 根节点
         *     第二层左节点         第二层右节点
         *         第三层右节点  第三层左节点   
         */
        at.creatRoot("根节点");
        at.add("第二层左节点", 0, true);
        at.add("第二层右节点", 0, false);
        at.add("第三层右节点", 1, false);
        System.out.println(at.toString());
        at.add("第三层左节点", 2, true);
        System.out.println(at.toString());
        System.out.println("二叉树元素个数: " + at.size());
    }
}

  • 链式存储结构实现 – 基于链表

一个节点主要有三部分组成:

  • 节点携带的数据

  • 节点指向左子树的指针

  • 节点指向右子树的指针

    在这里插入图片描述

public class BinaryTreeNode {

	//值
	private int value;
	//左子节点
	private BinaryTreeNode left;
	//右子节点
	private BinaryTreeNode right;

	public BinaryTreeNode(int value, BinaryTreeNode left, BinaryTreeNode right) {
		super();
		this.value = value;
		this.left = left;
		this.right = right;
	}

	public BinaryTreeNode(int value) {
		super();
		this.value = value;
	}
	//省略get / set
public class BinaryTree {
	
	//根节点
	BinaryTreeNode root;

	public BinaryTreeNode getRoot() {
		return root;
	}

	public void setRoot(BinaryTreeNode root) {
		this.root = root;
	}
	
	public static void main(String[] args) {
		
		BinaryTreeNode root = new BinaryTreeNode(21);
		
		BinaryTree binaryTree = new BinaryTree();
		
		binaryTree.setRoot(root);
		
		BinaryTreeNode leftNode = new BinaryTreeNode(12);
		root.setLeft(leftNode);
		
		BinaryTreeNode rightNode = new BinaryTreeNode(32);
		root.setRight(rightNode);
		
		leftNode.setLeft(new BinaryTreeNode(15));
		leftNode.setRight(new BinaryTreeNode(1));
		rightNode.setLeft(new BinaryTreeNode(4));
		rightNode.setRight(new BinaryTreeNode(5));
	}
}

二叉树的遍历

树的遍历操作有三种,前序遍历,中序遍历和后序遍历。三者的不同之处在于处理子节点的时间不同。前序遍历是先处理根节点,然后处理左孩子最后处理右孩子。将处理根节点的操作挪到处理左右节点的操作之间,我们就得到了中序遍历。如果挪到最后,那就是后序遍历。每一种遍历都有递归和非递归两种实现方式

  • 前序遍历
	/**
	 * 前序遍历非递归实现
	 * @param binaryTree
	 */
	private void preOrder(BinaryTree binaryTree) {
		
		BinaryTreeNode root = binaryTree.getRoot();
		ArrayList<Integer> list = new ArrayList<>();
		Stack<BinaryTreeNode> stack = new Stack<>();
		if(root == null) {
			return ;
		}else {
			stack.push(root);
			while(!stack.isEmpty()) {
				BinaryTreeNode node = stack.pop();
				list.add(node.getValue());
				if(node.getRight()!=null) {
					stack.push(node.getRight());
				}
				if(node.getLeft()!=null) {
					stack.push(node.getLeft());
				}
			}
		}
		for(Integer i : list) {
			System.out.println(i);
		}
	}
	
	/**
	 * 前序遍历递归实现
	 * @param root
	 */
	private void preOrder2(BinaryTreeNode root) {

		if(root!=null) {
			System.out.println(root.getValue());
			preOrder2(root.getLeft());
			preOrder2(root.getRight());
		}
	}
  • 中序遍历
	/**
	 * 中序遍历非递归实现
	 * @param binaryTree
	 */
	private void order(BinaryTree binaryTree) {
		
		BinaryTreeNode root = binaryTree.getRoot();
		ArrayList<Integer> list = new ArrayList<>();
		Stack<BinaryTreeNode> stack = new Stack<>();
		
		while(root!=null || !stack.isEmpty()) {
			if(root!=null) {
				//为了能让结果list先存放的左子节点
				stack.push(root);
				root = root.getLeft();
			}else {
				root= stack.pop();
				list.add(root.getValue());
				root = root.getRight();
			}
		}
		
		for(Integer i : list) {
			System.out.println(i);
		}
	}
	
	/**
	 * 中序遍历非递归实现
	 * @param root
	 */
	private void order2(BinaryTreeNode root) {
		if(root != null) {
			order2(root.getLeft());
			System.out.println(root.getValue());
			order2(root.getRight());
		}
	}
  • 后序遍历
	/**
	 * 后序遍历非递归实现
	 * @param binaryTree
	 */
	private void proOrder(BinaryTree binaryTree) {
		
		BinaryTreeNode root = binaryTree.getRoot();
		ArrayList<Integer> list = new ArrayList<>();
		//操作栈
		Stack<BinaryTreeNode> stack = new Stack<>();
		//结果栈,用于存放后序遍历的结果
		Stack<BinaryTreeNode> resultStack = new Stack<>();
		
		while(root!=null || !stack.isEmpty()) {
			if(root!=null) {
				//执行完这里后,结果栈中已经从栈顶到栈底分别是:右子节点 --- 根节点
				stack.push(root);
				resultStack.push(root);
				root = root.getRight();
			}else {
				//拿到左子节点放入操作栈,重复上边操作,此时,结果栈变成了我们想要的顺序:左子节点 -- 右子节点 -- 根节点
				root = stack.pop();
				root = root.getLeft();
			}
		}
		//将遍历结果放入ArrayList中,其实本可以不使用这一步直接遍历栈,个人习惯
		while(!resultStack.isEmpty()) {
			BinaryTreeNode node = resultStack.pop();
			list.add(node.getValue());
		}
		for(Integer i : list) {
			System.out.println(i);
		}
		
	}
	
	/**
	 * 后序遍历递归实现
	 * @param root
	 */
	private void proOrder2(BinaryTreeNode root) {
		if(root!=null) {
			proOrder2(root.getLeft());
			proOrder2(root.getRight());
			System.out.println(root.getValue());
		}
	}

测试

public static void main(String[] args) {
		
		BinaryTreeNode root = new BinaryTreeNode(21);
		
		BinaryTree binaryTree = new BinaryTree();
		
		binaryTree.setRoot(root);
		
		BinaryTreeNode leftNode = new BinaryTreeNode(12);
		root.setLeft(leftNode);
		
		BinaryTreeNode rightNode = new BinaryTreeNode(32);
		root.setRight(rightNode);
		
		leftNode.setLeft(new BinaryTreeNode(15));
		leftNode.setRight(new BinaryTreeNode(1));
		rightNode.setLeft(new BinaryTreeNode(4));
		rightNode.setRight(new BinaryTreeNode(5));
	
		System.out.println("********前序遍历非递归**********");
		binaryTree.preOrder2(binaryTree.root);
		System.out.println("********前序遍历递归**********");
		binaryTree.preOrder(binaryTree);
		System.out.println("********中序遍历非递归**********");
		binaryTree.order(binaryTree);
		System.out.println("********中序遍历递归**********");
		binaryTree.order2(binaryTree.root);
		System.out.println("********后序遍历非递归**********");
		binaryTree.proOrder(binaryTree);
		System.out.println("********后序遍历递归**********");
		binaryTree.proOrder2(binaryTree.root);
	}

该二叉树是
在这里插入图片描述
前序遍历:
21 --> 12 --> 15 --> 1 --> 32 --> 4 --> 5
在这里插入图片描述
中序遍历:
15 --> 12 --> 1 --> 21 --> 4 --> 32 --> 5
在这里插入图片描述
后序遍历:
15 --> 1 -->12 --> 4 -->5 -->32 -->21
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值