一,二叉树的链式结构
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是 链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所 在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面课程 学到高阶数据结构如红黑树等会用到三叉链。
typedef int BTDataType; //数据类型
typedef struct BinaryTreeNode
{
BTDataType data; //数据域
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
二,二叉树的前序遍历
学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
前序遍历用人话说,就是,若二叉树为空,则什么都不做,不是空,就,
先访问根节点,然后访问左子树,最后再访问右子树。
之前都太抽象了,现在找个二叉树解析一下:
这颗二叉树的前序遍历,是
第一步,先访问根节点1,
第二步然后访问左子树,下图,因为这是一颗二叉树,访问得先访问根节点2
第三步,访问这颗二叉树的左子树,也就是4,根节点也是4,左右子树为空
第四步,访问4的左子树,也就是空
第五步,刚刚访问到空了,该到右子树了,也是空。
第六步,4这颗二叉树已访问完毕,该回到上一级了,以2为根的二叉树的右子树5,根节点也是5
第七步,该访问5的左子树,为空,
第八步,该访问5的右子树,也为空
刚刚这颗子树彻底访问完毕了,又该回到上一级了,到了整颗二叉树的右子树了,
废话不多说,跟上面子树一个道理,
第九步,访问的是3,
第十步,访问3的左子树6,
第十一步,访问6的左子树,空,
第十二步,访问6的右子树,空,
第十三步,回到上一级,访问3的右子树7
第十四步,访问7的左子树,空,
第十五步,访问7的右子树,空
综合所述前序:1-->2-->4-->空-->空-->5-->空-->空-->3-->6-->空-->空-->7-->空-->空
(许多人认为节点为空就不访问了? 不是这样的,他也会访问,只是你看不到罢了)
下面是代码的实现,用递归的思想去解决。用链式结构写二叉树,首选队列结构,下图是队列结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
typedef BTNode* QDataType;
typedef struct QueueNode //链式结构:表示队列
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue //队列的结构
{
QNode* head;
QNode* tail;
int size;
}Queue;
void QueueInit(Queue* pq); //初始化队列
void QueueDestory(Queue* pq); //销毁队列
void QueuePush(Queue* pq,QDataType x); //队尾入队列
void QueuePop(Queue* pq); //队头出队列
QDataType QueueFront(Queue* pq); //获取队列头元素
QDataType QueueBack(Queue* pq); //获取队列队尾元素
bool QueueEmpty(Queue* pq); //检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueSize(Queue* pq); //获取队列中有效元素个数
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode =(QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
newnode->data = x;
newnode->next = NULL;
}
if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
del = NULL;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL && pq->tail == NULL;
}
int QueueSize(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
int n = 0;
while (cur)
{
++n;
cur = cur->next;
}
return pq->size;
}
接下来则是前序遍历代码,用递归的思想。
void PreOrder(BTNode* root) //二叉树前序遍历
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
看看视觉效果:就用上面那组数据,
看看结果,和上面分析的是不是一模一样,(傲娇脸)!
和上述分析的是不是一模一样,
三,二叉树的中序遍历
中序遍历用人话说,就是,若二叉树为空,则什么都不做,不是空,就,
先访问左子树,然后访问根节点,最后再访问右子树。和前序遍历有区别的,根节点跑到第二位了
还是刚刚那个图
第一步:先访问左子树,然后还没完,这颗左子树的左子树4,然后4的左子树,空,
第二步:访问4这个子树的根,也就是4
第三步:访问4这个子树的右子树,空
第四步:4这个子树访问完毕,返回上一级子树,访问根节点2
第五步:根节点2访问完毕,该到右子树5了,访问右子树5的左子树,空
第六步:访问5的根节点,是5
第七步:访问5的右子树,空
第八步:刚刚那颗小的二叉树访问结束,回到这颗大的二叉树了,访问根节点,1
第九步:访问大二叉树的右子树,下图,先访问这个子树的左子树6的左子树,空
第十步:访问6的根节点,6
第十一步:访问6的右子树,空
第十二步:访问这个子树的根节点,3
第十三步:访问右子树7的左子树,空
第十四步:访问7的根节点,7
第十五步:访问7的右子树,空
综合所述前序:空-->4-->空-->2-->空-->5-->空-->1-->空-->6-->空-->3-->空-->7-->空
下面是代码的实现,用递归的思想去解决。用链式结构写二叉树,依旧是刚刚的二叉树,用中序遍历,
void InOrder(BTNode* root) //二叉树中序遍历
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
看看视觉效果:就用上面那组数据,
看看结果,和上面分析的是不是一模一样,(傲娇脸)!
四,二叉树的后序遍历
后序遍历用人话说,就是,若二叉树为空,则什么都不做,不是空,就,
先访问左子树,再访问右子树,最后访问根节点。和前序遍历有区别的,根节点跑到第三位了
还是刚刚那个图
第一步:先访问左子树,然后还没完,这颗左子树的左子树4,然后4的左子树,空,
第二步:访问4这个子树的右子树,也就是空
第三步:访问4这个子树的根节点,也就是4
第四步:访问这颗子树的右子树5,必须先访问5这个子树的左子树,也就是,空
第五步:接着访问5这个子树的右子树,也是,空
第六步:访问5这个子树的根节点,也就是,5
第七步:访问上面这个二叉树的根节点,也就是2
第八步:这个左子树访问完毕,应该到了右子树,那必须先访问左子树6的左子树,空
第九步:接着访问6的右子树,也是,空
第十步:访问6的根节点,也就是,6
第十一步:6这个子树访问完毕,应该到了,右子树7,先访问7的左子树,空
第十二步:接着访问7的右子树,空
第十三步:访问7的根节点,也就是,7
第十四步:访问这颗右子树的根节点,3
第十五步:这个大二叉树的左右子树全部访问完毕,最后访问根节点 ,1
综合所述前序:空-->空-->4-->空-->空-->5-->2-->空-->空-->6-->空-->空-->7-->3-->1
下面是代码的实现,用递归的思想去解决。用链式结构写二叉树,依旧是刚刚的二叉树,用后序遍历,
void PostOrder(BTNode* root) //二叉树后序遍历
{
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
}
看看视觉效果:就用上面那组数据,
看看结果,和上面分析的是不是一模一样,(傲娇脸)!
各位老爷,小手点个赞再走!!!!!