总结树的各种建立过程(1)

总所周知,树是很常见的数据结构。在一些需求中,根据已知条件建立这棵树往往是解这道题的第一步。一般来说,以建立二叉树的需求居多,偶尔需要我们建立三叉树。目前想总结一下建树的方法。

二叉树的数据结构如下:

struct TreeNode{
    char data;
    TreeNode* lchild;
    TreeNode* rchild;
    TreeNode(char c):data(c),lchild(NULL),rchild(NULL){};
};

1.根据树的遍历来建立树。

先说结论:
(1)给定中序遍历和后序遍历,可以唯一地确定一棵二叉树。
(2)给定先序遍历和中序遍历,也可以唯一地确定一棵二叉树。
(3)给定先序遍历和后序遍历,无法唯一地确定一棵二叉树。


无论是(1)还是(2)都需要我们封装一个函数如下:

int findInStr(string str,char c){   //在str中查找c
    for(int i=0;i<str.length();i++){
        if(str[i] == c){
            return i;
        }
    }
    return -1;
}

在字符串str中查找c字符的下标。


如何根据先序遍历和中序遍历来建立一棵二叉树?
答:采用递归。
重要思路:
左(右)子树的先序遍历和中序遍历均为整棵树的先序遍历和中序遍历字符串的一个子串,找到规律就可以递归。

TreeNode* BuildTreeByInAndMid(string str1,string str2){
	/*
		str1  -->  先序遍历字符串
		str2  -->  中序遍历字符串
		返回建立的这棵树
	*/
    if(str1.size() == 0){   //输入的先序遍历是空,建立空树
        return NULL;   //空树
    }else{
        int length = str1.length(); //先序遍历的长度,也就是树结点的个数。
        char currentRoot = str1[0];
        TreeNode* root = new TreeNode(currentRoot);    //根节点

        int midIndex = findInStr(str2,currentRoot);//在中序遍历中找到根节点的位置。
        if(midIndex>0){   //建立左子树
            root->lchild = BuildTreeByInAndMid(str1.substr(1,midIndex),str2.substr(0,midIndex));
        }
        if(length - 1 - midIndex >= 0){ //建立右子树
            root->rchild = BuildTreeByInAndMid(str1.substr(midIndex+1,length-1-midIndex),str2.substr(midIndex+1,length-1-midIndex));
        }
        return root;
    }
}

这里偷了个懒,string.substr(int start,int length)返回的是string这个字符串从start开始连续length个字符组成的连续子串。


如何根据中序遍历和后序遍历来建立一棵二叉树?
答:采用递归。道理和上面相同。

TreeNode* BuildTreeByMidAndPost(string str1,string str2){
    /*
     * str1-->后序遍历
     * str2-->中序遍历
     * 返回建立的这棵树
     */
    if(str1.length() == 0){
        return NULL;
    }else{
        int length = str1.length();
        char currentRoot = str1[length-1];
        TreeNode* root = new TreeNode(currentRoot);

        int midIndex = findInStr(str2,currentRoot);
        if(midIndex > 0){  //有左子树
            root -> lchild = BuildTreeByMidAndPost(str1.substr(0,midIndex),str2.substr(0,midIndex));
        }
        if(length-midIndex-1>=0){
            root -> rchild = BuildTreeByMidAndPost(str1.substr(midIndex,length-midIndex-1),str2.substr(midIndex+1,length-midIndex-1));
        }
        return root;
    }

}

**

2.给出二叉树的完整先序遍历可以确定一棵二叉树,其中空树用‘#’表示。

**
例如:
输入abc##de#g##f###,以此字符串为先序遍历,可以建立唯一的一棵二叉树。
代码如下:

TreeNode* BuildTreeByInOrder(int& position,string str){
    char c = str[position++];

    if(c == '#'){
        return NULL;
    }else{
        TreeNode* root = new TreeNode(c);
        root -> lchild = BuildTreeByInOrder(position,str);
        root -> rchild = BuildTreeByInOrder(position,str);
        return root;
    }
}

在调用的时候

int main(){
    string str1;
    cin>>str1;
    int position = 0;
    TreeNode* mytree = BuildTreeByInOrder(position,str1);
    PostOrderTraverse(mytree);
    return 0;
}

position是整型引用,不是整型变量。


3.层次遍历建立三叉树

三叉树的数据结构如下:

struct TreeNode{
    int data;
    TreeNode* leftChild;
    TreeNode* middleChild;
    TreeNode* rightChild;
    TreeNode(int c):data(c),leftChild(NULL),middleChild(NULL),rightChild(NULL){};
};

例如有一道题如下:
总结树的各种建立过程(1)_第1张图片

第一行输出接下来要输出的行数。下面每行按照:
根节点 左孩子 中孩子 右孩子的顺序输入。
-1表示为空树,大于100的结点是分支结点,小于100的结点是叶子节点。
按照层次遍历的顺序输入。请建立这棵三叉树。
思路:必然要用到队列。

int main(){
    int inputRows;
    cin>>inputRows;  //输入要输入的行数。
    int data,leftData,midData,rightData;
    bool first = true;  //是否是首次输入
    queue<TreeNode*> q;  //用于层序遍历
    cin>>data;     //输入根节点
    TreeNode* root = new TreeNode(data);  //建立根节点
    TreeNode* tmp;
    q.push(root);
    while(!q.empty()){   //队列非空,
        tmp = q.front();
        q.pop();

        if(first == true){
            cin>>leftData>>midData>>rightData;
            first = false;
        }else{
            cin>>data>>leftData>>midData>>rightData;
        }
        //插左子树
        if(leftData>=0){
            TreeNode* addleftNode = new TreeNode(leftData);
            tmp->leftChild = addleftNode;
            if(leftData>100){
                q.push(addleftNode);  //分支节点入队列
            }
        }

        //插中子树
        if(midData>=0){
            TreeNode* addmidNode = new TreeNode(midData);
            tmp->middleChild = addmidNode;
            if(midData>100){
                q.push(addmidNode);   //分支节点入队列
            }
        }

        //插右子树
        if(rightData>=0){
            TreeNode* addrightNode = new TreeNode(rightData);
            tmp->rightChild = addrightNode;
            if(rightData>100){
                q.push(addrightNode);  //分支节点入队列
            }
        }
    }
    
    return 0;
}

这个题的关键就是利用队列,将根节点在内的分支节点(即大于等于100的结点)加入到队列中。当队列是空时,就建完了这棵树。
关于各种建树的过程,还有很多不同的需求。今后会继续补充。

你可能感兴趣的:(数据结构,c++)