《算法竞赛入门经典》6-7 Trees on the level UVA122——二叉树的层次遍历(宽度优先遍历BFS)

Trees on the level       UVA - 122 

Trees are fundamental in many branches of computer science (Pun definitely intended). Current state- of-the art parallel computers such as Thinking Machines’ CM-5 are based on fat trees. Quad- and octal-trees are fundamental to many algorithms in computer graphics.

This problem involves building and traversing binary trees.

Given a sequence of binary trees, you are to write a pro- gram that prints a level-order traversal of each tree. In this problem each node of a binary tree contains a positive integer and all binary trees have have fewer than 256 nodes.

In a level-order traversal of a tree, the data in all nodes at a given level are printed in left-to-right order and all nodes at level k are printed before all nodes at level k + 1.

For example, a level order traversal of the tree on the right is: 5,4,8,11,13,4,7,2,1.

In this problem a binary tree is specified by a sequence
of pairs ‘(n,s)’ where n is the value at the node whose path
from the root is given by the string s. A path is given be
a sequence of ‘L’s and ‘R’s where ‘L’ indicates a left branch and ‘R’ indicates a right branch. In the tree diagrammed above, the node containing 13 is specified by (13,RL), and the node containing 2 is specified by (2,LLR). The root node is specified by (5,) where the empty string indicates the path from the root to itself. A binary tree is considered to be completely specified if every node on all root-to-node paths in the tree is given a value exactly once.

Input

The input is a sequence of binary trees specified as described above. Each tree in a sequence consists of several pairs ‘(n,s)’ as described above separated by whitespace. The last entry in each tree is ‘()’. No whitespace appears between left and right parentheses.

All nodes contain a positive integer. Every tree in the input will consist of at least one node and no more than 256 nodes. Input is terminated by end-of-file.

Output

For each completely specified binary tree in the input file, the level order traversal of that tree should be printed. If a tree is not completely specified, i.e., some node in the tree is NOT given a value or a node is given a value more than once, then the string ‘not complete’ should be printed.

Sample Input

(11,LL) (7,LLL) (8,R)
(5,) (4,L) (13,RL) (2,LLR) (1,RRR) (4,RR) ()
(3,L) (4,R) ()

Sample Output

5 4 8 11 13 4 7 2 1
not complete

解题思路:

首先要解决读数据问题,根据题意,当输入为“()”时,结束该组数据读入,当没有字符串时,整个输入结束。因此可以专门编写一个read_input()函数,类型设置为bool型,遇到第一种情况时返回true,遇到第二种情况返回false,主程序中只要发现read_input返回false时就break,结束整个大循环。

接下来要建立二叉树,首先为二叉树编写一个结构体,然后根据字符串的输入情况来建树,如果是‘L’就往左走,走不动时建一颗新树,同样的方法处理右子树,最后读入结点值。由于输入可能有误,因此用一个全局变量failed来记录是否有输入错误的情况出现,如果在建树过程中发现该结点已经被赋过值,那么全局变量failed变为true。

最后开始BFS找结点值,此时可能出现有结点没有结点值的情况,因此要把bfs定义为bool型,只要出现这种非法情况,返回false。最后便不难根据情况进行输出了。

a)        编写read_input函数,专门用来读取数据。函数返回类型为bool,输入不合法时返回false。依题意,如果输入只有(),表示input终止,跳出循环并返回true.

//输入和主程序
bool read_input(){
    failed=false;
    remove_tree(root);
    root=newnode();                 //创建根结点
    for (; ; ) {
        if(scanf("%s",s)!=1)return false;//整个输入结束
        if(!strcmp(s, "()")) break;//读到结束标志
        int v;
        sscanf(&s[1], "%d",&v);     //读入结点值
        addnode(v,strchr(s,',')+1); //查找逗号,然后插入结点
        
    }
    return true;
}

可以把任意“指向字符的指针”看成是字符串,从该位置开始,知道字符'\0'。函数strchr(s,',')返回字符串s中从左往右第一个字符','的指针。

sscanf 读取格式化的字符串中的数据。以固定字符串为输入源sscanf函数原型为int sscanf(const char *str, const char *format, ...)。将参数str的字符串根据参数format字符串来转换并格式化数据,转换后的结果存于对应的参数内。具体功能如下:

(1)根据格式从字符串中提取数据。如从字符串中取出整数、浮点数和字符串等。

(2)取指定长度的字符串

(3)取到指定字符为止的字符串

(4)取仅包含指定字符集的字符串

(5)取到指定字符集为止的字符串

b)        编写名为Node的结构体。结构体中包含input中括号左侧的结值、判断结点是否被赋值的bool类型的值,和类型为Node *的左右子结点。由于二叉树是递归定义的,其左右子结点类型都是“指向结类型的指针”,因此左右子结点的类型都是Node *.

//二叉树的定义和操作
struct Node{
    bool have_value;                //是否被赋值过
    int v;//结点值
    Node *left,*right;
    Node():have_value(false),left(NULL),right(NULL){}   //构造函数
};
Node* root;             //二叉树的根结点
//申请新结点:每次需要一个新的Node时,都需要用new运算符申请内存,并执行构造函数
Node* newnode(){
    return new Node();
}

c)        建立全局变量failed,记录是否有从根到某个叶结的路径上有的结没有在输入中给出或值给出超过一次的情况

d)        建立函数addnode:按照移动序列走,目标不存在时调用newnode函数来创造新结点,判断结点是否已被赋值(如果已经被赋过值,将failed值改为true),并将结点值赋给对应的结点.

void addnode(int v,char* s){
    unsigned long n=strlen(s);
    Node* u=root;       //从根结点开始往下走
    for (int i=0; i<n; i++) {
        if (s[i]=='L') {
            if(u->left==NULL)u->left=newnode();//左结点不存在,那就建立新结点
            u=u->left; //往左走
        }
        else if(s[i]=='R'){
            if(u->right==NULL)u->right=newnode();
            u=u->right;
        }                   //忽略其他情况,即使最后那个多余的右括号
    }
    if(u->have_value) failed=true;  //已经赋过值,表明输入有误
    u->v=v;
    u->have_value=true;         //别忘记做标记
}

e)        宽度优先遍历(Breadth-First Search)bfs找结点,使用队列:初始时只有一个根结点,然后每次取出一个结点,把它的左右子结点(如果存在)放进队列中,用vector保存结点的值.

//按照层次顺序遍历这棵树。此处实用队列来完成任务
bool bfs(vector<int>& ans){
    queue<Node*> q;
    ans.clear();
    q.push(root);                    //初始时只有一个根结点
    while (!q.empty()) {
        Node* u=q.front();q.pop();
        if(!u->have_value)  return false;//有结点没有被赋值过,比噢名输入有误
        ans.push_back(u->v);        //增加到输出序列尾部
        if(u->left!=NULL)q.push(u->left);//把左子结点(如果有)放到队列中
        if(u->right!=NULL)q.push(u->right);//把右子结点放进队列
    }
    return true;
}

f)        如果程序动态申请内存,请注意内存泄漏。释放二叉树。在“root=newnode()"之前加一行“remove_tree(tree)":

//释放二叉树,对内存泄漏保持警惕
void remove_tree(Node* u){
    if(u==NULL) return; //提前判断比较稳妥
    remove_tree(u->left);//递归释放左子树的空间
    remove_tree(u->right);//递归释放右子树的空间
    delete u;             //调用u的析构函数并释放u结点本身的内存
}

 

 

#include <algorithm>
#include <cstdio>
#include <string>
#include <iostream>
#include <list>//双向链表
#include <stack>//栈
#include <queue>//队列
using namespace std;
const int maxn=20;
char s[maxn];                       //保存读入结点
bool failed;
//二叉树的定义和操作
struct Node{
    bool have_value;                //是否被赋值过
    int v;//结点值
    Node *left,*right;
    Node():have_value(false),left(NULL),right(NULL){}   //构造函数
};
Node* root;             //二叉树的根结点
//申请新结点:每次需要一个新的Node时,都需要用new运算符申请内存,并执行构造函数
Node* newnode(){
    return new Node();
}

//释放二叉树,对内存泄漏保持警惕
void remove_tree(Node* u){
    if(u==NULL) return; //提前判断比较稳妥
    remove_tree(u->left);//递归释放左子树的空间
    remove_tree(u->right);//递归释放右子树的空间
    delete u;             //调用u的析构函数并释放u结点本身的内存
}


//按照移动序列行走,目标不存在时用newnode来创建结点
void addnode(int v,char* s){
    unsigned long n=strlen(s);
    Node* u=root;       //从根结点开始往下走
    for (int i=0; i<n; i++) {
        if (s[i]=='L') {
            if(u->left==NULL)u->left=newnode();//左结点不存在,那就建立新结点
            u=u->left; //往左走
        }
        else if(s[i]=='R'){
            if(u->right==NULL)u->right=newnode();
            u=u->right;
        }                   //忽略其他情况,即使最后那个多余的右括号
    }
    if(u->have_value) failed=true;  //已经赋过值,表明输入有误
    u->v=v;
    u->have_value=true;         //别忘记做标记
}

//输入和主程序
bool read_input(){
    failed=false;
    remove_tree(root);
    root=newnode();                 //创建根结点
    for (; ; ) {
        if(scanf("%s",s)!=1)return false;//整个输入结束
        if(!strcmp(s, "()")) break;//读到结束标志
        int v;
        sscanf(&s[1], "%d",&v);     //读入结点值
        addnode(v,strchr(s,',')+1); //查找逗号,然后插入结点
    }
    return true;
}

//按照层次顺序遍历这棵树。此处实用队列来完成任务
bool bfs(vector<int>& ans){
    queue<Node*> q;
    ans.clear();
    q.push(root);                    //初始时只有一个根结点
    while (!q.empty()) {
        Node* u=q.front();q.pop();
        if(!u->have_value)  return false;//有结点没有被赋值过,比噢名输入有误
        ans.push_back(u->v);        //增加到输出序列尾部
        if(u->left!=NULL)q.push(u->left);//把左子结点(如果有)放到队列中
        if(u->right!=NULL)q.push(u->right);//把右子结点放进队列
    }
    return true;
}
int main(int argc, const char * argv[]) {
    while (1) {
        if(!read_input())break;
        vector<int> ans;
        if (!failed&&bfs(ans)) {
            int len=(int)ans.size();
            for(int i=0;i<len;i++)
                printf("%d%c",ans[i],i==len-1?'\n':' ');
        }
        else printf("not complete");
    }
    return 0;
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章