您的位置:首页 > 运维架构 > Linux

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

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

二叉树概念属性

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



图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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐