树总结:二叉树、二叉搜索树、平衡二叉树、DFS/BFS、LeetCode经典题

树的特征

在区分各类树结构之前,先了解一些树的基本特征:

节点的高度 = 节点到叶子节点的最长路径(边数)
节点的深度 = 根节点到这个节点所经历的边个数
节点的层数 = 节点的深度+1
树的高度 = 根节点的高度

图例:
树的高度、深度、层数

树结构简介

满二叉树、完全二叉树

  • 二叉树
    二叉树的每个节点最多有两个子节点,且可以为空
  • 满二叉树
    在一棵二叉树中,如果所有分支节点都存在左右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
    满二叉树

性质:

  • 层数为k且含有2k-1个节点
  1. 完全二叉树
    对一颗具有n个节点的二叉树按层序号编号,如果编号为i(1<=i<=n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
    完全二叉树

性质:

  • 具有n个结点的完全二叉树的深度为[log2n]+1(注:[ ]表示向下取整)
  • 如果对一棵有n个节点的完全二叉树的节点按层序编号,则对任一节点i(1<=i<=n) ,有:
    • 如果i = 1,则节点i是二叉树的根,无双亲;如果i > 1,则其双亲parent(i)是节点[i/2];
    • 如果2i>n,则节点i无左孩子,否则其左孩子是节点2i;
    • 如果2i+1>n,则节点i无右孩子,否则其右孩子是节点2i+1。

二叉搜索树

二叉搜索树(Binary Search Tree,BST)又叫二叉查找树
性质:左节点值 < 根节点值 < 右节点值

平衡二叉树

平衡二叉树(Balanced Binary Tree)又被称为AVL树
性质:它是一棵空树或者它的左右两个子树的高度差的绝对值不超过1

深度优先遍历DFS

  1. 遍历规则
    不断沿着定点的深度方向遍历
  2. 细分
    1)先序遍历:根节点——左节点——右节点
    2)中序遍历:左节点——根节点——右节点
    3)后序遍历:左节点——右节点——根节点
  3. 分析
    不全部保留节点,占用空间少;有回溯操作(即有入栈、出栈操作),运行速度慢。
  4. Java代码
//深度优先遍历(双端队列)
public void depthFirstSearch(TreeNode root) {
		if(root==null) {
			return;
		}
		Deque<TreeNode> deque=new Deque<>();
		deque.offerLast(root);
		while(!deque.isEmpty()) {
			TreeNode node = deque.pollLast();    //弹出栈顶元素
			System.out.print(node.data+" ");
			if(node.right!=null) {
				deque.offerLast(node.right);    //深度优先遍历,先遍历左边,后遍历右边,栈先进后出
			}
			if(node.left!=null) {
				deque.offerLast(node.left);
			}
		}
}

广度优先遍历BFS

  1. 遍历规则
    先访问当前节点的所有邻接点,再返回父节点
  2. 层次遍历
    从上到下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问节点,访问完一层就进入下一层,直到没有节点可以访问为止。
  3. 分析
    保留全部节点,占用空间大;无回溯操作(即无入栈、出栈操作),运行速度快。
  4. Java代码
//广度优先遍历(优先队列)
public void BroadFirstSearch(TreeNode root) {
	if(root==null) {
	    return;
	}
	Queue<TreeNode> queue=new LinkedList<>();
	queue.add(root);
	while(!queue.isEmpty()) {
	    TreeNode node=queue.poll();
	    System.out.print(node.data+" ");
	    if(node.left!=null) {
		queue.offer(node.left);    //广度优先遍历,每一行从左到右遍历
	    }
	    if(node.right!=null) {
		queue.offer(node.right);
	    }		
	}
}

LeetCode经典题

从上到下打印二叉树

此系列对应剑指offer的32-I、II、III

  1. 32-I(BFS)

从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。输出为一个数组

public int[] levelOrder(TreeNode root){
		List<Integer> list = new ArrayList<>();
		//BFS,优先队列存储
		Queue<TreeNode> queue = new LinkedList<>();
		if(root==null)return new int[0];
		queue.add(root);
		while(!queue.isEmpty()){
			TreeNode node = queue.poll();
			list.add(node.val);
			if(node.left!=null)queue.add(node.left);
			if(node.right!=null)queue.add(node.right);
		}
		//list转换为数组形式
		int[] ans = new int[list.size()];
		for(int i = 0;i < list.size();i++){
			ans[i] = list.get(i);
		}
		return ans;
	}
  1. 32-II(BFS+双端队列)

从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。输出为一个双重list数组

public List<List<Integer>> levelOrder1(TreeNode root){
		List<List<Integer>> ans = new ArrayList<>();
		Deque<TreeNode> deque = new LinkedList<>();
		
		if(root!=null)deque.offerLast(root);
		while(!deque.isEmpty()){
			List<Integer> tmp = new ArrayList<>();
			for(int i = deque.size();i > 0;i--){
				TreeNode node = deque.pollFirst();
				tmp.add(node.val);
				if(node.left!=null)deque.offerLast(node.left);
				if(node.right!=null)deque.offerLast(node.right);
			}
			ans.add(tmp);
			if(!deque.isEmpty())break;
		}
		return ans;
		
	}
  1. 32-III(BFS+双端队列+奇偶逻辑分离)

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其它行依次类推。

public List<List<Integer>> levelOrder2(TreeNode root){
		//奇偶逻辑分离
		List<List<Integer>> ans = new ArrayList<>();
		Deque<TreeNode> deque = new LinkedList<>();
		
		if(root!=null)deque.offerLast(root);
		while(!deque.isEmpty()){
			//从左往右打印奇数层
			List<Integer> tmp = new ArrayList<>();
			for(int i = deque.size();i > 0;i--){
				TreeNode node = deque.pollFirst();
				tmp.add(node.val);
				if(node.left!=null)deque.offerLast(node.left);
				if(node.right!=null)deque.offerLast(node.right);
			}
			ans.add(tmp);
			if(deque.isEmpty())break;
			//从右往左打印偶数层
			tmp = new ArrayList<>();
			for(int i = deque.size();i > 0;i--){
				TreeNode node = deque.pollLast();
				tmp.add(node.val);
				if(node.right!=null)deque.offerFirst(node.right);
				if(node.left!=null)deque.offerFirst(node.left);
			}
			ans.add(tmp);
			if(deque.isEmpty())break;
		}
		return ans;
		
	}

二叉(搜索)树的最近公共祖先

此系列对应剑指offer的68-I、II

  1. 68-I(利用二叉搜索树的性质)

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
		TreeNode ancestor = root;
		while(true){
			if(p.val < ancestor.val && q.val < ancestor.val)ancestor = ancestor.left;
			else if(p.val < ancestor.val && q.val < ancestor.val)ancestor = ancestor.right;
			else break;
		}
		return ancestor;
	}
  1. 68-II(递归)

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q){
		if(root==null||root==p||root==q)return root;
		TreeNode left = lowestCommonAncestor(root.left, p, q);
		TreeNode right = lowestCommonAncestor(root.right, p, q);
		if(left==null && right==null)return null;
		else if(left!=null && right!=null)return root;
		else return left==null?right:left;
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值