一、引言:
大三上学期学了数据结构后就没再接触过数据结构的内容了,至今已经整整两年半了,已经把链表,队列,堆栈,树,二叉树等内容忘得干干净净了,并且如果不是决定做《机器学习》的作业,自己丝毫没有意识到自己已经忘得干干净净,或者说是以前只是依样画葫芦的把二叉树的程序调出来了,但是根本就没有明白其中的原理!!
二、做《机器学习》作业的时候,怎么也想不明白两个问题:
第一个问题:
"p->next"表示什么?p是一个指针,指针怎么会有next呢?应该Node结构体才有next指针呀。
第二个问题:
怎么建立一棵树?杨惠说要传一个根结点,并返回该根结点,但是为什么要传一个根结点呢?返回一个根结点怎么就能表示一棵树了呢??真是想不明白!!
第三个问题:
建树函数buildTree()或者insert(),create()等需要设置几个参数?必须设置一个Node root参数吗?如果不设置root参数,怎么让它递归呢?
三、解决:
第一个问题:
唉,太久没看指针了,或者说以前根本就没有理解什么是指针,指针变量,指针变量所指向的变量等概念,复习了一遍谭浩强的C语言的书,感觉第一个问题豁然开朗。原来"p->next"表示的是:p指针变量所指向的变量的next指针变量。
第二个问题:
唉,看来以前根本没有理解二叉树,也没有理解递归方法,只因为吴平老师的一句“递归方法效率低”就完全不看递归了,真是傻瓜。不仅仅是递归,感觉自己连函数参数都不太理解,根本不知道什么时候需要参数,参数有何作用。更不用说明白一个函数的设计关键要清楚它传入什么参数,返回什么结果了。
第三个问题:
至今仍然不太明白!似乎就是必须接受一个Node root参数。
查了网上的递归建立二叉树的几个程序,它们分别是无参数、有一个参数、有两个参数的三种不同实现方法,从中终于领悟到什么时候需要参数,为什么需要参数了!可是第二、三个例子的一个参数和两个参数实际是一样的,只不过一个参数的例子用控制台输入数据而已。唉,还是不知道如果不设置一个Node root参数可不可实现建树。
四、下面分别是这三个建立二叉树的程序:
1.无参数:http://www.51log.net/dev/603/4692612.htm
Tree *Create()
{
char ch;
cin>> ch;
Tree *root;
if( ch == NIL )
{
return NULL;
}
else
{
root = new Tree(ch);
root->left = Create();
root->right = Create();
return root;
}
}
自己的注释:
Create函数:
输入:无
输出:一棵二叉树(根据返回的root根结点指针(注意不是root根结点,而是指针)就能访问到这棵树的所有结点了)
2.一个参数:http://shakesmin.javaeye.com/blog/43107
# //创建二叉树
# void CreateBiTree(BiTree &t) {
# int ch=getchar();
# if(ch==' ') t=NULL;
# else {
# if( !( t=(BiTNode*)malloc(sizeof(BiTNode))) ) exit(OVERFLOW);
# t->data=ch;
# CreateBiTree(t->lchild);
# CreateBiTree(t->rchild);
# }
# }
自己的注释:
CreateBiTree函数:
输入:根结点;
输出:一棵二叉树;(无返回值)
注意:
这种方法传入根结点,但是没有返回值的原理。下面看一下它的使用方式就明白了:
# //递归遍历二叉树
# void PreOrderTraverse(BiTree t) {
# if(t) {
# /* 以先序方式遍历,若要以中序或后序遍历
# 只需改变以下三句顺序*/
# printf("%c ",t->data);
# PreOrderTraverse(t->lchild);
# PreOrderTraverse(t->rchild);
# }
# }
# int main() {
# BiTree t;
# printf("请按先序正确输入二叉树(空格为空树):\n");
# CreateBiTree(t);
#
# printf("先序历遍: ");
# PreOrderTraverse(t);
# return 0;
# }
明白了吧?原来是在main函数中定义了一个全局的根结点,以该根结点为基础建立二叉树和遍历二叉树,因此CreateBiTree中就不再需要返回root根结点指针才能访问该二叉树,它的PreOrderTraverse遍历函数传的直接是根结点,而第一个程序中遍历函数传的是指向root的指针变量。
3.有两个参数:http://dev.csdn.net/article/68/68480.shtm
#include <iostream.h>
#ifndef DEBUG
#define DEBUG
typedef int DataType;
typedef struct Node
{
DataType data;
struct Node *LChild;
struct Node *RChild;
}Node;
/*树的数据结构*/
/////////////////////////////////////////////////////////////
Node * Initiate()
/*初始化为空树*/
{
Node *root = 0;
return root ;
}
/////////////////////////////////////////////////////////////
Node * Creat( DataType data )
/*建节点*/
{
Node * Temp = new Node ;
Temp -> data = data ;
Temp -> LChild = 0 ;
Temp -> RChild = 0;
return Temp ;
}
/************************************************/
void Insert( Node *&root , DataType data )
//在c下不能这样 Node *&root
/* 降顺序二叉数装入数据,左子树<右子树*/
{
Node *p = Creat( data );//注:该程序中此行代码在if前,如果要插入一个非root结点,则每次都要新创建两个结点,一进入insert()创建一个p,递归时又创建一次。如果把该语句放到if内也不对,因为else if( p->data < root->data )时p就为空了。
if( !root )
{ //Node *p = Creat( data ); //注:原程序逻辑不对,应该在if为真时才创建新结点;
root = p;
}
else if( p->data < root->data )
{
Insert ( root->LChild , p->data );
}
else
{
Insert ( root->RChild , p->data );
} /*相等的 将装数据到右孩子 */
}
/****************************************************/
void PrintTree(Node * root)
/*递归中序遍历 ---> 显示从小到大*/
{
if( !root ) return ;
PrintTree(root->LChild);
cout<< root->data <<" :";
PrintTree( root->RChild );
return ;
}
自己的注释:
Insert函数:
输入:根结点和结点数据;
输出:一棵二叉树;(无返回值)
注意:
同样,这种方式也传入了根结点来创建二叉树,并且函数没有返回root指针变量。再看它的使用方式:
///测试代码////////////
void main()
{
int a;
Node *Root = Initiate() ;
cout<<" -1 to exit: "<<endl;
cin>>a;
while( (a != -1)&&cin.good() )
//遇到非法输入同样退出循环
{
Insert( Root , a );
cin>>a ;
}
if(!cin.good())
//输出错误信息
{
cout<<" the type is error ! "<<endl;
}
PrintTree(Root);
cout<<" ok ? "<<endl;
FreeTree(Root);//销毁树 防止内存泄漏
return;
}
现在明白了吧,也是在main中事先定义了一个root全局指针变量,然后把它做为参数建立二叉树和遍历二叉树。
结合自己买的那本书《数据结构 算法与实现 C++描述》上的清晰描述,知道,一棵二叉树是数据在内存中的逻辑表示,需要有一个指向该二叉树的root根节点的指针变量来记录它的首地址,然后就能访问到该树的所有结点了。
那么,这个根结点的指针变量在create()函数内定义呢(第一种无参的实现方式),还是在main函数中定义?——>两种方式都可以!如果在create()函数内定义,则需要返回该root的指针变量,这样才能取得二叉树的首地址遍历该树;如果在main函数中定义,则在create(TreeNode *root)中添加一参数接受该首地址建立二叉树。ok!
参考资料:
1. 二叉树的创建函数中对参数的疑问
http://www.51log.net/dev/603/4692612.htm
2. 数据结构课程源代码之二叉树实现及相关算法
http://shakesmin.javaeye.com/blog/43107
3. ::递归实现——创建二叉树 ----> 装入数据--->遍历---> 显示 --->销毁::递归实现)
http://dev.csdn.net/article/68/68480.shtm