zoukankan      html  css  js  c++  java
  • 二叉树实现

    树是一个非空元素的集合,其中一个元素为根,其余元素为子树,树根是唯一没有父母的元素,相同父母的孩子是兄弟。

    树的高度是指树的级的个数,元素的度是指孩子的个数,叶子节点的度为0,如图所示。

    二叉树是一种特殊的树,二叉树的区别如下:

    (1)二叉树的每个元素正好有两棵子树,树可以有任意棵

    (2)二叉树中每个元素是有序的(左右不能互换),二树是无序的

    (3)二叉树允许为空(根节点=null),树不能为空(有争议)

    二叉树典型结构如图所示

    有以下特性:

    (1)一棵二叉树有n个元素,有n-1条边

    (2)一棵二叉树的高度为h(≥0),则最少有h个元素,最多有2^h-1个元素

    (3)一棵二叉树有n个元素(>0),二叉树的最大高度是n,最小高度是log2(n+1)

    (4)叶子节点数目=度为2的节点数目+1

    二叉树可以采用链表描述和数组描述

    采用链表描述:

    采用链表描述需要首先描述单个节点,每个节点需要保持的信息包括左孩子、有孩子和元素值,左孩子和右孩子可以用指针描述,代码如下:

     1 class BinaryTreeNode{
     2         public BinaryTreeNode left, right;//左子数,右子数
     3         int val;//节点元素
     4         //提供不同的初始化函数
     5         public BinaryTreeNode(){
     6             left = right = null;
     7         }
     8         public BinaryTreeNode(int val){
     9             this.val = val;
    10             left = right = null;
    11         }
    12         public BinaryTreeNode(int val, BinaryTreeNode left, BinaryTreeNode right){
    13             this.val = val;
    14             this.left = left;
    15             this.right = right;
    16         }
    17     }

    然后实现二叉树的操作,二叉树需要实现的操作有元素的增删改查以及打印等信息,二叉树主要实现功能如下:

    public class BinaryTree {
    	private BinaryTreeNode root;
    	private int treeSize; 
    	
    	class BinaryTreeNode{
    		public BinaryTreeNode left, right;//左子数,右子数
    		int val;//节点元素
    		//提供不同的初始化函数
    		public BinaryTreeNode(){
    			left = right = null;
    		}
    		public BinaryTreeNode(int val){
    			this.val = val;
    			left = right = null;
    		}
    		public BinaryTreeNode(int val, BinaryTreeNode left, BinaryTreeNode right){
    			this.val = val;
    			this.left = left;
    			this.right = right;
    		}
    	}
    	
    	//前序遍历
    	public void preOrder(BinaryTreeNode binaryTreeNode){
    		
    	}
    	
    	//中序遍历
    	public void inOrder(BinaryTreeNode binaryTreeNode){
    
    	}
    	
    	//后续遍历
    	public void postOrder(BinaryTreeNode binaryTreeNode){
    
    	}
    	
    	//层次遍历
    	public void levelOrder(BinaryTreeNode binaryTreeNode){
    		
    	}
    	
    	//返回节点数目
    	public final int size(){
    		return treeSize;
    	}
    	
    	//检查是树否为空
    	public final boolean  isEmpty()  {
    		return treeSize == 0;
    	}
    	
    	//确定二叉树的高度
    	public int heightRoot(){
    		
    	}
    	
    	//查找结点
    	public BinaryTreeNode find(int value){
    		
    		return current;
    	}
    	
    	//增加节点
    	public void insert(int value){
    		
    	}
    	
    	//查找最小值
    	public int minimum(){
    
    	}
    	
    	//根据值,删除节点
    	public boolean delete(int value){
    			
    		return true;
    	} 
    	
    }
    

      在所有功能中,节点的数目以及检查树是否为空代码较为简单不做叙述,其他功能实现方式如下。

    前序遍历(DLR):又叫先跟遍历,属于深度优先遍历,遍历方式是根->左->右

    中序遍历(LDR):又叫中跟遍历,属于深度优先遍历,遍历方式是左->根->右

    后续遍历(LRD):又叫后根遍历,属于深度优先遍历,遍历方式是左->右->根

    层次遍历:属于广度优先遍历,自上而下,自左向右逐层遍历节点。

    各个遍历方式如下图所示:

    前序遍历反映了深度优先搜索的思想,沿着二叉树的枝干从左到右游历一圈就是前序遍历,如图所示。

    中序遍历能够应用在BST(Binary Sort Tree|二叉排序树)中,遍历出来的结果一定是有序的,对于一个二叉树进行垂直投影也可以得到中序遍历结果,如图所示:

    此外,中序遍历还可以用于表示中缀表达式,后续遍历可以用于后缀表达式,效果如图所示:

    层次遍历,层次遍历反应的是广度优先搜索思想,对于四种遍历方式,知道任意一种不能唯一的确定数据结构,如果知道中序遍历和另外任意一种遍历方式,就能够唯一的确定一棵树。

    前序遍历、中序遍历和后续遍历的实现代码如下:

    //前序遍历
    	public void preOrder(int binaryTreeNode){
    		//前序遍历二叉树
    		if(binaryTreeNode >= 0 && binaryTreeNode < treeSize){
    			visitTreeNode(binaryTreeNode);
    			preOrder(2*binaryTreeNode+1);
    			preOrder(2*binaryTreeNode+2);
    		}
    	}
    	
    	//中序遍历
    	public void inOrder(int binaryTreeNode){
    		//中序遍历二叉树
    		if(binaryTreeNode >= 0 && binaryTreeNode < treeSize){
    			inOrder(2*binaryTreeNode+1);
    			visitTreeNode(binaryTreeNode);
    			inOrder(2*binaryTreeNode+2);
    		}
    	}
    	
    	//后续遍历
    	public void postOrder(int binaryTreeNode){
    		//后续遍历二叉树
    		if(binaryTreeNode >= 0 && binaryTreeNode < treeSize){
    			postOrder(2*binaryTreeNode+1);
    			postOrder(2*binaryTreeNode+2);
    			visitTreeNode(binaryTreeNode);
    		}
    	}
    

      层次遍历的实现方式稍微复杂,层次遍历首先需要记录每一层的换行的地方,我们可以采用nlast指针始终指向最右的节点来实现,其次需要将每一层的节点依次打印,我们可以借助链表实现,整体的访问过程如下图所示:

    代码实现如下:

    //层次遍历
    	public void levelOrder(BinaryTreeNode binaryTreeNode){
    		//层次遍历二叉树
    		Queue<BinaryTreeNode> nodeQueue = new LinkedList<BinaryTreeNode>();
    		BinaryTreeNode last=binaryTreeNode;
    		BinaryTreeNode nlast=binaryTreeNode;
    		while(binaryTreeNode != null){
    			visitTreeNode(binaryTreeNode);
    			last = binaryTreeNode;
    			//将孩子加入队列
    			if(binaryTreeNode.left != null){
    				nodeQueue.offer(binaryTreeNode.left);
    			}
    			if(binaryTreeNode.right != null){
    				nodeQueue.offer(binaryTreeNode.right);
    			}
    			//提取下一个要访问的节点
    			if(nodeQueue.isEmpty() == true){
    				return;//此处可以退出循环
    			}
    			binaryTreeNode = nodeQueue.poll();
    			if(last == nlast){
    				//该层打印结束,需要换行
    				System.out.println();
    				if(nlast.right != null){
    					nlast = nlast.right;
    				}
    			}
    			
    		}
    	}
    

      树的高度确定只需要递归判定左右子树的高度即可,实现代码如下:

    //确定二叉树的高度
    	public int heightRoot(){
    		return height(root);
    	}
    	
    	private int height(BinaryTreeNode binaryTreeNode){
    		int hl, hr;//左树高度,右树高度
    		if(binaryTreeNode == null){
    			return 0;
    		}
    		hl = height(binaryTreeNode.left);
    		hr = height(binaryTreeNode.right);
    		if(hl > hr)
    			return ++hl;
    		else
    			return ++hr;
    	}
    

      节点的插入:插入节点从根节点出发,如果小于根节点,就向左子树移动,与左孩子比较,如果大于根节点就向右子树移动,与有孩子比较,知道遇到空节点,放到该位置,实现代码如下:

    //增加节点
    	public void insert(int value){
    		BinaryTreeNode node = new BinaryTreeNode(value);
    		if(root == null){
    			//将增加的节点设置为根节点
    			root = node;
    		}else{
    			BinaryTreeNode current = root;
    			BinaryTreeNode parent = root;
    			while(current != null){
    				parent = current;//记录父类信息,防止丢失父类信息
    				if(value < current.val){
    					current = current.left;
    				}else{
    					current = current.right;
    				}
    			}
    			if(value < parent.val){
    				parent.left = node;
    			}else{
    				parent.right = node;
    			}
    		}
    		treeSize++;
    	}
    

      查找最小值:根据二叉树的插入方式可以看出,该二叉树属于二叉查找树,所有节点都满足左子树<该节点<右子树,因此我们按照这个顺序查找最小值即可,代码如下。同理,也可以查找最大值。

    	//查找最小值
    	public int minimum(){
    		BinaryTreeNode current = new BinaryTreeNode();
    		current = root;
    		while(current.left != null){
    			current = current.left;
    		}
    		return current.val;
    	}
    

      查找结点:根据查找二叉树的性质查找即可,代码如下

    //查找结点
    	public BinaryTreeNode find(int value){
    		BinaryTreeNode current = root;
    		while(value != current.val){
    			if(value < current.val){
    				current = current.left;
    			}else{
    				current = current.right;
    			}
    			if(current == null)
    				return null;//没有找到
    		}
    		return current;
    	}
    

      删除节点:对于二叉树来说,删除节点最为复杂,删除节点首先要查找结点,按照节点二叉树的特性首先找到要删除的节点,查找的代码如下:

    BinaryTreeNode current;
    		BinaryTreeNode parent = new BinaryTreeNode();
    		boolean isLeftTrue = false;
    		current = root;
    		//首先查找这个节点
    		while(current.val != value){
    			parent = current;//保存父类信息
    			if(value < current.val){
    				current = current.left;
    				isLeftTrue = true;
    			}else{
    				current = current.right;
    				isLeftTrue = false;
    			}
    			if(current == null){
    				//判断是否找到
    				return false;
    			}
    		}
    

      然后根据要删除节点的情况分为四种情况,分别是没有孩子的节点(叶子)、有左孩子的节点、有有孩子的节点和有两个孩子的节点。

    对于没有孩子的节点,直接删除即可

    //删除一个子节点的情况
    		if(current.left == null && current.right == null){
    			//节点是叶子节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = null;
    			else//删除右节点
    				parent.right = null;
    		}
    

      对于只有一个孩子的节点,先删除该节点,然后将该孩子节点直接放到该节点的位置即可,代码如下:

    else if(current.left == null){
    			//只有右节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = current.right;
    			else//删除右节点
    				parent.right = current.right;
    		}else if(current.right == null){
    			//只有左节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = current.left;
    			else//删除右节点
    				parent.right = current.left;
    		}
    

      对于两个孩子的节点,首先要找到后继节点,然后将后继节点来代替该节点即可,所谓后续节点即也就是中序后继(比这个节点大一点的数),对于某一结点的后继节点一定存在于右子树,因为只有右子树比这个节点大,然后找大的最少的树,寻找右子树的最小值即可(沿着左孩子寻找即可)。寻找后继节点的代码如下:

    //查找后继节点
    	private BinaryTreeNode getSuccessor(BinaryTreeNode delNode){
    		
    		BinaryTreeNode current = delNode.right;//先定位到右子树
    		BinaryTreeNode parent = delNode;
    		//寻找右子树的最小值
    		while(current.left!=null){
    			parent = current;
    			current = current.left;
    		}
    		//如果继点有分支(一定是右分支),因为这个继点要移动到删除节点的位置,需要将继点的右分支放在继点的位置
    		if(current != delNode.right){
    			parent.left = current.right;//继点的右分支放在继点的位置
    			current.right = delNode.right;//将继点放到删除节点的位置
    		}
    		return current;
    	}
    

      然后将继点放在删除的节点位置即可,代码如下:

    else{
    			//有两个分支节点
    			BinaryTreeNode successor = getSuccessor(current);
    			if(current == root){
    				root = successor;
    			}else if(isLeftTrue){
    				parent.left = successor;
    			}else{
    				parent.right = successor;
    			}
    			successor.left = current.left;
    		}
    

      二叉树删除节点的整体代码如下:

    //根据值,删除节点
    	public boolean delete(int value){
    		BinaryTreeNode current;
    		BinaryTreeNode parent = new BinaryTreeNode();
    		boolean isLeftTrue = false;
    		current = root;
    		//首先查找这个节点
    		while(current.val != value){
    			parent = current;//保存父类信息
    			if(value < current.val){
    				current = current.left;
    				isLeftTrue = true;
    			}else{
    				current = current.right;
    				isLeftTrue = false;
    			}
    			if(current == null){
    				//判断是否找到
    				return false;
    			}
    		}
    		
    		//删除一个子节点的情况
    		if(current.left == null && current.right == null){
    			//节点是叶子节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = null;
    			else//删除右节点
    				parent.right = null;
    		}else if(current.left == null){
    			//只有右节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = current.right;
    			else//删除右节点
    				parent.right = current.right;
    		}else if(current.right == null){
    			//只有左节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = current.left;
    			else//删除右节点
    				parent.right = current.left;
    		}else{
    			//有两个分支节点
    			BinaryTreeNode successor = getSuccessor(current);
    			if(current == root){
    				root = successor;
    			}else if(isLeftTrue){
    				parent.left = successor;
    			}else{
    				parent.right = successor;
    			}
    			successor.left = current.left;
    		}
    			
    		return true;
    	} 
    	
    	//查找后继节点
    	private BinaryTreeNode getSuccessor(BinaryTreeNode delNode){
    		
    		BinaryTreeNode current = delNode.right;//先定位到右子树
    		BinaryTreeNode parent = delNode;
    		//寻找右子树的最小值
    		while(current.left!=null){
    			parent = current;
    			current = current.left;
    		}
    		//如果继点有分支(一定是右分支),因为这个继点要移动到删除节点的位置,需要将继点的右分支放在继点的位置
    		if(current != delNode.right){
    			parent.left = current.right;//继点的右分支放在继点的位置
    			current.right = delNode.right;//将继点放到删除节点的位置
    		}
    		return current;
    	}
    

      二叉树所有代码及测试程序如下所示:

    package dataStructure;
    
    import java.util.LinkedList;
    import java.util.Queue;
    
    
    
    public class BinaryTree {
    	private BinaryTreeNode root;
    	private int treeSize; 
    	
    	class BinaryTreeNode{
    		public BinaryTreeNode left, right;//左子数,右子数
    		int val;//节点元素
    		//提供不同的初始化函数
    		public BinaryTreeNode(){
    			left = right = null;
    		}
    		public BinaryTreeNode(int val){
    			this.val = val;
    			left = right = null;
    		}
    		public BinaryTreeNode(int val, BinaryTreeNode left, BinaryTreeNode right){
    			this.val = val;
    			this.left = left;
    			this.right = right;
    		}
    	}
    	
    	//访问一个节点
    	private void visitTreeNode(BinaryTreeNode binaryTreeNode){
    		System.out.print(binaryTreeNode.val+" ");
    	}
    	
    	//前序遍历
    	public void preOrder(BinaryTreeNode binaryTreeNode){
    		//前序遍历二叉树
    		if(binaryTreeNode != null){
    			visitTreeNode(binaryTreeNode);
    			preOrder(binaryTreeNode.left);
    			preOrder(binaryTreeNode.right);
    		}
    	}
    	
    	//中序遍历
    	public void inOrder(BinaryTreeNode binaryTreeNode){
    		//中序遍历二叉树
    		if(binaryTreeNode != null){
    			inOrder(binaryTreeNode.left);
    			visitTreeNode(binaryTreeNode);
    			inOrder(binaryTreeNode.right);
    		}
    	}
    	
    	//后续遍历
    	public void postOrder(BinaryTreeNode binaryTreeNode){
    		//后续遍历二叉树
    		if(binaryTreeNode != null){
    			postOrder(binaryTreeNode.left);
    			postOrder(binaryTreeNode.right);
    			visitTreeNode(binaryTreeNode);
    		}
    	}
    	
    	//层次遍历
    	public void levelOrder(BinaryTreeNode binaryTreeNode){
    		//层次遍历二叉树
    		Queue<BinaryTreeNode> nodeQueue = new LinkedList<BinaryTreeNode>();
    		BinaryTreeNode last=binaryTreeNode;
    		BinaryTreeNode nlast=binaryTreeNode;
    		while(binaryTreeNode != null){
    			visitTreeNode(binaryTreeNode);
    			last = binaryTreeNode;
    			//将孩子加入队列
    			if(binaryTreeNode.left != null){
    				nodeQueue.offer(binaryTreeNode.left);
    			}
    			if(binaryTreeNode.right != null){
    				nodeQueue.offer(binaryTreeNode.right);
    			}
    			//提取下一个要访问的节点
    			if(nodeQueue.isEmpty() == true){
    				return;//此处可以退出循环
    			}
    			binaryTreeNode = nodeQueue.poll();
    			if(last == nlast){
    				//该层打印结束,需要换行
    				System.out.println();
    				if(nlast.right != null){
    					nlast = nlast.right;
    				}
    			}
    			
    		}
    	}
    	
    	//返回节点数目
    	public final int size(){
    		return treeSize;
    	}
    	
    	//检查是树否为空
    	public final boolean  isEmpty()  {
    		return treeSize == 0;
    	}
    	
    	//确定二叉树的高度
    	public int heightRoot(){
    		return height(root);
    	}
    	
    	private int height(BinaryTreeNode binaryTreeNode){
    		int hl, hr;//左树高度,右树高度
    		if(binaryTreeNode == null){
    			return 0;
    		}
    		hl = height(binaryTreeNode.left);
    		hr = height(binaryTreeNode.right);
    		if(hl > hr)
    			return ++hl;
    		else
    			return ++hr;
    	}
    	
    	//查找结点
    	public BinaryTreeNode find(int value){
    		BinaryTreeNode current = root;
    		while(value != current.val){
    			if(value < current.val){
    				current = current.left;
    			}else{
    				current = current.right;
    			}
    			if(current == null)
    				return null;//没有找到
    		}
    		return current;
    	}
    	
    	//增加节点
    	public void insert(int value){
    		BinaryTreeNode node = new BinaryTreeNode(value);
    		if(root == null){
    			//将增加的节点设置为根节点
    			root = node;
    		}else{
    			BinaryTreeNode current = root;
    			BinaryTreeNode parent = root;
    //			while(true){ 
    //				if(value < parent.val){
    //					current = parent.left;
    //					if(current == null){
    //						parent.left = node;//插入节点
    //						return;
    //					}else{
    //						parent = parent.left;
    //					}
    //				}else{
    //					current = parent.right;
    //					if(current == null){
    //						parent.right = node;//插入节点
    //						return;
    //					}else{
    //						parent = parent.right;
    //					}
    //				}
    //			}
    			while(current != null){
    				parent = current;//记录父类信息,防止丢失父类信息
    				if(value < current.val){
    					current = current.left;
    				}else{
    					current = current.right;
    				}
    			}
    			if(value < parent.val){
    				parent.left = node;
    			}else{
    				parent.right = node;
    			}
    		}
    		treeSize++;
    	}
    	
    	//查找最小值
    	public int minimum(){
    		BinaryTreeNode current = new BinaryTreeNode();
    		current = root;
    		while(current.left != null){
    			current = current.left;
    		}
    		return current.val;
    	}
    	
    	//根据值,删除节点
    	public boolean delete(int value){
    		BinaryTreeNode current;
    		BinaryTreeNode parent = new BinaryTreeNode();
    		boolean isLeftTrue = false;
    		current = root;
    		//首先查找这个节点
    		while(current.val != value){
    			parent = current;//保存父类信息
    			if(value < current.val){
    				current = current.left;
    				isLeftTrue = true;
    			}else{
    				current = current.right;
    				isLeftTrue = false;
    			}
    			if(current == null){
    				//判断是否找到
    				return false;
    			}
    		}
    		
    		//删除一个子节点的情况
    		if(current.left == null && current.right == null){
    			//节点是叶子节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = null;
    			else//删除右节点
    				parent.right = null;
    		}else if(current.left == null){
    			//只有右节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = current.right;
    			else//删除右节点
    				parent.right = current.right;
    		}else if(current.right == null){
    			//只有左节点
    			if(current == root)//判断是根节点
    				root = null;
    			else if(isLeftTrue)//删除左节点
    				parent.left = current.left;
    			else//删除右节点
    				parent.right = current.left;
    		}else{
    			//有两个分支节点
    			BinaryTreeNode successor = getSuccessor(current);
    			if(current == root){
    				root = successor;
    			}else if(isLeftTrue){
    				parent.left = successor;
    			}else{
    				parent.right = successor;
    			}
    			successor.left = current.left;
    		}
    			
    		return true;
    	} 
    	
    	//查找后继节点
    	private BinaryTreeNode getSuccessor(BinaryTreeNode delNode){
    		
    		BinaryTreeNode current = delNode.right;//先定位到右子树
    		BinaryTreeNode parent = delNode;
    		//寻找右子树的最小值
    		while(current.left!=null){
    			parent = current;
    			current = current.left;
    		}
    		//如果继点有分支(一定是右分支),因为这个继点要移动到删除节点的位置,需要将继点的右分支放在继点的位置
    		if(current != delNode.right){
    			parent.left = current.right;//继点的右分支放在继点的位置
    			current.right = delNode.right;//将继点放到删除节点的位置
    		}
    		return current;
    	}
    	
    	public static void main(String[] args){
    		BinaryTree tree = new BinaryTree();
    //		二叉树结构如下
    //				    10 
    //			  6             15 
    //		  4      8       12      18 
    //		1   5   7  9   11 13    16 19 		
    		tree.insert(10);
    		tree.insert(6); 
    		tree.insert(15);
    		tree.insert(4);
    		tree.insert(8);
    		tree.insert(1);
    		tree.insert(5);
    		tree.insert(7);
    		tree.insert(9);
    		tree.insert(12);
    		tree.insert(18);
    		tree.insert(11);
    		tree.insert(13);
    		tree.insert(16);
    		tree.insert(19);
    
    		System.out.println("
    前序遍历");
    		tree.preOrder(tree.root);
    		System.out.println("
    中序遍历");
    		tree.inOrder(tree.root);
    		System.out.println("
    后序遍历");
    		tree.postOrder(tree.root);
    		System.out.println("
    层次遍历");
    		tree.levelOrder(tree.root);
    		System.out.println("
    寻找"+tree.find(10).val);
    		tree.delete(8);
    		tree.levelOrder(tree.root);
    		System.out.println("
    ");
    		tree.delete(11); 
    		tree.levelOrder(tree.root);
    		System.out.println("
    ");
    		tree.delete(12); 
    		tree.levelOrder(tree.root);
    		System.out.println("
    ");
    		tree.delete(4);
    		tree.levelOrder(tree.root);
    	}
    }
    

      运行结果如下:

    前序遍历
    10 6 4 1 5 8 7 9 15 12 11 13 18 16 19
    中序遍历
    1 4 5 6 7 8 9 10 11 12 13 15 16 18 19
    后序遍历
    1 5 4 7 9 8 6 11 13 12 16 19 18 15 10
    层次遍历
    10
    6 15
    4 8 12 18
    1 5 7 9 11 13 16 19
    寻找10
    10
    6 15
    4 9 12 18
    1 5 7 11 13 16 19

    10
    6 15
    4 9 12 18
    1 5 7 13 16 19

    10
    6 15
    4 9 13 18
    1 5 7 16 19

    10
    6 15
    5 9 13 18
    1 7 16 19

     此外二叉树还可与用数组实现,采用数组实现不用采用指针记录连接关系,每个节点的左节点是index*2+1,右节点是诗index*2+2,在节点删除的时候,需要手动将节点置空。采用数组实现的二叉树,会存在空间浪费,效率也不是很高,空间表示二叉树的结果如图所示:

    完整代码实现如下所示:

    package dataStructure;
    
    import java.util.LinkedList;
    import java.util.Queue;
    
    
    
    public class BinaryTreeInArray {
    	private int root; 
    	private int treeSize; 
    	private int[] nodeArray = new int[30];
    	
    	public BinaryTreeInArray(){
    		root = 0;
    		treeSize = 0;
    		for(int i=0; i<30; i++)
    			nodeArray[i]  = -1;
    	}
    	//访问一个节点
    	private void visitTreeNode(int binaryTreeNode){
    		System.out.print(nodeArray[binaryTreeNode]+" ");
    	}
    	
    	//前序遍历
    	public void preOrder(int binaryTreeNode){
    		//前序遍历二叉树
    		if(binaryTreeNode >= 0 && binaryTreeNode < treeSize){
    			visitTreeNode(binaryTreeNode);
    			preOrder(2*binaryTreeNode+1);
    			preOrder(2*binaryTreeNode+2);
    		}
    	}
    	
    	//中序遍历
    	public void inOrder(int binaryTreeNode){
    		//中序遍历二叉树
    		if(binaryTreeNode >= 0 && binaryTreeNode < treeSize){
    			inOrder(2*binaryTreeNode+1);
    			visitTreeNode(binaryTreeNode);
    			inOrder(2*binaryTreeNode+2);
    		}
    	}
    	
    	//后续遍历
    	public void postOrder(int binaryTreeNode){
    		//后续遍历二叉树
    		if(binaryTreeNode >= 0 && binaryTreeNode < treeSize){
    			postOrder(2*binaryTreeNode+1);
    			postOrder(2*binaryTreeNode+2);
    			visitTreeNode(binaryTreeNode);
    		}
    	}
    	
    	//层次遍历
    	public void levelOrder(int binaryTreeNode){
    		//层次遍历二叉树
    		Queue<Integer> nodeQueue = new LinkedList<Integer>();
    		int last=binaryTreeNode;
    		int nlast=binaryTreeNode;
    		while(binaryTreeNode >= 0 && binaryTreeNode < treeSize){
    			visitTreeNode(binaryTreeNode);
    			last = binaryTreeNode;
    			//将孩子加入队列
    			if(2*binaryTreeNode+1 >= 0 && 2*binaryTreeNode+1 < treeSize){
    				nodeQueue.offer(2*binaryTreeNode+1);
    			}
    			if(2*binaryTreeNode+2 >= 0 && 2*binaryTreeNode+2 < treeSize){
    				nodeQueue.offer(2*binaryTreeNode+2);
    			}
    			//提取下一个要访问的节点
    			if(nodeQueue.isEmpty() == true){
    				return;//此处可以退出循环
    			}
    			binaryTreeNode = nodeQueue.poll();
    			if(last == nlast){
    				//该层打印结束,需要换行
    				System.out.println();
    				if(2*nlast+2 >= 0 && 2*nlast+2 < treeSize){
    					nlast = 2*nlast+2;
    				}
    			}
    			
    		}
    	}
    	
    	//返回节点数目
    	public final int size(){
    		return treeSize;
    	}
    	
    	//检查是树否为空
    	public final boolean  isEmpty()  {
    		return treeSize == 0;
    	}
    	
    	//确定二叉树的高度
    	public int heightRoot(){
    		return height(root);
    	}
    	
    	private int height(int binaryTreeNode){
    		int hl, hr;//左树高度,右树高度
    		if(binaryTreeNode <= 0 && binaryTreeNode < treeSize){
    			return 0;
    		}
    		hl = height(2*binaryTreeNode+1);
    		hr = height(2*binaryTreeNode+2);
    		if(hl > hr)
    			return ++hl;
    		else
    			return ++hr;
    	}
    	
    	//查找结点
    	public int find(int value){
    		int current = root;
    		while(value != nodeArray[current]){
    			if(value < nodeArray[current]){
    				if(2*current+1 < treeSize)
    					current = nodeArray[2*current+1];
    				else
    					return -1;//没有找到
    			}else{ 
    				if(2*current+2 < treeSize)
    					current = nodeArray[2*current+2];
    				else
    					return -1;//没有找到
    			} 
    		}
    		return nodeArray[current];
    	}
    	
    	//增加节点
    	public void insert(int value){ 
    		int node = value;
    		if(nodeArray[root] == -1){
    			//将增加的节点设置为根节点
    			nodeArray[root] = node;
    		}else{
    			int current = root;
    			int parent = root;
    //			while(true){ 
    //				if(value < nodeArray[parent]){
    //					current = nodeArray[2*parent+1];
    //					if(current >= 0 && current < treeSize){
    //						nodeArray[2*parent+1] = node;//插入节点
    //						return;
    //					}else{
    //						nodeArray[parent] = nodeArray[2*parent+1];
    //					}
    //				}else{
    //					current = nodeArray[2*parent+2];
    //					if(current >= 0 && current < treeSize){
    //						nodeArray[2*parent+2] = node;//插入节点
    //						return;
    //					}else{
    //						parent = nodeArray[2*parent+2];
    //					}
    //				}
    //			}
    			while(nodeArray[current] != -1){
    				parent = current;//记录父类信息,防止丢失父类信息
    				if(value < nodeArray[current]){
    					current = 2*current + 1;
    				}else{
    					current = 2*current + 2;
    				}
    			}
    			nodeArray[current] = node;
    		}
    		treeSize++;
    	}
    	
    	//查找最小值
    	public int minimum(){
    		int current = root;
    		while(nodeArray[2*current + 1] != -1){
    			current = nodeArray[2*current + 1];
    		}
    		return nodeArray[current];
    	}
    	
    	//根据值,删除叶子
    	public boolean delete(int value){
    		int current;
    		int parent = -1;
    		boolean isLeftTrue = false;
    		current = root;
    		//首先查找这个节点
    		while(nodeArray[current] != value){
    			parent = current;//保存父类信息
    			if(value < nodeArray[current]){
    				if(2*current+1 < treeSize){ 
    					current = 2*current+1;
    					isLeftTrue = true;
    				}else
    					return false;
    			}else{
    				if(2*current+2 < treeSize){
    					current = 2*current+2;
    					isLeftTrue = false;
    				}else
    					return false; 
    			} 
    		}
    		
    		//删除一个子节点的情况
    		if(nodeArray[2*current+1] == -1 && nodeArray[2*current+2] == -1){
    			//节点是叶子节点
    			if(current == root)//判断是根节点
    				nodeArray[root] = -1;
    			else if(isLeftTrue)//删除左节点
    				nodeArray[2*parent+1] = -1;
    			else//删除右节点
    				nodeArray[2*parent+2] = -1;
    		}else if(nodeArray[2*current+1] == -1){
    			//只有右节点
    			if(current == root)//判断是根节点
    				nodeArray[root] = nodeArray[2*current+2];
    			else if(isLeftTrue)//删除左节点
    				nodeArray[2*parent+1] = nodeArray[2*current+2];
    			else//删除右节点
    				nodeArray[2*parent+2] = nodeArray[2*current+2];
    			nodeArray[2*current+2] = -1;
    		}else if(nodeArray[2*current+2] == -1){
    			//只有左节点
    			if(current == root)//判断是根节点
    				nodeArray[root] = nodeArray[2*current+1];
    			else if(isLeftTrue)//删除左节点
    				nodeArray[2*parent+1] = nodeArray[2*current+1];
    			else//删除右节点
    				nodeArray[2*parent+2] = nodeArray[2*current+1];
    			nodeArray[2*current+1] = -1;
    		}else{
    			//有两个分支节点
    			int successor = getSuccessor(current);
    			if(current == root){
    				nodeArray[root] = nodeArray[successor];
    			}else if(isLeftTrue){
    				nodeArray[2*parent+1] = nodeArray[successor];
    			}else{
    				nodeArray[2*parent+2] = nodeArray[successor];
    			} 
    			nodeArray[successor] = -1;
    		}
    			
    		return true;
    	} 
    	
    	//查找后继节点
    	private int getSuccessor(int delNode){
    		
    		int current = 2*delNode+2;
    		int parent = delNode;
    		while(nodeArray[2*current+1] != -1){
    			parent = current;
    			current = 2*current+1;
    		}
    		//此处是删除子节点有分支的方法
    //		if(current != 2*delNode+2){
    //			nodeArray[2*parent+1] = nodeArray[2*current+2];//将继点的右节点连接到父类的左节点,空出继点
    ////			nodeArray[2*parent+2] = -1;//右
    //		}
    		return current;
    	}
    	
    	public static void main(String[] args){
    		BinaryTreeInArray tree = new BinaryTreeInArray();
    //		二叉树结构如下
    //				    10 
    //			  6             15 
    //		  4      8       12      18 
    //		1   5   7  9   11 13    16 19 		
    		tree.insert(10);
    		tree.insert(6); 
    		tree.insert(15);
    		tree.insert(4);
    		tree.insert(8);
    		tree.insert(1);
    		tree.insert(5);
    		tree.insert(7);
    		tree.insert(9);
    		tree.insert(12);
    		tree.insert(18);
    		tree.insert(11);
    		tree.insert(13);
    		tree.insert(16);
    		tree.insert(19);
    
    		System.out.println("
    前序遍历");
    		tree.preOrder(tree.root);
    		System.out.println("
    中序遍历");
    		tree.inOrder(tree.root);
    		System.out.println("
    后序遍历");
    		tree.postOrder(tree.root);
    		System.out.println("
    层次遍历");
    		tree.levelOrder(tree.root);
    		System.out.println("
    寻找"+tree.find(10));
    		tree.delete(8);
    		tree.levelOrder(tree.root);
    		System.out.println("
    ");
    		tree.delete(11); 
    		tree.levelOrder(tree.root);
    		System.out.println("
    ");
    		tree.delete(12); 
    		tree.levelOrder(tree.root);
    		System.out.println("
    ");
    		tree.delete(4);
    		tree.levelOrder(tree.root);
    	}
    }
    

    运行结果如下:


    前序遍历
    10 6 4 1 5 8 7 9 15 12 11 13 18 16 19
    中序遍历
    1 4 5 6 7 8 9 10 11 12 13 15 16 18 19
    后序遍历
    1 5 4 7 9 8 6 11 13 12 16 19 18 15 10
    层次遍历
    10
    6 15
    4 8 12 18
    1 5 7 9 11 13 16 19
    寻找10
    10
    6 15
    4 9 12 18
    1 5 7 -1 11 13 16 19

    10
    6 15
    4 9 12 18
    1 5 7 -1 -1 13 16 19

    10
    6 15
    4 9 13 18
    1 5 7 -1 -1 -1 16 19

    10
    6 15
    5 9 13 18
    1 -1 7 -1 -1 -1 16 19

  • 相关阅读:
    《财富自由之路》读后感及读书笔记
    echarts3.x 入门
    Ubuntu 16.04 硬盘安装
    语义化版本控制的规范(转载)
    appcan IDE 无法 请求数据
    jQuery extend 函数
    63342 接口 奇遇 IDEA
    C++调用Java的Jar包
    无法打开 源 文件“stdafx.h”的解决方法
    CString的头文件
  • 原文地址:https://www.cnblogs.com/feichangnice/p/7727928.html
Copyright © 2011-2022 走看看