简单二叉树新手教程

二叉树的傻瓜教程,简单实现

二叉树是数据结构初学者所要了解的重要概念,如果能用代码实现它的简单功能,能帮助我们更好的理解它的结构和用途。

在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
一棵深度为k,且有2^k-1个结点的二叉树,称为满二叉树。这种树的特点是每一层上的结点数都是最大结点数。而在一棵二叉树中,除最后一层外,若其余层都是满的,并且或者最后一层是满的,或者是在右边缺少连续若干结点,则此二叉树为完全二叉树。具有n个结点的完全二叉树的深度为floor(log2n)+1。深度为k的完全二叉树,至少有2k-1个叶子结点,至多有2k-1个结点。
如下图:
在这里插入图片描述
以上来自————二叉树百度百科

了解单向链表结构的同学会知道链表的C语言实现需要用到结构体的知识,在单向链表中,结构体包含一个存放数据的域和指向下一个节点的地址的指针域。

而二叉树的节点包含数据域之余,还包含指向左指数的指针域——left,和指向右子树的指针域——right。就如上图F点的左子树是C,右子树是E,这时问题来了,如何用C语言表示如上图指向关系呢?(这也是许多数据结构需要理解的重要之处)。
重点来了!!!
我们先定义如下结构体:

typedef 相当于重命名,以后声明一个如下的结构体变量p就不需要struct node p;了,直接Node p;就行。

typedef struct node
{
	char data;
	struct node * left , *right;
}Node;

假设我们声明了三个如上结构的变量:Node F,C,E;
然后执行如下操作:

//涉及结构体和指针的简单知识,需要提前理解。
F.left = F.right = NULL;
F.data = 'F';
C.left = C.right = NULL;
C.data = 'C';
E.left = E.right = NULL;
E.data = 'E';
F.left = &C;
F.right = &E;

经过上述的代码运行后,F,C,E节点就向上图所表示的连接起来了!

当然以上的操作是为了便于理解而实现的。而且问题十分的明显,仅仅三个节点的关系实现就要如此复杂,是不可能真正在实际应用的。我们自然而然的想到要实现简单,需要将各种操作函数化,比如涉及插入的函数,涉及遍历的函数,涉及开辟空间的函数(比如我有一百个节点,不可能一一声明)等等…

关于插入函数,我们需要注意下,一个节点只能插入左右子树,而且插入的位置也需要分摊下来,如果所有新节点全部往右边或左边插入,就成线性表了。如下图:
全部往右插入所以我们规定一个插入的规则,比如一个数如果比当前节点大就放到节点的右边,如果比它小或者等于它就放在左边,如下图:在这里插入图片描述这样树会显得平衡匀称点。这样的插入方式其实是实现二叉搜索树的插入方式。当然这里仅仅提一下,笔者后续可能会更新相关的文章,大家也可自行在网上搜索学习。

插入理解了,遍历的操作也需要明白。任何二叉树的遍历都是从根节点开始的(上图 3所在的节点)
遍历包含先序中序后序层序遍历。
前三种遍历是用递归实现的,最后原代码会实现,层序遍历需要用到队列的知识,限于篇幅不会提及。
先序遍历顺序如下:3 1 2 4 (先中间,再左 再右)
中序遍历:1 2 3 4(先左 再中间 再右)
后序遍历:2 1 4 3(先左,再右,最后中间)

所谓先,中,后指的就是父节点的遍历顺序。
遍历方法是简单的递归实现,比较容易理解和记忆。

最重要的实现插入的操作。
我们先来看函数的参数声明。这里我定义了一个指向Node结构体的二级指针,和需要插入的数据data。为什么要定义二级指针呢,总的来说为了方便吧
举个例子:

我有一个Node类型的变量 n
如果我要在某个函数中改变n的值,我需要向该函数传入n的地址,也就是 &n
同理我有一个指针 p 我需要在函数中改变p的内容,我就得在函数中传入指针的地址,也就是 &p
假设一个Node类型指针的定义 是 Node * t; 所以二级指针的定义则为 Node ** p;
t的地址赋给p的语句是p=&t;
需要通过p访问t的内容的语句是*p;

void insert(Node** t, int data)
void insert(Node** t, int data)
{
	if (*t == NULL)	//如果当前节点为空,则为其开辟空间,并赋值给它
	{
		(*t) = (Node*)malloc(sizeof(Node));
        (*t)->data = data;
        (*t)->left = (*t)->right = NULL;		//不忘初始化指针指向为空。
       
	}
	else if (data >= (*t)->data)	//比根节点(父节点)大则插入右边
	{
		insert(&((*t)->right), data);
	}
	else insert(&((*t)->left), data);//同理
}

接下来就是完整代码的提供了。

#include<stdio.h>
#include<malloc.h>
/**/
typedef struct node
{
	int data;
	struct node* left, * right;
}Node;

void insert(Node** t, int data)
{
	if (*t == NULL)
	{
		(*t) = (Node*)malloc(sizeof(Node));
        (*t)->data = data;
        (*t)->left = (*t)->right = NULL;
       
	}
	else if (data >= (*t)->data)
	{
		insert(&((*t)->right), data);
	}
	else insert(&((*t)->left), data);
}
/**前序遍历 根左右**/
void PreOrderTravel(Node* T)
{
    if (T == NULL )
        return;
    printf("%d ", T->data);
    PreOrderTravel(T->left);
    PreOrderTravel(T->right);
}

/**中序遍历 左根右**/
void InOrderTravel(Node* T)
{
    if (T == NULL)
        return;
    InOrderTravel(T->left);
    printf("%d ", T->data);
    InOrderTravel(T->right);
}

/**后序遍历 左右根**/
void TailOrderTravel(Node* T)
{
    if (T == NULL)
        return;
    TailOrderTravel(T->left);
    TailOrderTravel(T->right);
    printf("%d ", T->data);
}

int main()
{
    Node* t = NULL;
    int i,a[4] = { 3,1,2,4 };
   
    for(i = 0; i < 4; i++)
        insert(&t, a[i]);
    PreOrderTravel(t); printf("\n");

    InOrderTravel(t); printf("\n");

    TailOrderTravel(t); printf("\n");
}

测试一下:
在这里插入图片描述感谢观看。
希望点个赞~~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值