树
二叉树
---- 二叉树的遍历,添加,查找,删除
---- 顺序存储二叉树
-----线索化二叉树
赫夫曼树
赫夫曼编码
二叉排序树
平衡二叉树中的AVL数
二叉树
完全二叉树:
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树
满二叉树:
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树
二叉树的遍历,添加,查找,删除
代码
public class BinaryTreeDemo{
public static void main(String[] args) {
TreeNode root=new TreeNode(1);
TreeNode node1=new TreeNode(2);
root.setLeft(node1);
TreeNode node2=new TreeNode(3);
root.setRight(node2);
TreeNode node3=new TreeNode(4);
node1.setLeft(node3);
TreeNode node4=new TreeNode(5);
node1.setRight(node4);
TreeNode node5=new TreeNode(6);
node2.setLeft(node5);
TreeNode node6=new TreeNode(7);
node2.setRight(node6);
TreeNode node7=new TreeNode(8);
node3.setLeft(node7);
TreeNode node8=new TreeNode(9);
node3.setRight(node8);
BinaryTree binaryTree = new BinaryTree(root);
//前序遍历输出
//binaryTree.preOrder();
//中序遍历输出
//binaryTree.infixOrder();
//后序遍历输出
//binaryTree.postOrder();
//前序遍历查找
// TreeNode tree = binaryTree.preOrderSearch(8);
// System.out.println(tree);
//中序遍历查找
//TreeNode tree = binaryTree.infixOrderSearch(1);
//System.out.println(tree.getNo());
//后序遍历查找
//TreeNode tree = binaryTree.postOrderSearch(0);
//System.out.println(tree.getNo());
//节点删除,如果删除的是非叶子节点,将非叶子节点下的节点也删除
//binaryTree.deleteTree(node5);
//binaryTree.preOrder();
//节点删除,如果删除的是非叶子节点,只删除此节点,此节点的左节点代替它
binaryTree.deleteNode(node1);
binaryTree.preOrder();
}
}
class BinaryTree{
private TreeNode root;
public BinaryTree(TreeNode root) {
this.root=root;
}
//删除节点node,然后node节点的左子节点代替node
public void deleteNode(TreeNode node5) {
if(root !=null){
if(this.root.getNo()==node5.getNo()){
//1.首先缓存根节点的右节点(tempNode)
//2.如果根节点没有左子节点,则将右节点设置为根节点结束
//3.如果根节点有左子节点,则将左子节点设置为根节点(leftRoot)
//4.如果tempNode为null则直接结束,否则进行如下操作
//5.如果leftRoot有没有左子节点,则将tempNode设置为leftRoot的左子节点
//6.如果leftRoot有没有右子节点,则将tempNode设置为leftRoot的右子节点
//7.如果leftRoot有左子节点和又子节点则作如下操作
//8.
// 缓存根节点
// TreeNode temp=leftRoot;
// 缓存根节点的右节点
// TreeNode rightTemp=null;
// 只要他的右子节点不为空
// while(temp.right!=null){
// 缓存两个值,同时给
// rightTemp=temp.right;
// temp.right=tempNode;
// tempNode=rightTemp;
// 节点后移
// temp=temp.left;
// }
// 他的右子节点为空时,将值设置为他的父节点的右子节点
// temp.setRight(tempNode);
}else{
this.root.deleteNode(node5);
}
}
}
//删除以node5为根节点的树
public void deleteTree(TreeNode node5) {
if(root.getNo()==node5.getNo()){
this.root=null;
return;
}
root.deleteTree(node5);
}
//后序遍历查找
public TreeNode postOrderSearch(int i) {
return root.postOrderSearch(i);
}
//中序遍历查找
public TreeNode infixOrderSearch(int i) {
return root.infixOrderSearch(i);
}
//前序遍历查找
public TreeNode preOrderSearch(int i) {
return this.root.preOrderSerach(i);
}
//前序遍历
//中->左->右
public void preOrder() {
if(root!=null){
root.preOrder();
}
}
//中序遍历
//左->中->右
public void infixOrder(){
if(root!=null){
root.infixOrder();
}
}
//后序遍历
//左->右->中
public void postOrder(){
if(root!=null){
root.postOrder();
}
}
}
class TreeNode{
private int no;
private TreeNode left;
private TreeNode right;
public TreeNode(int no) {
this.no=no;
}
//删除node节点
public void deleteNode(TreeNode node) {
if(this.left!=null && this.left.getNo()==node.getNo()){
//这时要删除this.left节点
//1.如果this.left有右节点 缓存this.left的左子节点
TreeNode rightNode=null;
if(this.left.right!=null){
rightNode=this.left.right;
}
//删除left节点,如果left有子节点,将left的左子节点代替它
if(this.left.left==null){
this.setLeft(rightNode);
}else{//要删除的孩子的节点有值
this.setLeft(this.left.left);
//当替换的节点没有右节点时直接将缓存的节点添加上
if(rightNode!=null){
if(this.left.right==null){
this.left.right=rightNode;
}else if(this.left.left==null){
this.left.left=rightNode;
}else{
//缓存删除节点位置上的节点
TreeNode temp=this.left;
//缓存删除节点位置的右节点
TreeNode temp2=null;
while(temp.right!=null){
temp2=temp.right;
temp.right=rightNode;
rightNode=temp2;
temp=temp.left;
}
temp.setRight(rightNode);
}
}
}
}
if(this.right!=null && this.right.getNo()==node.getNo()){
//同left 代码略
}
if(this.left!=null){
this.left.deleteNode(node);
}
if(this.right!=null){
this.right.deleteNode(node);
}
}
//删除以node为根节点的树
public void deleteTree(TreeNode node5) {
// if(this.getNo()==node5.getNo()){
// this=null; 这里不能设置要在调用方法中设置
// }
if(this.getLeft()!=null && this.getLeft().getNo() == node5.getNo()){
this.setLeft(null);
}
if(this.getRight()!=null && this.getRight().getNo() == node5.getNo()){
this.setRight(null);
}
if(this.getLeft()!=null){
this.getLeft().deleteTree(node5);
}
if(this.getRight()!=null){
this.getRight().deleteTree(node5);
}
}
public TreeNode postOrderSearch(int i) {
TreeNode treeNode=null;
if(this.left!=null){
treeNode=this.left.postOrderSearch(i);
}
if(this.right!=null){
treeNode=this.right.postOrderSearch(i);
}
if(this.no==i){
return this;
}
return treeNode;
}
public TreeNode infixOrderSearch(int i) {
TreeNode treeNode=null;
if(this.left !=null){
treeNode=this.left.infixOrderSearch(i);
}
if(treeNode!=null){
return treeNode;
}
if(this.no==i){
return this;
}
if(this.right !=null){
treeNode=this.right.infixOrderSearch(i);
}
return treeNode;
}
//前序遍历查找
public TreeNode preOrderSerach(int i) {
if(this.no==i)
return this;
//用来返回,如果没有找到就返回这个null
TreeNode treeNode = null;
if(this.left!=null)
treeNode=this.left.preOrderSerach(i);
if(treeNode != null){
return treeNode;
}
if(this.right!=null)
treeNode=this.right.preOrderSerach(i);
return treeNode;
}
//后序遍历
public void postOrder() {
if(this.getLeft()!=null){
this.getLeft().postOrder();
}
if(this.getRight()!=null){
this.getRight().postOrder();
}
System.out.println(this.no);
}
//中序遍历
public void infixOrder() {
if(this.getLeft()!=null){
this.left.infixOrder();
}
System.out.println(this.no);
if(this.getRight()!=null){
this.right.infixOrder();
}
}
//前序遍历
public void preOrder() {
System.out.println(this.getNo());
if(this.getLeft()!=null){
this.getLeft().preOrder();
}
if(this.getRight()!=null){
this.getRight().preOrder();
}
}
public int getNo() {
return no;
}
public TreeNode getLeft() {
return left;
}
public TreeNode getRight() {
return right;
}
public void setNo(int no) {
this.no = no;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public void setRight(TreeNode right) {
this.right = right;
}
}
顺序存储二叉树
思想
从数据存储来看,数组存储方式和树的存储方式可以相互转换,即数组可以转换成树,树也可以转换成数组。
顺序存储二叉树的特点:
顺序二叉树通常只考虑完全二叉树
第n个元素的左子节点为 2 * n + 1
第n个元素的右子节点为 2 * n + 2
第n个元素的父节点为 (n-1) / 2
最后一个叶子节点为 数组长度/2-1
代码
public class ArrBinaryTreeDemo {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5, 6, 7 };
BinaryTree tree=new BinaryTree(arr);
tree.preOrder();
}
}
class BinaryTree{
//用数组存储树
private int[] root;
public BinaryTree(int[] arr) {
this.root=arr;
}
//前序遍历 中 左 右
public void preOrder() {
preOrder(0);
}
private void preOrder(int i) {
System.out.println(root[i]);
if(2*i+1<root.length){
preOrder(2*i+1);
}
if(2*i+2<root.length){
preOrder(2*i+2);
}
}
}
线索化二叉树
思想
线索化二叉树 本质上是将 一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继节点
优点
(1)利用线索二叉树进行中序遍历时,不必采用堆栈处理,速度较一般二叉树的遍历速度快,且节约存储空间(使用while循环即可遍历树,不用递归调用了)
(2)任意一个结点都能直接找到它的前驱和后继结点
缺点
(1)结点的插入和删除麻烦,且速度也较慢
(2)线索子树不能共用
图解
代码
赫夫曼树
思想
给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称这样的二叉树为赫夫曼树
权值较大的结点离根较近,权值较小的结点离根较远(权值一般为节点出现个数)
代码
赫夫曼编码
思想
代码
二叉排序树
思想
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
平衡二叉树中的AVL树
思想
代码