Java学习day21-利用顺序表创建二叉树和二叉树的迭代遍历

一级目录

二级目录

三级目录

利用顺序表建立二叉树

使用通用队列存储二叉树时,我们用到了

 /**                                            
  * the values of the nodes.                    
  */                                            
 char[] valuesArray;                            
 /**                                            
  * the indices  in the complete binary tree.   
  */                                            
 int[] indicesArray;                            

分别来存储二叉树节点和整数的值,使用存储对象的队列来提高代码的复用性。那我们能否利用利用这两个数组进行二叉树的创建?
我们之前提到了采用下标和数据相互对应的顺序表来模拟二叉树,这里的下标之间刚好就满足我们需要的2 * i + 1或者2 * i + 2的左右儿子关系。如下图所示: 在这里插入图片描述
我们可以发现:一个节点的孩子节点只能在它对应顺序表的后面找到,对于某一节点来说,它的孩子节点在其后,它孩子节点的孩子节点也在其后,但是我们可以根据

1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
   2.2i+1<n,左孩子序号:2i+12i+1>=n否则无左孩子
   3.2i+2<n,右孩子序号:2i+22i+2>=n否则无右孩子

来判断它的直接相连的节点。
于是我们的思路可以得出:从树的第一个非根分支结点开始,逐层向下进行遍历,遍历过程中枚举他的全部长辈,直到枚举出父级,并进行连接。

  /**                                                                                        
   * ********************                                                                    
   * The second constructor. The parameters must be correct since no validity                
   * check is undertaken.                                                                    
   *                                                                                         
   * @param paraDataArray    The array for data.                                             
   * @param paraIndicesArray The array for indices.                                          
   *                         ********************                                            
   */                                                                                        
  public BinaryCharTree(char[] paraDataArray, int[] paraIndicesArray) {                      
      // Step 1. Use a sequential list to store all nodes.                                   
      int tempNumNodes = paraDataArray.length;                                               
      BinaryCharTree[] tempAllNodes = new BinaryCharTree[tempNumNodes];                      
      for (int i = 0; i < tempNumNodes; i++) {                                               
          tempAllNodes[i] = new BinaryCharTree(paraDataArray[i]);                            
      } // Of for i                                                                          
                                                                                             
      // Step 2. Link these nodes.                                                           
      for (int i = 1; i < tempNumNodes; i++) {                                               
          for (int j = 0; j < i; j++) {                                                      
              System.out.println("indices " + paraIndicesArray[j] + " vs. " + paraIndicesArra
              if (paraIndicesArray[i] == paraIndicesArray[j] * 2 + 1) {                      
                  tempAllNodes[j].leftChild = tempAllNodes[i];                               
                  System.out.println("Linking " + j + " with " + i);                         
                  break;                                                                     
              } else if (paraIndicesArray[i] == paraIndicesArray[j] * 2 + 2) {               
                  tempAllNodes[j].rightChild = tempAllNodes[i];                              
                  System.out.println("Linking " + j + " with " + i);                         
                  break;                                                                     
              } // Of if                                                                     
          } // Of for j                                                                      
      } // Of for i                                                                          
                                                                                             
      //Step 3. The root is the first node.                                                  
      value = tempAllNodes[0].value;                                                         
      leftChild = tempAllNodes[0].leftChild;                                                 
      rightChild = tempAllNodes[0].rightChild;                                               
  }// Of  the second constructor                                                             

首先需要把链式树的结点存起来,一个节点的孩子节点在顺序表对应的位置必然在其后面,反过来说,一个节点的父节点在顺序表中的位置必然在其前面,于是可以得出双重循环。

  for (int i = 1; i < tempNumNodes; i++) {      

是针对每一个节点

  for (int j = 0; j < i; j++) {     

是paraIndicesArray[i]对应节点的父类节点(包括直接和间接),其次判断是否为直接父类,如果是,则相连接。
运行结果

indices 0 vs. 1
Linking 0 with 1
indices 0 vs. 2
Linking 0 with 2
indices 0 vs. 4
indices 1 vs. 4
Linking 1 with 3
indices 0 vs. 5
indices 1 vs. 5
indices 2 vs. 5
Linking 2 with 4
indices 0 vs. 12
indices 1 vs. 12
indices 2 vs. 12
indices 4 vs. 12
indices 5 vs. 12
Linking 4 with 5

中序遍历

递归实现时,是函数自己调用自己,一层层的嵌套下去,操作系统/虚拟机自动帮我们用 栈 来保存了每个调用的函数,现在我们需要自己模拟这样的调用过程。
递归的调用过程是这样的:

dfs(root.left)
	dfs(root.left)
		dfs(root.left)null返回
		打印节点
		dfs(root.right)
			dfs(root.left)
				dfs(root.left)
				........

递归的调用过程是不断往左边走,当左边走不下去了,就打印节点,并转向右边,然后右边继续这个过程。
我们在迭代实现时,就可以用栈来模拟上面的调用过程。
在这里插入图片描述
在这里插入图片描述
我们不断地往左子树方向走,每走一次就将当前节点保存到栈中,如果当tempNode为空时,说明左边走到头了,从栈中弹出节点并保存,然后转向右边节点,继续上面整个过程。

前序遍历

首先我们想要打印根节点的数据,此时Stack里面的内容为空,所以我们优先将头结点加入Stack,然后打印。
之后我们应该先打印左子树,然后右子树。所以先加入Stack的就是右子树,然后左子树。
在这里插入图片描述

后序遍历

先序遍历是中左右,后序遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图:

在这里插入图片描述

 /**
     * postOrder visit with a stack.
     */
    public void postOrderVisitWithStack() {
        ObjectStack tempStack = new ObjectStack();
        BinaryCharTree tempNode = this;
        tempStack.push(tempNode);
        while(!tempStack.isEmpty())
        {
            tempNode=(BinaryCharTree) tempStack.pop();
            System.out.println(" "+tempNode.value+" ");
            if(tempNode.leftChild!=null)
            {
                tempStack.push(tempNode.leftChild);
            }
            if(tempNode.rightChild!=null)
            {
                tempStack.push(tempNode.rightChild);
            }


        }

    }//of postOrderVisitWithStack

在这里我们可以用一个数组来存放出栈的结果,最后反转顺序即可得到后序遍历结果。

/**
	 *********************
	 * Post-order visit with stack.
	 *********************
	 */
	public void postOrderVisitWithStack() {
		ObjectStack tempStack = new ObjectStack();
		BinaryCharTree tempNode = this;
		ObjectStack tempOutputStack = new ObjectStack();
		
		while (!tempStack.isEmpty() || tempNode != null) {
			if (tempNode != null) {
				//Store for output.
				tempOutputStack.push(new Character(tempNode.value));
				tempStack.push(tempNode);
				tempNode = tempNode.rightChild;
			} else {
				tempNode = (BinaryCharTree) tempStack.pop();
				tempNode = tempNode.leftChild;
			} // Of if
		} // Of while
		
		//Now reverse output.
		while (!tempOutputStack.isEmpty()) {
			System.out.print("" + tempOutputStack.pop() + " ");
		}//Of while
	}// Of postOrderVisitWithStack

这里采用的是中右左的入栈方式,同时记录下结点对应的值,最后进行反转。

二.代码展示

 /**
     * inOrder  visit with a stack.
     */
    public void inOrderVisitWithStack() {
        ObjectStack tempStack = new ObjectStack();
        BinaryCharTree tempNode = this;
        while (!tempStack.isEmpty() || tempNode != null) {
            if (tempNode != null) {
                tempStack.push(tempNode);
                tempNode = tempNode.leftChild;
            } else {
                tempNode = (BinaryCharTree) tempStack.pop();
                System.out.println(" " + tempNode.value + " ");
                tempNode = tempNode.rightChild;

            }//of if
        }//of while


    }//of inOrderVisitWithStack

    /**
     * preOrder visit with a stack.
     */
    public void preOrderVisitWithStack() {
        ObjectStack tempStack = new ObjectStack();
        BinaryCharTree tempNode = this;
        tempStack.push(tempNode);
        while(!tempStack.isEmpty())
        {
            tempNode=(BinaryCharTree) tempStack.pop();
            System.out.println(" "+tempNode.value+" ");
            if(tempNode.rightChild!=null)
            {
                tempStack.push(tempNode.rightChild);
            }
            if(tempNode.leftChild!=null)
            {
                tempStack.push(tempNode.leftChild);
            }

        }

    }//of preOrderVisitWithStack

    /**
     * postOrder visit with a stack.
     */
    public void postOrderVisitWithStack() {
        ObjectStack tempStack = new ObjectStack();
        BinaryCharTree tempNode = this;
        ObjectStack tempOutputStack = new ObjectStack();

        while (!tempStack.isEmpty() || tempNode != null) {
            if (tempNode != null) {
                //Store for output.
                tempOutputStack.push(new Character(tempNode.value));
                tempStack.push(tempNode);
                tempNode = tempNode.rightChild;
            } else {
                tempNode = (BinaryCharTree) tempStack.pop();
                tempNode = tempNode.leftChild;
            } // Of if
        } // Of while

        //Now reverse output.
        while (!tempOutputStack.isEmpty()) {
            System.out.print("" + tempOutputStack.pop() + " ");
        }//Of while

    }//of postOrderVisitWithStack

运行结果

Preorder visit:
A
B
D
C
E
F

In-order visit:
B
D
A
E
F
C

Post-order visit:
D
B
F
E
C
A

In-order visit with a stack:
 B 
 D 
 A 
 E 
 F 
 C 

pre-order visit with a stack:
 A 
 B 
 D 
 C 
 E 
 F 

post-order visit with a stack:
D B F E C A 

总结

二叉树的遍历有递归和迭代两种,使用递归是使用系统自带的栈来进行操作,只需要把

 if (leftChild != null) {
            leftChild.inOrderVisit();
        }//of if
        System.out.println("" + value + "");
        if (rightChild != null) {
            rightChild.inOrderVisit();
        }//of if

这几句代码调换顺序即可得到结果。在采用迭代方法的时候,我们需要自己模拟这样的调用过程。
在后序遍历的过程中,很巧妙的利用到了前序遍历的特点,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中。今天再回头看二叉树的遍历,又获得了不少新的认知,自己用迭代模拟栈的实现,以及前后序遍历的相关性都会在之后的学习中有着影响。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值