一、二叉树的遍历
1.前序、中序与后序遍历:
学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是指:按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。同时,遍历也是二叉树上最重要的运算之一,是二叉树上进行其它运算的基础。
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之间。
后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
由于被访问的结点必是某子树的根,所以 N(Node)、L(Left subtree)和 R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR 和 LRN 分别又称为先根遍历、中根遍历和后根遍历。
// 二叉树前序遍历
void PreOrder(BTNode* root);
// 二叉树中序遍历
void InOrder(BTNode* root);
// 二叉树后序遍历
void PostOrder(BTNode* root);
2.二叉树相关题目
1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为( )
A. ABDHECFG
B. ABCDEFGH
C. HDBEAFCG
D. HDEBFGCA
此题选A
2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK; 中序遍历:HFIEJKG; 则二叉树根结点为( )
A. E
B. F
C. G
D. H
3.设—课二叉树的中序遍历序列: badce,后序遍历序列: bdeca, 则二叉树前序遍历序列为( )
A. adbce
B. decab
C. debac
D. abcde
所以选择 D 选项
4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF,则按层次输出 (同一层从左到右) 的序列为( )
A. FEDCBA
B. CBAFED
C. DEFCBA
D. ABCDEF
所以选择 A 选项
3.层序遍历
除了最常用的先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为 1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第 2 层上的节点,接着是第三层的节点,以此类推。而这种自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
二、遍历的实现
1.二叉树的结构创建
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
2.二叉树的节点创建
BTNode* BTBuyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("node");
return NULL;
}
node->data = x;
node->left = NULL;
node->right = NULL;
return node;
}
3.前序遍历
先访问根节点,即当前节点的值,接着递归访问左子树,最后递归访问右子树。使用递归实现整个二叉树的遍历。
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
4.中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
5.后序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
InOrder(root->right);
printf("%d ", root->data);
}
6.层序遍历
层序遍历就是一层一层的遍历,在链式储存中,我们一般借助队列来实现层序遍历。
利用的是队列的先进先出的性质。先让根入队,然后出队头数据,再让队头数据的左右孩子入队。每从队头删除掉一个元素,就让这个元素的两个孩子入队,直到队列为空为止。
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->data);
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
}
printf("\n");
QueueDestroy(&q);
}
7.二叉树节点个数
对于二叉树节点数量的统计,采用的方式是任意选择一种遍历顺序(只依照遍历顺序,不访问节点),遍历整个树结构,每找到一个节点让计数变量加一即可。
int BTreeSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
else
{
return BTreeSize(root->left) + BTreeSize(root->right) + 1;
}
}
8.求树的深度
int BTreeDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int n1 = BTreeDepth(root->left) + 1;
int n2 = BTreeDepth(root->right) + 1;
int n3 = n1 > n2 ? n1 : n2;
return n3;
}
9.第 K 层节点个数
若根节点为空,则节点的个数为0。如果我们要计算第 K 层的元素(节点)个数,首先从根节点开始统计,假设我们每向下一层 K 就减 1,那么当 K = 1 时,表示我们来到了第 K 层,然后计算 K = 1 时的节点个数返回值相加的结果即可。
int BTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BTreeLevelKSize(root->left, k-1) + BTreeLevelKSize(root->right, k-1);
}
10.叶节点个数
叶节点就是度为0的节点,即没有子树,我们同样使用递归进行统计。
根节点进入函数后,应当首先判断根节点是否为叶节点,如果一个节点的左子树和右子树同时为空,说明这是一个叶节点;如果不是,其左子树的叶节点和右子树的叶节点之和就是当前节点以下的所以叶节点,形成递归。
//二叉树叶子结点
int BTreeLeave(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return BTreeLeave(root->left) + BTreeLeave(root->right);
}
11.查找值为x的节点
若节点为空,就返回空。若节点的值等于要查找的值,就返回该节点。
若节点不为空,但节点的值不是我们要查找的值,就查找节点的左子树,如果查找的结果不为空,就返回该节点。若左子树的查找结果为空,就以同样的方式处理右子树。如果都找不到,就返回空。
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
BTNode* ret1 = BinaryTreeFind(root->left, x);
if(ret1)
{
return ret1;
}
BTNode* ret2 = BinaryTreeFind(root->right, x);
if (ret2)
{
return ret2;
}
return NULL;
}
12.判断完全二叉树
若一个二叉树不是完全二叉树时,那么当我们对它进行层序遍历时,其中的部分节点就会是 NULL,于是我们可以通过这一点来判断一个二叉树是否为完全二叉树。
前半部分与二叉树的层序遍历一样,建队列,根入队,队列不为空,进入while循环,在循环中删队头节点,然后让该节点的左右孩子入队。
注意 这里循环停止的条件还要加上一个即堆顶的元素为空。
在跳出循环后存在两种情况,第一种是队列已空,节点之间没有空,表明是完全二叉树,返回true;而第二种情况是队列不为空,但在访问队头节点时访问到了 NULL,这时我们需要再次进行循环,若队列不为空,就进入循环逐个查找并删除队头的节点,若发现不为空的节点,说明节点间有 NULL 相隔,即该二叉树不是完全二叉树,返回false。
//判断完全二叉树
bool BTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
break;
}
QueuePush(&q,front->left);
QueuePush(&q,front->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
13.二叉树的销毁
void BTreeDestory(BTNode* root)
{
if (root == NULL)
{
return;
}
BTreeDestory(root->left);
BTreeDestory(root->right);
free(root);
}
Queue.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* ptail;
QNode* phead;
int size;
}Queue;
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
Queue.c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
void QueueInit(Queue* pq)
{
//assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* behind = cur->next;
free(cur);
cur = behind;
}
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("QueuePush");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
if (pq->phead == NULL)
{
assert(pq->ptail == NULL);
pq->phead = newnode;
pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
QNode* cur = pq->phead;
pq->phead = pq->phead->next;
free(cur);
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->phead->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return (pq->phead == NULL && pq->ptail == NULL);
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"Queue.h"
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
BTNode* BTBuyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("node");
return NULL;
}
node->data = x;
node->left = NULL;
node->right = NULL;
return node;
}
BTNode* CreatBinaryTree()
{
BTNode* node1 = BTBuyNode(1);
BTNode* node2 = BTBuyNode(2);
BTNode* node3 = BTBuyNode(3);
BTNode* node4 = BTBuyNode(4);
BTNode* node5 = BTBuyNode(5);
BTNode* node6 = BTBuyNode(6);
BTNode* node7 = BTBuyNode(7);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
node5->left = node7;
return node1;
}
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
//二叉树大小
int BTreeSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
else
{
return BTreeSize(root->left) + BTreeSize(root->right) + 1;
}
}
//二叉树叶子结点
int BTreeLeave(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return BTreeLeave(root->left) + BTreeLeave(root->right);
}
//二叉树深度
int BTreeDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int n1 = BTreeDepth(root->left) + 1;
int n2 = BTreeDepth(root->right) + 1;
int n3 = n1 > n2 ? n1 : n2;
return n3;
}
//二叉树第K层有几个节点
int BTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BTreeLevelKSize(root->left, k-1) + BTreeLevelKSize(root->right, k-1);
}
//找二叉树节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
BTNode* ret1 = BinaryTreeFind(root->left, x);
if(ret1)
{
return ret1;
}
BTNode* ret2 = BinaryTreeFind(root->right, x);
if (ret2)
{
return ret2;
}
return NULL;
}
//层序遍历
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->data);
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
}
printf("\n");
QueueDestroy(&q);
}
//二叉树销毁
void BTreeDestory(BTNode* root)
{
if (root == NULL)
{
return;
}
BTreeDestory(root->left);
BTreeDestory(root->right);
free(root);
}
//判断完全二叉树
bool BTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
break;
}
QueuePush(&q,front->left);
QueuePush(&q,front->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
int main()
{
BTNode* root = CreatBinaryTree();
//PrevOrder(root);
//printf("\n");
//InOrder(root);
int a = BTreeSize(root);
int b = BTreeLeave(root);
int c = BTreeDepth(root);
int d = BTreeLevelKSize(root, 3);
BTNode* e = BinaryTreeFind(root, 3);
//printf("%d\n", e->data);
//LevelOrder(root);
printf("%d\n", BTreeComplete(root));
BTreeDestory(root);
return 0;
}