linux下GCC编译环境中二叉树遍历、C语言实现以及调试过程中段错误

最近一直在学习数据结构准备面试,昨天晚上看到了二叉树,在网上查了一些资料以后照猫画虎的写了一个二叉树遍历的程序,主要是为了消化递归构建二叉树和遍历的过程,调试过程中也发现了不少问题。

二叉树概念属性

       二叉树是由一个根节点和两棵互不相交的、分别成为根节点左子树和右子树的的二叉树组成。


图1

      图1为典型的二叉树。

二叉树的遍历方式

1.前序遍历

先访问根节点,然后访问左子树,之后是右子树。注意在左右子树中也是同样的顺序访问。

2.中序遍历

先访问左子树,然后访问根节点,最后访问右子树。注意在左右子树中也是同样的顺序访问。

3.后序遍历

先访问左子树,然后访问右子树,最后访问根节点。注意在左右子树中也是同样的顺序访问。

二叉树的构建方式

1.已知二叉树的前序遍历和中序遍历序列,通过递归构建二叉树。

2.已知二叉树的后续遍历和中序遍历序列,通过递归构建二叉树。


介绍完概念之后上代码,编译环境为ubuntu10.04,使用G++遍历CPP文件。

  1 /************************************************************
  2 *文件名:binary_tree.cpp
  3 *文件描述:该文件用于演示二叉树的遍历操作,分别为:
  4 *已知二叉树的中序遍历和后续遍历,利用递归进行二叉树的前序遍历
  5 *已知二叉树的中序遍历和前序遍历,利用递归进行二叉树的后续遍历
  6 *文件包含函数:void BinaryTreeFromMidAndPost(TREENODE &tree, 
  7 *                                           stirng mid,
  8 *                                           string post,
  9 *                                           int lm, int rm,
 10 *                                           int lp, int rp)
 11 *函数功能:该函数用于从中序遍历和后续遍历构建二叉树             
 12 *              void BinaryTreeFromMidAndPost(TREENODE &tree, 
 13 *                                           stirng mid,
 14 *                                           string pre,
 15 *                                           int lm, int rm,
 16 *                                           int lp, int rp)
 17 *函数功能:该函数用于从中序遍历和前序遍历构建二叉树
 18 *              void PreOrder(TREENODE &tree)
 19 *函数功能:该函数用于进行二叉树的前序遍历
 20 *              void MidOrder(TREENODE &tree)
 21 *函数功能:该函数用于进行二叉树的中序遍历
 22 *              void PostOrder(TREENODE &tree)
 23 *函数功能:该函数用于进行二叉树的后续遍历
 24 *
 25 *文件创建日期:2011-8-29
 26 ************************************************************/
 27 
 28 #include <iostream>
 29 #include <string>
 30 using namespace std;
 31 
 32 typedef char  DATA_TYPE;
 33 
 34 //构建二叉树结构体
 35 typedef struct tagTreeNode{
 36     DATA_TYPE  data;
 37     struct tagTreeNode *pLChild;
 38     struct tagTreeNode *pRChild;
 39 }TREENODE;
 40 
 41 /***********************************************************
 42 *函数名称:BinaryTreeFromMidAndPost
 43 *函数功能:该函数用于根据二叉树的中序遍历和后续遍历构建
 44 *二叉树,使用的是递归的方法。
 45 *入口参数:TREENODE &tree   所要构造的二叉树对象
 46            string Mid       中序遍历顺序字符串数组
 47            string Post      后续遍历顺序字符串数组
 48            int lm,rm        中序字符串数组的左右边界
 49            int lp,rp        后序字符床数组的左右边界
 50 *创建日期:2011-8-30
 51 ***********************************************************/
 52 void BinaryTreeFromMidAndPost(TREENODE **pTree,
 53                               string Mid,
 54                               string Post,
 55                               int lm,int rm,
 56                               int lp,int rp)
                                                                                             
 57 {
 58     /*开辟内存空间构造节点*/
 59     *pTree = new TREENODE;
 60 
 61     TREENODE *pTemp = *pTree;
 62     pTemp->data = Post[rp];
 63     pTemp->pLChild = NULL;
 64     pTemp->pRChild = NULL;
 65 
 66     /*寻找根节点*/
 67     int pos = lm;
 68     while(Mid[pos] != Post[rp])
 69         pos++;
 70 
 71     int lChildLen = pos - lm;    //左字数节点数
 72 
 73     if(pos > lm)
 74     {
 75         BinaryTreeFromMidAndPost(&(pTemp->pLChild),
 76                                  Mid,Post,
 77                                  lm,pos-1,
 78                                  lp,lp + lChildLen - 1);
 79     }
 80 
 81     if(pos < rm)
 82     {
 83         BinaryTreeFromMidAndPost(&(pTemp->pRChild),
 84                                  Mid,Post,
 85                                  pos+1,rm,
 86                                  lp + lChildLen,rp - 1);
 87     }
 88 }
 89 
 90 
 91 /***********************************************************
 92 *函数名称:BinaryTreeFromMidAndPre
 93 *函数功能:该函数用于根据二叉树的中序遍历和前序遍历构建
 94 *二叉树,使用的是递归的方法。
 95 *入口参数:TREENODE &tree   所要构造的二叉树对象
 96            string Mid       中序遍历顺序字符串数组
 97            string Pre      后续遍历顺序字符串数组
 98            int lm,rm        中序字符串数组的左右边界
 99            int lp,rp        后序字符床数组的左右边界
100 *创建日期:2011-8-30
101 ***********************************************************/
102 void BinaryTreeFromMidAndPre(TREENODE **pTree,
103                               string Mid,
104                               string Pre,
105                               int lm,int rm,
106                               int lp,int rp)
107 {
108     /*开辟内存空间构造节点*/
109     *pTree = new TREENODE;
110 
111     TREENODE *pTemp = *pTree;
112     pTemp->data = Pre[lp];
113     pTemp->pLChild = NULL;
114     pTemp->pRChild = NULL;
115 
116     /*寻找根节点*/
117     int pos = lm;
118     while(Mid[pos] != Pre[lp])
119         pos++;
120 
121     int lChildLen = pos - lm;    //左字数节点数
122 
123     if(pos > lm)
124     {
125         BinaryTreeFromMidAndPre(&(pTemp->pLChild),
126                                  Mid,Pre,
127                                  lm,pos-1,
128                                  lp + 1,lp + lChildLen);
129     }
130 
131     if(pos < rm)
132     {
133         BinaryTreeFromMidAndPre(&(pTemp->pRChild),
134                                  Mid,Pre,
135                                  pos+1,rm,
136                                  lp + lChildLen + 1,rp);
137     }
138 }
139 
140 /******************************************************************************
141 *函数名称:PreOrder
142 *函数功能:递归实现二叉树的前序遍历
143 *入口参数:TREENODE &tree
144 *创建日期:2011-8-30
145 ******************************************************************************/
146 void PreOrder(TREENODE *pTree)
147 {
148     if(pTree != NULL)
149     {
150     cout << pTree->data << ' ';    //访问根节点
151     PreOrder(pTree->pLChild);       //访问左子树
152     PreOrder(pTree->pRChild);       //访问右子树
121     int lChildLen = pos - lm;    //左字数节点数
122 
123     if(pos > lm)
124     {
125         BinaryTreeFromMidAndPre(&(pTemp->pLChild),
126                                  Mid,Pre,
127                                  lm,pos-1,
128                                  lp + 1,lp + lChildLen);
129     }
130 
131     if(pos < rm)
132     {
133         BinaryTreeFromMidAndPre(&(pTemp->pRChild),
134                                  Mid,Pre,
135                                  pos+1,rm,
136                                  lp + lChildLen + 1,rp);
137     }
138 }
139 
140 /******************************************************************************
141 *函数名称:PreOrder
142 *函数功能:递归实现二叉树的前序遍历
143 *入口参数:TREENODE &tree
144 *创建日期:2011-8-30
145 ******************************************************************************/
146 void PreOrder(TREENODE *pTree)
147 {
148     if(pTree != NULL)
149     {
150     cout << pTree->data << ' ';    //访问根节点
151     PreOrder(pTree->pLChild);       //访问左子树
152     PreOrder(pTree->pRChild);       //访问右子树
        }
154 }
155 
156 
157 /******************************************************************************
158 *函数名称:MidOrder
159 *函数功能:递归实现二叉树的前序遍历
160 *入口参数:TREENODE &tree
161 *创建日期:2011-8-30
162 ******************************************************************************/
163 void MidOrder(TREENODE *pTree)
164 {
165     if(pTree != NULL)
166     {
167     MidOrder(pTree->pLChild);       //访问左子树
168     cout << pTree->data << ' ';     //访问根节点
169     MidOrder(pTree->pRChild);       //访问右子树
170     }
171 }
172 
173 
174 /******************************************************************************
175 *函数名称:PreOrder
176 *函数功能:递归实现二叉树的前序遍历
177 *入口参数:TREENODE &tree
178 *创建日期:2011-8-30
179 ******************************************************************************/
180 void PostOrder(TREENODE *pTree)
181 {
182     if(pTree != NULL)
183     {
184     PostOrder(pTree->pLChild);       //访问左子树
185     PostOrder(pTree->pRChild);       //访问右子树
186     cout << pTree->data << ' ';    //访问根节点
187     }
188 }
189 
190 
191 /******************************************************************************
192 *函数名称:ReleaseBinaryTree
193 *函数功能:递归实现二叉树对象的销毁
194 *入口参数:TREENODE *pTree
195 *创建日期:2011-8-31
196 ******************************************************************************/
197 void ReleaseBinaryTree(TREENODE **pTree)
198 {
199     TREENODE* pTemp = *pTree;
200 
201     if(pTemp != NULL)
202     {
203         delete pTemp;
204         ReleaseBinaryTree(&(pTemp->pLChild));       //访问左子树
205         ReleaseBinaryTree(&(pTemp->pRChild));       //访问右子树
206     }
207 }
208 
209 
210 int main(void)
211 {
212     //三种遍历顺序
213     string MidArr = "DGBAECHF";
214     string PreArr = "ABDGCEFH";
215     string PostArr = "GDBEHFCA";
216 

217     TREENODE *pRoot = NULL;
218 //  TREENODE *pRoot1 = NULL;
219 
220     cout << "已知后序遍历与中序遍历顺序,构建二叉树,求前序序列" << endl;
221 
222     /*递归构建二叉树*/
223     BinaryTreeFromMidAndPost(&pRoot, MidArr, PostArr, 0, MidArr.size()-1,
224                      0, PostArr.size() - 1);
225 
226     cout << "前序遍历为:" << endl;
227     PreOrder(pRoot);
228     cout << endl;
229 
230     cout << "中序遍历为:" << endl;
231     MidOrder(pRoot);
232     cout << endl;
233 
234     cout << "后序遍历为:" << endl;
235     PostOrder(pRoot);
236     cout << endl;
237 
238     /*销毁二叉树对象*/
239     ReleaseBinaryTree(&pRoot);
240 
241     cout << "已知前序遍历与中序遍历顺序,构建二叉树,求后序序列" << endl;
242 
243     /*递归构建二叉树*/
244     BinaryTreeFromMidAndPre(&pRoot, MidArr, PreArr, 0, MidArr.size()-1,
245                      0, PreArr.size() - 1);
246 
247     cout << "前序遍历为:" << endl;
248     PreOrder(pRoot);
249     cout << endl;
250 
251     cout << "中序遍历为:" << endl;
252     MidOrder(pRoot);
253     cout << endl;
254 
255     cout << "后序遍历为:" << endl;
256     PostOrder(pRoot);
257     cout << endl;
258 
259     return 0;
260 }
      这里涉及到C++中指针传递内存的问题。由于递归的原因,在函数BinaryTreeFromMidAndPre和BinaryTreeFromMidAndPost中我们将创建二叉树的根节点使用函数参数传入。但是在初次调试的时候执行完构建函数后,pRoot指针仍然为NULL。说明构建了二叉树之后函数并没有传回根节点的指针,这是为什么呢?
        先看下面的程序:

void GetMemory(char *p, int num)//zbf: 感觉非常隐蔽,设计错 误
{
    p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(str, 100);    // str 仍然为 NULL 
    strcpy(str, "hello");   // 运行错误
}
       Test 函 数的语句GetMemory(str, 200) 并没有使str 获得期望的内存,str 依旧是NULL。
    毛病出在函数GetMemory 中。 编译器总是要为函数的每个参数制作临时副本,指针参数p 的副本是 _p编译器使 _p = p 。如果函数体内的程序修 改了_p 的内容 , 就导致参数p 的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请了新的内存,只是把_p 所指的内存地址改变了, 但是p 丝毫未变。所以函数GetMemory 并 不能输出任何东西。事实上,每执行一次GetMemory 就会泄露一块内存,因为没有用free释放内存。
      如果必须使用指针参数去申请内存,那么一般有两种方法:
      1.使用指针的指针传递指针的地址。

void GetMemory(char **p,int num)
{
    *p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(&str, 100);     
    strcpy(str, "hello");   
}
      2.使用return来返回开辟空间的指针
char* GetMemory(char *p, int num)
{
    p = (char *)malloc(sizeof(char) * num);
    return p;
}
void Test(void)
{
    char *str = NULL;
    str = GetMemory(&str, 100);     
    strcpy(str, "hello");
    free(str);   
}
这样就可以解决指针传参数在函数中动态开辟内存的问题。
最后展示一下调试结果:
要构建的二叉树

  

图2 

结果:

图3


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值