最近在看数据结构,准备跨考计算机专业,感觉一直看书很挺无趣的。感觉一周抽出一天写写总结,刷一刷OJ还是挺快乐的一件事情,感觉现在才做这些事情有点晚了,很多大佬已经很早之前就开始了,所以我只能站在大佬的肩膀上看世界了。
首先谈谈我总体对数据结构这们课的看法,我并没有上过这个课,听了几节课的视频,就听不下去了,可能是我不太喜欢别人讲课,再者就是这门课不太好讲吧。我第一本书看的是《大话数据结构》,这本书讲的很基础,可以像看故事书那样看。然后我就开始看书做题,《王道》和《天勤》,天勤讲的更仔细,王道的题目不错适合有点基础的。
什么是树,没错就是我们日常生活中见到的。但是在数据结构中是倒过来的树,其实我觉得更像“根”。
怎么种一棵树,不可避免的要知道它的存储结构,就是先找到自己要待的地方,每种数据结构都差不多。有两种存储结构,,顺序存储和链式存储。这两种我就不解释了。
大千世界树的形状千奇百怪,什么树都有,树上这时候就会引入二叉树。有时候惯性思维,按部就班的按照书中的章节开始学,我们可以试着探索前人的研究思路。树的类型千奇百怪,规律性不容易找到,这时候前辈们研究了二叉树。其实不太明白为什么会引入这个树,肯定不是因为它“二”。我就胡思乱想了,可能是因为树的分支越多研究的起来越复杂,但是一叉树就 没必要了,因为那就是个棍。还可能就是,像二进制那样,二进制转化成其他进制,其他进制转化成二进制,二叉树也是,后面就有了,二叉树与树之间的转化。所以二叉树是研究的重点对象。
二叉树存储结构,顺序存储和链式存储。顺序结构结构适合存储完全二叉树,存储其他的树就会造成空间浪费,现在房价那么贵,这样不太好。还是链式存储比较好
链式结构节点类型的定义
typedef struct BTNode
{
char data;//数据域,这里的节点类型为char,可以修改成其他类型
struct BTNode *lchild;//左指针域
struct BTNode *rchild;// 右指针域
}BTNode;
知道到了定义之后我们怎么用这个构建出一棵二叉树呢,很多数据结构的书只给出了一个代码段,其实我很希望就是有一种输入然后有一种输出的那种形象的例子。这里先放一个代码段,后面会有一个完整的。
BTNode* createBitree()//前序顺序建立二叉树
{
BTNode*p;
char c;
cin>>c;
if(c=='0')
{
p=NULL;
}
else
{
p=new BTNode;//C++中开辟新的结构体空间
//p =(BTNode*)malloc(sizeof(BTNode));//C语言中的用法,要包含stdlib.h这个头文件
p->data=c;
p->lchild=createBitree();
p->rchild=createBitree();
}
return p;
}
我们现在构建好了这个二叉树,大家都定居好了。我怎么能够去他们家里串门呢,这就引入了遍历,先序、中序,后序,层次。
就是先去老王家还是先去老张家的问题。
这里先给出递归的算法,因为代码太简单了,一眼就懂,感觉自己很牛逼的样子。
先序遍历
void preorder(BTNode *p)
{
if(p!=NULL)
{
cout<<p->data<<" ";
preorder(p->lchild);
preorder(p->rchild);
}
}
中序遍历
void inorder(BTNode *p)
{
if(p!=NULL)
{
preorder(p->lchild);
cout<<p->data<<" ";
preorder(p->rchild);
}
}
后续遍历
void postorder(BTNode *p)
{
if(p!=NULL)
{
preorder(p->lchild);
preorder(p->rchild);
cout<<p->data<<" ";
}
}
完整的构建与遍历
#include <iostream>
#include <stdlib.h>
using namespace std;
typedef struct BTNode
{
char data;//数据域,这里的节点类型为char,可以修改成其他类型
struct BTNode *lchild;//左指针域
struct BTNode *rchild;// 右指针域
}BTNode;
BTNode* createBitree()//前序顺序建立二叉树
{
BTNode*p;
char c;
cin>>c;
if(c=='0')
{
p=NULL;
}
else
{
p=new BTNode;//C++中开辟新的结构体空间
//p =(BTNode*)malloc(sizeof(BTNode));//C语言中的用法,要包含stdlib.h这个头文件
p->data=c;
p->lchild=createBitree();
p->rchild=createBitree();
}
return p;
}
void preorder(BTNode *p)
{
if(p!=NULL)
{
cout<<p->data<<" ";
preorder(p->lchild);
preorder(p->rchild);
}
}
void inorder(BTNode *p)
{
if(p!=NULL)
{
inorder(p->lchild);
cout<<p->data<<" ";
inorder(p->rchild);
}
}
void postorder(BTNode *p)
{
if(p!=NULL)
{
postorder(p->lchild);
postorder(p->rchild);
cout<<p->data<<" ";
}
}
int main()
{
BTNode *T=NULL;
cout<<"请创建一个二叉树"<<endl;//例如:124000300
T=createBitree();
cout<<"二叉树创建完成!"<<endl;
cout<<"前序遍历二叉树"<<endl;
preorder(T);
cout<<endl;
cout<<"中序遍历二叉树"<<endl;
inorder(T);
cout<<endl;
cout<<"后序遍历二叉树"<<endl;
postorder(T);
return 0;
}
这三种遍历用递归非常简单,但是很低效。为什么低效呢,我觉得简单粗暴往往会牺牲其他东西,我们不累了,电脑就会骂街了。所以下面要用非递归的算法来搞定。这里我要引用《天勤》书里面大佬的解释“递归函数申请是系统栈,是一个所有递归函数都通用的栈,对于二叉树深度优先遍历算法,系统栈除了记录访问的节点信息外,还有其他信息要记录,以实现函数的递归调用,而用户自己定义的栈仅保存了遍历所需的节点信息,是遍历设计算法一个针对性的设计,也就是一般情况下,专业的比通用的要好一些”。
那么就研究研究非递归的遍历,感觉还挺多的,如果写在一篇上感觉没耐心看下去,下篇见