zoukankan      html  css  js  c++  java
  • 二叉树(1)已知某2种排序方式,创建这个二叉树并按第3种方式排序

    1.关于指针和引用的说明

       数据结构中建立二叉树子函数,根结点为什么用双重指针,即指针的指针。因为树的结点要用指针描述。如果只用指针,作形参传给建立结点的函数,这个指针值传给了函数栈中的内存,函数返回后,函数栈销毁,不能获得结点。而用指针的指针,函数内修改了这个双重指针指向的值(即结点指针),在函数外也能获得结点。这swap()函数要用指针而不能用值做参数一样。只是这里的值本身就是个指针,所以要用指针的指针。如下:
     typedef struct BinaryTreeNode
    {
        char data;
        BinaryTreeNode * leftChild;
        BinaryTreeNode * rightChild;
    }Node;
    void Create(Node**T){......}
    int main(){
    	 Node* T;
    	 Create(&T);
    }

       如果Create的参数不是指针的引用(等同双指针),main中 Create(T)是把指针T指向的地址传进去了。注意,只是地址.然后你在Create函数内部申请内存时, 把这个地址给改变了, 但是因为你传的是一个地址, 这个地址本身跟T无关,T仅仅是指向了这个地址而以. 所以Create(T)之后, T还是指向原来的地址,并未改变, 后面的操作当然就是崩溃了(因为T未初始化,是一个野指针)。
    传值: 函数内部修改不会对原值内容进行改变. 传址: 函数内部修改会影响原值. 可能我们认为值的指针就是传址了.如果是传值, 函数入栈的时候就是把这个变量的值压栈,如果是传址, 就是把指针压栈,但压栈的时候,本身实际也是写一个数值而以.你传的是指针的情况下, 如果修改指针指向的内容, 那么函数外部会同步修改.但是你传入的是指针, 但是又修改的是指针本身, 而不是其内容, 那么你这就相当于传值.当你要修改指针本身的时候, 你可以这样理解.

    //比如有:
    void Creat(BTNode *pRoot);
    //可以修改成以下形式
    typedef BTNode* PNODE;
    void Creat(PNODE pRoot);
    //这时你可能就看出来了, 原来这是一个传值调用.想要修改pRoot的值, 那么就要
    void Creat(PNODE &pRoot); ====还原就是 void Creat(BTNode* &pRoot);
    //或者
    void Creat(PNODE *pRoot); ====还原就是 void Creat(BTNode* *pRoot);
    注意区分传址还是传值的区分不是看参数是否是指针, 而是要看你在函数内部是如何操作参数的. 如果你操作的是参数本身, 那么就是传值.如果你是把参数当成一个地址, 操作的是那个地址上的内容,那就是传址。

    一个简单的例子:

      int a = 1;
      int b = 2;  
      int *tmp = &a;
      int *p = tmp;// 第二种情况:int *&p = tmp;(此即是指向指针的引用)
      p = &b;
      *p = 5;

      第一种情况:a=1,b=5,*p=5,tmp=1
    
      第二种情况:a=1,b=,*p=5,tmp=5. 
       这是因为指向指针的引用,不仅改变了指针所指的对象,也改变了指针本身。

    2.前序,中序,后序遍历的简单说明


        先序遍历也叫做先根遍历,前序遍历可记做左右(二叉树父结点向下先左后右)。首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树,如果二叉树为空则返回。上图所示二叉树的先序遍历结果是:ABDECF。
        已知中序遍历和后序遍历,可以确定唯一的前序遍历。
        中序遍历也叫中根遍历中序周游,可记做首先遍历左子树,然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树。注意的是:遍历左右子树时仍然采用中序遍历方法。序遍历的时间复杂度:O(n)。如果一棵二叉排序树的节点值是数值,中序遍历的结果为升序排列的数组。可以利用该性质检测一棵树是否为二叉排序数。上图所示二叉树序遍历结果:DBEAFC
        已知前序遍历和后序遍历,不能确定唯一的中序遍历。

        后序遍历(LRD)也叫做后根遍历、后序周游,可记做左右。后序遍历有递归算法和非递归算法两种。后序遍历首先遍历左子树,然后遍历右子树,最后遍历访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。即:二叉树为空则结束返回,否则:(1)后序遍历左子树(2)后序遍历右子树(3)访问根结点。如右图所示二叉树后序遍历结果:DEBFCA。     已知前序遍历和中序遍历,可以确定唯一的后序遍历。

       解释:前序或后序能够确定根节点,结合中序能够唯一确定左子树和右子树元素。而仅仅知道前序和后序时,当左右子树存在空时,无法唯一确定究竟是哪一棵树为空。即仅仅知道前序和后序,无法唯一的还原2叉树,即中序无法唯一确定。


    3.程序代码以及说明

       1.已知前序和中序排列,创建二叉树,并输出后序排列
       2.已知中序和后序排列,创建二叉树,并输出前序排列
       3.使用递归法遍历二叉树


    已知前序中序求后序:

     Node* MakeBinaryTree_PM(char* preOrder, char* midOrder,int length)//基于前序和中序遍历计算后序遍历  
     {  
         if(length==0)  
                  return NULL; 
         else  {  
                 Node* root=new Node;  
                 root->data=*preOrder;//前序第一个元素为根节点  
                 char * rootposition = strchr(midOrder,root->data);//查找根节点在排序中的位置  
                 if (rootposition == NULL)  
                      {cout <<"Wrong Input !!!"<<endl;
    			       return NULL;}  
                 else  
                     {int LeftSubTreeSize=strlen(midOrder)-strlen(rootposition);  
    		  int RightSubTreeSize=length-LeftSubTreeSize-1;  
    		  root->leftchild=MakeBinaryTree_PM(preOrder+1,midOrder,LeftSubTreeSize);  
    		  root->rightchild=MakeBinaryTree_PM(preOrder+LeftSubTreeSize+1,rootposition+1,RightSubTreeSize);  
                 }  
                 return root;
         }  
     }  

    已知中序后序求前序:

    Node* MakeBinaryTree_ML(char* midOrder,char* lastOrder,int length)//基于中序和后序遍历计算前序遍历  
     {  
         if(length==0)  
              return NULL;
         else{  
    		  Node*root=new Node;  
              root->data=lastOrder[length-1];//后序最后一个元素为根节点  
              char * rootposition = strchr(midOrder,root->data);//查找根节点在排序中的位置  
              if (rootposition == NULL)  
                  {cout <<"Wrong Input !!!"<<endl;
    	       return NULL;}  
              else { 
                  int LeftSubTreeSize=strlen(midOrder)-strlen(rootposition);
                  int RightSubTreeSize=length-LeftSubTreeSize-1;  
    	      root->leftchild=MakeBinaryTree_ML(midOrder,lastOrder,LeftSubTreeSize);
    	      root->rightchild=MakeBinaryTree_ML(rootposition+1,lastOrder+LeftSubTreeSize,RightSubTreeSize);
             }  
         return root;
         }  
     } 

    二叉树的三种递归遍历

    void MidTraverse(Node* Root)//嵌套中序遍历  
     {  
           if(Root==NULL);  
           else  
               {MidTraverse(Root->leftchild);  
                cout<<Root->data;  
               MidTraverse(Root->rightchild);}  
     } 
     void PostTraverse(Node* Root)//嵌套后序遍历  
    {  
        if (Root == NULL)  
            return;  
        PostTraverse(Root->leftchild);  
        PostTraverse(Root->rightchild);  
        cout << Root->data;  
    }
    void PreTraverse(Node* Root)//嵌套前序遍历  
     {  
        if(Root==NULL);  
        else  
           {cout<<Root->data;  
            PreTraverse(Root->leftchild);  
            PreTraverse(Root->rightchild);}  
     }  


     注意 1.必须知道中序,知道任何其他两种中的一种倘若知道,可建唯一的二叉树进而得到另外一种排序。
          2.创建二叉树的时候,使用指针的引用作为形参,即双重指针。





  • 相关阅读:
    Java的代码风格
    哪些你容易忽略的C语言基础知识
    Java基础学习笔记第二章
    Java代码性能优化总结
    Java并发编程(2):线程中断(含代码)
    C语言代码训练(一)
    数控G代码编程详解大全
    PLC编程算法
    博客转移到新地址
    一些吐槽
  • 原文地址:https://www.cnblogs.com/engineerLF/p/5393138.html
Copyright © 2011-2022 走看看