故事是这样开始的,项目经理有一天终于还是拍拍我肩膀说:

无论你的链表写得多么的好,无论是多么的灵活,我也得费老半天才查找到想要的数据;

这让我的工作非常苦恼,听说有一种叫做二叉树的数据结构,你看能不能给我弄来;

Probelm:

看看如下的数据:

我们每次都要从头到尾的查看我们的数据链表里面是否存在着目标数据;

每次都小心翼翼的害怕漏掉哪个,这样的工作的确让人烦恼;

Solution

再看看如下的解决解决方案;

显然,我们很清楚自己要查找的目标大致会在那里出现;

例如查找的目标是6,那么我知道6小于9所以根本不会去看右边的数据;

我们继续看6大于5所以找到啦目标;

换句话说我们只对比了两次找到啦目标;

而对于链表,我们发现6排在了链表的尾部;

我们花啦大量的时间。。

好啦!到此为止我们知道这样的二叉树的确是高效的;

说干就干,我们来实现它;

typedef struct _node {
int data;
struct _node *left;
struct _node *right;
}Node;

节点定义可以如上面的方式,这也是大多数二叉树定义的方式;

但是试想想,左右子树的差别是什么呢?

我们都知道要是存在x节点,那么x.left.data < x.data < x.right.data这个规律;

我们为什么不充分的考虑考虑有没有其他可以更好的定义我们的左右节点呢?

让它自动的区决定什么时候是左节点存数据,什么时候是右节点存数据呢;

我们都知道,假设a=9;BOOL B =  a < 10;那么布尔值b是什么呢?

我们可以写下面这段代码分析分析,注意我们任然是在学习二叉树,你没有走神:)

#include <stdio.h>
int main(void)
{
int a = ;
printf("a < 10 return :%d\n",a < );
printf("a < 8 return :%d\n",a < );
return ;
}

结果是:

显然,我们希望10存放在右子树,8存放在左子树;

所以你会想是这样写;

a = ;
new = ;
if (x->a < new){
x->right = new;
}else{
x->left = new;
}

当然,上面的代码是接近伪代码的表述方式啦!相信你不会去运行它的;

然而我想说上面的代码是非常的繁琐的,虽然感觉很清晰;

试试下面的写法吧:)

typedef struct _node {
int data;
struct _node *link[];
}Node;

我们重新定义啦节点;

注意我们把左右节点改成啦一个指针数组的形式;

我们这样写的好处待会你就能体验到;

现在我们继续定义一个树;

typedef struct _tree{
struct _node *root;
}Tree;

我们的树定义得更加简单,注意我们是先定义节点,再定义树;

因为树的定义需要用到节点结构体;

接下来我们需要初始化我们的树;

Tree * init_tree()
{
Tree *temp = (Tree*)malloc(sizeof(Tree));
temp->root = NULL;
return temp;
}

这种写法是非常简洁的,但是它也有弊端;

它没有错误处理,例如:万一malloc分配内存错误;

通常我们可能会这样写:

Tree * init_tree()
{
Tree *temp = (Tree*)malloc(sizeof(Tree));
if(!temp) return NULL;
temp->root = NULL;
return temp;
}

或者这样写:

Tree * init_tree()
{
Tree *temp = (Tree*)malloc(sizeof(Tree));
if(temp){
temp->root = NULL;
}
return temp;
}

都是为啦!让程序运行的时候万一出问题,我们知道错误在哪?

你甚至可以写上提示信息,但是咱们的核心问题不是这些,所以我们都假设程序不会出现这些问题;

我们现在需要做的是创建节点;

Node * make_node(int data)
{
Node *temp = (Node*)malloc(sizeof(Node));
temp->link[] = temp->link[] = NULL;
temp->data = data;
return temp;
}

创建节点也比较容易理解,首先申请内存,然后初始化数据成员;

最后返回该节点;

下面是如何插入这个创建好的节点呢?

我们需要一个函数大概会叫做insert,希望你能够通过名字大概能找到感觉;

Node * insert_recursive(Node *root,int data)
{
if(root == NULL){
root = make_node(data);
}else if(root->data == data){
return root;
}else{
int dir = root->data < data;
root->link[dir] = insert_recursive(root->link[dir],data);
}
return root;
}

注意,希望你能够一眼看出recursive的意思是什么。我不会告诉你的,我们希望你自己查查哈;

我们分析分析代码吧;

第一个if语句,告诉我们如果这个节点为空,那么就创建它;

else if语句,告诉我们如果这个数据与树上找的一致,那么就返回这个节点给调用者;

else语句,告诉我们的就稍微复杂一点啦;

它首先声明一个dir的整形变量,我们可以偷偷告诉你它其实是一个只有两种可能值得整形;

我们通常都可以用BOOL类型替换它;你可以发现它马上就露馅啦;

后面马上赋值,root->data < data;是一个表达式,这个表达式只会返回1,0;

希望你还记得什么时候返回1,什么时候返回0;(我们前面写过测试的哦)

最后,这个节点已经存放到该存放的位置啦;

我们不应该直接去调用点,我们需要的是去操作一颗树;

因此我们需要包装一下我们的insert函数;

int insert(Tree * tree, int data)
{
tree->root = insert_recursive(tree->root,data);
return ;
}

很简单就ok啦;

为啦看到我们的成果,你一定希望它能够打印些什么,而且证明它的确是一颗树;

我们需要一种叫做中序遍历的方式打印它,这种方式的好处是你可以看到它会按照从小到大得方式输出;

void print_inorder_recursive(Node *root)
{
if(root){
print_inorder_recursive(root->link[]);
printf("data:%d\n",root->data);
print_inorder_recursive(root->link[]);
}
return ;
}

同理,我们任然写一个包装函数;

void print_inorder(Tree *tree)
{
print_inorder_recursive(tree->root);
return ;
}

好啦,最后测试一下我们的二叉树;

int main(void)
{
Tree * tree = init_tree();
insert(tree,);
insert(tree,);
insert(tree,);
insert(tree,);
insert(tree,);
insert(tree,);
insert(tree,); print_inorder(tree);
return ;
}

编译运行,看看我们的结果如下;

Discussion

我们的功能是实现啦!但是这远远不够的;

例如,

1,我们该证怎么查找一个目标节点呢?

2,我们该如何删除一个节点呢?

3,插入的方式只能用递归吗?

4,递归有什么缺点吗?

我们下次再聊!

C语言实现二叉树-01版的更多相关文章

  1. C语言实现二叉树-02版

    ---恢复内容开始--- 昨天,提交完我们的二叉树项目后,今天早上项目经理早早给我打电话: 他说,小伙子干的不错.但是为什么你上面的insert是recusive的呢? 你难道不知道万一数据量大啦!那 ...

  2. C语言实现二叉树-04版

    二叉树,通常应当是研究其他一些复杂的数据结构的基础.因此,通常我们应该精通它,而不是了解:当然,可能并不是每个人都认同这种观点,甚至有些人认为理解数据结构就行了!根本没有必要去研究如何实现,因为大多数 ...

  3. C语言实现二叉树-03版

    我们亲爱的项目经理真是有创意,他说你给我写得二叉树挺好的: 功能还算可以:插入节点,能够删除节点: 可是有时候我们只是需要查找树的某个节点是否存在: 所以我希望你能够给我一个find功能: 还有就是, ...

  4. 数据结构与抽象 Java语言描述 第4版 pdf (内含标签)

    数据结构与抽象 Java语言描述 第4版 目录 前言引言组织数据序言设计类P.1封装P.2说明方法P.2.1注释P.2.2前置条件和后置条件P.2.3断言P.3Java接口P.3.1写一个接口P.3. ...

  5. C语言实现二叉树-利用二叉树统计单词数目

    昨天刚参加了腾讯2015年在线模拟考: 四道大题的第一题就是单词统计程序的设计思想: 为了记住这一天,我打算今天通过代码实现一下: 我将用到的核心数据结构是二叉树: (要是想了解简单二叉树的实现,可以 ...

  6. 《C++程序设计语言(英文第四版)》【PDF】下载

    <C++程序设计语言(英文第四版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382177 内容简介 本书是C++领域经典的参 ...

  7. Rust语言之HelloWorld Web版

    Rust语言之HelloWorld Web版 下面这篇文章值得仔细研读: http://arthurtw.github.io/2014/12/21/rust-anti-sloppy-programmi ...

  8. | C语言I作业01

    C语言I作业01 标签:18软件 李煦亮 1.1 你对软件工程专业了解是怎样? 对软件工程的了解是从人工智能频繁地出现在各大新闻,新闻报道了许多高校针对人工智能开设了相关课程或者专业,软件工程是开设的 ...

  9. c文件二进制读取写入文件、c语言实现二进制(01)转化成txt格式文本、c读取文件名可变

    c语言实现二进制(01)转化成txt格式文本: 下面的程序只能实现ascall对应字符转换,如果文件内出现中文字符,则会出现错误. 本程序要自己创建个文本格式的输入文件a1.txt,编译后能将文本文件 ...

随机推荐

  1. web.py+mysql插入中文提示query = query.encode(charset) UnicodeEncodeError: 'latin-1' codec can't encode characters in position 86-100

    对于中文编码的问题,总会出现各种各样恶心的错误,还不知道应该怎么解决,首先,你从最开头就应该关注编码问题,尽量保证所有的编码方式都是一致的 用python+web.py+mysql来写程序,首先要保证 ...

  2. 对div作用域的理解

    若想要div的height起作用,则要保证它上面的层的height也是有用的,如上图

  3. 第三十三章 metrics(1) - graphite搭建 + whisper存储模式 + 高精度向低精度聚合方式 + 集成StatsD + 集成grafana

    组件介绍: carbon:Carbon实际上是一系列守护进程,组成一个Graphite安装的存储后端.这些守护进程用一个名为Twisted的事件驱动网络引擎监听时间序列数据.Twisted框架让Car ...

  4. HttpClient请求网络数据的Post请求

    new Thread(){            public void run() {                                try { //获得输入框内容          ...

  5. CDN在中国的发展的九个年头的点点滴滴

    对于发展快速的互联网行业来说,8年时间已经足够让一个产业跌宕起伏.但CDN在国内的发展却没有大红大紫,直到2005... 对于发展快速的互联网行业来说,8年时间已经足够让一个产业跌宕起伏.但CDN在国 ...

  6. three.js 之旅一

    扯一段废话,当你遇到一个没人知道的问题时,你该怎么办?         问周围人,他们遇到这种情况怎么办.作为程序员,这种情况肯定时有发生,我们要学会寻找资源. Three.js的六个基本步骤 1.设 ...

  7. [VBS]带参数删除扩展名不是*.h、*.c、*.cpp的全部文件

    脚本使用例程CleanFolder遍历一个文件夹 1)使用CleanFolder递归遍历该文件夹下的所有子文件夹 2)如果该子文件夹的大小为0,则删除这个文件夹 3)遍历该文件夹下的所有文件,扩展名不 ...

  8. hd2066一个人的旅行

    Problem Description 虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,^0^),很多事,还能丰 ...

  9. a:link,a:visited,a:hover,a:active

    1:解释 link:连接平常的状态 visited:连接被访问过之后 hover:鼠标放到连接上的时候 active:连接被按下的时候      详细的: :hover版本:CSS1/CSS2 兼容性 ...

  10. 使用Notepad++编码编译时报错(已解决?)

    使用Notepad++编码编译时报错(已解决?) 使用Notepad++编码,编译的时候经常会报错,说什么GBK编码啥啥啥~~~但同样的编码用ECLIPSE就没有问题.再有,用记事本把他保存成ANSI ...