linux下GCC编译环境中二叉树遍历、C语言实现以及调试过程中段错误
2011-08-31 17:34
811 查看
最近一直在学习数据结构准备面试,昨天晚上看到了二叉树,在网上查了一些资料以后照猫画虎的写了一个二叉树遍历的程序,主要是为了消化递归构建二叉树和遍历的过程,调试过程中也发现了不少问题。
二叉树概念属性
二叉树是由一个根节点和两棵互不相交的、分别成为根节点左子树和右子树的的二叉树组成。
图1
图1为典型的二叉树。
二叉树的遍历方式
1.前序遍历
先访问根节点,然后访问左子树,之后是右子树。注意在左右子树中也是同样的顺序访问。
2.中序遍历
先访问左子树,然后访问根节点,最后访问右子树。注意在左右子树中也是同样的顺序访问。
3.后序遍历
先访问左子树,然后访问右子树,最后访问根节点。注意在左右子树中也是同样的顺序访问。
二叉树的构建方式
1.已知二叉树的前序遍历和中序遍历序列,通过递归构建二叉树。
2.已知二叉树的后续遍历和中序遍历序列,通过递归构建二叉树。
介绍完概念之后上代码,编译环境为ubuntu10.04,使用G++遍历CPP文件。
这里涉及到C++中指针传递内存的问题。由于递归的原因,在函数BinaryTreeFromMidAndPre和BinaryTreeFromMidAndPost中我们将创建二叉树的根节点使用函数参数传入。但是在初次调试的时候执行完构建函数后,pRoot指针仍然为NULL。说明构建了二叉树之后函数并没有传回根节点的指针,这是为什么呢?
先看下面的程序:
毛病出在函数GetMemory 中。 编译器总是要为函数的每个参数制作临时副本,指针参数p 的副本是 _p ,编译器使 _p = p 。如果函数体内的程序修 改了_p 的内容 , 就导致参数p 的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请了新的内存,只是把_p 所指的内存地址改变了,
但是p 丝毫未变。所以函数GetMemory 并 不能输出任何东西。事实上,每执行一次GetMemory 就会泄露一块内存,因为没有用free释放内存。
如果必须使用指针参数去申请内存,那么一般有两种方法:
1.使用指针的指针传递指针的地址。
最后展示一下调试结果:
要构建的二叉树
图2
结果:
图3
二叉树概念属性
二叉树是由一个根节点和两棵互不相交的、分别成为根节点左子树和右子树的的二叉树组成。
图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
相关文章推荐
- linux下c++编程环境搭建,运行过程以及调试,内含C++头文件源文件编译过程链接
- gcc编译过程、C语言编译过程分析、环境变量设置、linux文件夹结构和用途介绍、常用文件和目录的操作命令、文件类型
- arm-linux-gcc4.4.3 交叉编译环境搭建&错误修正 fedora 19(KDE)
- 链表的c语言实现以及根据linux内核中链表的实现过程
- 64位Ubuntu 16.01搭建嵌入式交叉编译环境arm-linux-gcc过程图解
- Ubuntu 12.04嵌入式交叉编译环境arm-linux-gcc搭建过程图解
- Ubuntu12.04嵌入式交叉编译环境arm-linux-gcc搭建过程,图解
- Linux环境下段错误的产生原因及调试方法小结 最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多、花费时间最长的问题就是
- ok6410[001] Ubuntu 16.04[64bit]嵌入式交叉编译环境arm-linux-gcc搭建过程图解
- Ubuntu 12.04嵌入式交叉编译环境arm-linux-gcc搭建过程图解
- Ubuntu 14.04 LTS嵌入式交叉编译环境arm-linux-gcc搭建过程图解
- 一个C程序的编译过程(Linux环境下GCC)
- Linux下GCC的DEBUG和优化,以及编译过程
- 在linux下用gdb调试gcc编译的代码;以及反汇编的操作
- 一个C程序的编译过程(Linux环境下Gcc)
- Ubuntu 12.04嵌入式交叉编译环境arm-linux-gcc搭建过程图解
- linux 环境下 c语言实现mysql数据库图片的存储以及多数据库直接的转存
- C语言的编译过程、安装gcc编译器以及设置环境变量
- Linux 0.11 在ubuntu-11和gcc-4.6.1下编译调试至正常运行的过程详解
- ios: ffmpeg-0.9.2 xcode4.3.2 IOS5.1 真机调试编译过程以及Mac OSX10.7.3安装 gcc和make