二叉树Java

目录

创建二叉树

实现思路

代码展示

代码讲解

遍历二叉树

实现思路

代码展示

代码讲解

总结


 

创建二叉树

实现思路

想要创建二叉树,首先就要有一个理论基础。

二叉树(binary tree)可以理解为子节点最多为2的树,也就是说每个节点(node)度(degree)都不大于2。什么是度呢?度指的是一个节点拥有子树的数目。如下图,A点的度为2,因为它有两个节点B,C,同时,A也被称为根节点,因为该节点上面再没有点了。另外,我们可以看到,这棵二叉树有4层节点,换句话说,该树的深度为4。

那节点包含什么呢?节点包含了数据项以及指向子树节点的指针。我们应该注意到了H,I,L等在最后一层的节点,它们没有子节点。像这种度为0的节点,我们称之为叶节点(leaf)

二叉树没有规定每个节点的度必须是二,于是这里就衍生出了几种二叉树。

 

b2ea618502b577f27d0fe260617a5c1d.jpeg

满二叉树

第一种是满二叉树。如上图所示,满二叉树指的是度只有0或2的树,而且度为0的节点在同一层内。其实就是除了最后一层没有任何子节点外,每一层的节点都有两个子节点。那这满二叉树的总节点又是多少呢?让我们观察一下。深度为2的满二叉树总节点为3;深度为3的总节点是7;深度为4的总节点是15……想必大家都已经发现规律了,对于一个深度为h的满二叉树,其总节点数=2^h-1。由此我们也不难推出,满二叉树中每一层的节点数=2^(h-1)。

1cb69c6fcf6ae01820db9bfbe5f6c0b6.jpeg

完全二叉树

 第二种是完全二叉树。上图(图2)就是一个完全二叉树。完全二叉树由满二叉树衍生而来。若一个二叉树有h层,从1~h-1层的所有节点数都达到最大个数,且第 h 层所有的结点都连续集中在最左边,就是完全二叉树。比如回到图1,我们砍掉O点后它就变成了个完全二叉树。什么?还想砍?我们需要按照这个顺序砍:O->N->M……如果你砍完O后就心急火燎的把M砍掉了,这就不是一个完全二叉树了,因为M比N更靠左,要先砍N才能砍M。

76d46929194fe7e24fdfe5e0b02a7f5a.png

退化二叉树

 第三种是退化二叉树。如上图,退化二叉树十分显著的特征就是:1.该树的度只有0或1;  2.要不所有节点都处于左侧,要不所有节点都处于右侧;  3.长相与链表神似。

9f764cff1be8db172c2077141c15ff9a.png

平衡二叉树

注:上图来源于知乎文章:什么是平衡二叉树(AVL)

平衡二叉树(AVL)。上图就是一个平衡二叉树(图内的数字是每个节点内储存的数据)。平衡二叉树的左子树和右子树的高度之差的绝对值不超过1且它的左子树和右子树都是一颗平衡二叉树。

6f802020b3d6ddf7b695e013e047d61f.jpeg

搜索二叉树 - 斐波那契数列

二叉搜查树。上述树的类型针对的是树的整体结构,而二叉搜查树针对的是二叉树节点的值的分配。在二叉搜查树中,每一个节点的左子节点都小于等于该节点的值,而右子节点大于该节点的值。搜查树,顾名思义,方便搜查。如果大家了解过二分查找(binary search)和线性查找( linear search),就会发现,这里二叉搜查树对值的分配就很符合二分查找的原理,而退化二叉树就对应这线性查找。毫无疑问,二叉搜查树在支持搜索节点值这方面是极具优越性的。我们一会初始化的也是二叉搜查树。

1fd883e385eb0002ad7658fcd792ad83.jpeg

二叉树:数组/列表储存 - 节点对应索引

二叉树的储存形式。二叉树有两种储存方式。我们代码演示的是链表储存,实现起来相对直接且简单。其实数组储存也是比较主流的一种储存方式。大家可能会比较疑惑,毕竟一个节点只包含指针和数据项,怎么转换成有索引的数组元素呢?我们可以给每个节点标上索引。如果节点的索引是i,左子树就是i*2+1,右子树则是i*2+2。听起来比较抽象,我们看上图理解一下:(节点处标的是索引)


 

代码展示

package advance.dataStructure;

class TreeNode {
	int val;
	TreeNode left;
	TreeNode right;

	TreeNode() {
	}
	TreeNode(int val) {//overload
		this.val = val;
	}

	TreeNode(int val, TreeNode left, TreeNode right) {
		this.val = val;
		this.left = left;
		this.right = right;
	}
};
//test
public class Main{
	public static void main(String[] args) {
		TreeNode node=new TreeNode(4);
		System.out.println(node.left);
	}
}
package advance.dataStructure;

import java.util.ArrayList;
import java.util.List;
import advance.algorithm.sortArray.Select;

public class BinaryTree_init {
	public TreeNode root = null;

	public BinaryTree_init(int data) {
		this.root = new TreeNode(data);
	}

	public void addVal(int data) {//balanced binary tree
		TreeNode node = new TreeNode(data);
		TreeNode temp = this.root;
		while (true) {
			if (temp.val >= data) {
				if (temp.left != null) {
					temp = temp.left;
				} else {
					temp.left = node;
					break;
				}

			} else {
				if (temp.right != null) {
					temp = temp.right;
				} else {
					temp.right = node;
					break;
				}
			}
		}
	}

	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();//创建列表对象
		int[] arr = { 1, 5, 9, 7, 4, 2, 3 };
		arr = Select.select(arr);

        //将数组里的元素排序好后全部添加到列表中来
		for (int e : arr)
			list.add(e);
		int mid = Math.round(arr.length / 2);
		list.remove(mid);//删除中位数,因为该数要作根节点的值

        //初始化二叉树并赋值
		BinaryTree_init bt = new BinaryTree_init(arr[mid]);
		for (int e : list)
			bt.addVal(e);

        //遍历二叉树
		ErgordicBinaryTree obj = new ErgordicBinaryTree();
		List<Integer> lst = obj.order(bt.root, 1);
		for (int e : lst)
			System.out.println(e);

	}
}

代码讲解

treeNode这个语法和链表的listNode十分相似,除了treeNode有两个“next”,其他性质和listNode都差不多。另外,addVal()用的是链式存储,前序遍历。得到的树还是一个二叉树。


遍历二叉树

实现思路

遍历二叉树有三种主要实现思路,其中两种是迭代法。我们今天讲的思路是递归法,是这三种方法中最简单也最容易理解的。遍历有分为前序遍历,中序遍历和后序遍历。这三者有什么不同呢?我们可以看下图理解。(图中标的是每种遍历取节点数据项的顺序) 

cfa1a2b4ec7637eca67c87f5ef566997.jpeg

前序遍历

前序遍历大家看了上图理解的估计也七七八八了,我们发现,前序遍历的顺序刚好就是平衡二叉树从小到大的节点值。

 

15f6316d03fbe0dcfc542248536ad588.jpeg

中序遍历

中序遍历 

 

4f44b148a44b85bae78e2bc2f7d57a5f.jpeg

后序遍历

后序遍历在处理逆波兰表达式上很有效果。如果把第三层的节点值算作数,第二层节点算作加减而根节点为乘除的话,按照这个顺序遍历倒也是很方便 


 代码展示

package advance.dataStructure;

import java.util.ArrayList;
import java.util.List;

public class ErgordicBinaryTree {
	public List<Integer> order(TreeNode root, int num) {
		List<Integer> bt = new ArrayList<>();
		switch (num) {
		case 0:
			preorder(root, bt);
			break;
		case 1:
			inorder(root, bt);
			break;
		case 2:
			postorder(root, bt);
			break;
		default:
			break;
		}
		return bt;
	}

	private void preorder(TreeNode root, List<Integer> R) {
		if (root == null) {
			return;
		}
		R.add(root.val);
		preorder(root.left, R);
		preorder(root.right, R);
	}

	private void inorder(TreeNode root, List<Integer> R) {
		if (root == null) {
			return;
		}
		inorder(root.left, R);
		R.add(root.val);
		inorder(root.right, R);
	}

	private void postorder(TreeNode root, List<Integer> R) {
		if (root == null) {
			return;
		}
		postorder(root.left, R);
		postorder(root.right, R);
		R.add(root.val);
	}
}

代码讲解

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值