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

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

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

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. (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  2. git 一般的使用操作

    1.先在github上建立自己的repository,取名为yourRepo 2.创建本地库 ssh -T git@github.com # 在初始化版本库之前,先要确认认证的公钥是否正确 git i ...

  3. 单片机TM4C123学习(八):SPI接口D/A

    1.头文件和变量定义(不是很清楚) #include "driverlib/ssi.h" #include "driverlib/i2c.h" #include ...

  4. C# 编程指南-事件

    来自微软官方的msdn:   首页:https://msdn.microsoft.com/zh-cn/library/ms366768.aspx   1.如何:订阅和取消订阅事件   2.如何:发布符 ...

  5. iOS.ReactNative-2-bridge-and-react-native-app-execution

    Bridge and React Native App Execution 基于0.18.1 Async batched bridge used to communicate with the Jav ...

  6. 计算机图形学——OpenGL开发库开发库

    vc++6.0 有 OpenGL 的东西.有头文件 GL.H, GLAUX.H, GLU.H 但没有 GLUT 软件包/工具包 如果想使用glut.h的话必须自己添加. 首先下载 OpenGL开发库, ...

  7. PHP 判断数组是否为空的5大方法

    1. isset功能:判断变量是否被初始化 说明:它并不会判断变量是否为空,并且可以用来判断数组中元素是否被定义过 注意:当使用isset来判断数组元素是否被初始化过时,它的效率比array_key_ ...

  8. lab 7 函数超级多的类

    #include<iostream>#include<string>#include<cmath>using namespace std; class Ration ...

  9. WebStorm License Activation (WebStorm许可证激活)

    User or company name(用户或公司名称):  EMBRACE License key(许可证密钥):  ===== LICENSE BEGIN =====89374-12042010 ...

  10. HDU 1166 敌兵布阵 (数状数组,或线段树)

    题意:... 析:可以直接用数状数组进行模拟,也可以用线段树. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000&quo ...