目录
一、树的概念
二、二叉树概念及结构
三、二叉树链式结构的实现
四、二叉树的相关操作实现(均以(三)中创建的二叉树为例)
1、树:树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
* 有一个特殊的结点,称为根结点,根节点没有前驱结点。
2、树的相关概念:(部分)
* 节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:D的为3
* 叶节点或终端节点:度为0的节点称为叶节点; 如上图:F、G、H、I、J节点为叶节点
既保存值域,也要保存结点和结点之间的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。这里就简单的了解其中最常用的孩子兄弟表示法。
typedef int DataType;
struct Node
{
struct Node* firstChild1; // 第一个孩子结点
struct Node* NextBrother; // 指向其下一个兄弟结点
DataType _data; // 结点中的数据域
};
(中间为数据域,两边为指针域。C表示child, B表示brother)
注:1. 二叉树不存在度大于2的结点 。2. 二叉树的子树有左右之分,次序不能颠倒。
2、特殊的二叉树:
3、二叉树的性质:
typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
struct BinTreeNode* left; // 指向当前节点左孩子
struct BinTreeNode* right; // 指向当前节点右孩子
BTDataType data; // 当前节点值域
}
// 三叉链
struct BinaryTreeNode
{
struct BinTreeNode* parent; // 指向当前节点的双亲
struct BinTreeNode* left; // 指向当前节点左孩子
struct BinTreeNode* right; // 指向当前节点右孩子
BTDataType data; // 当前节点值域
}
typedef int BTDataType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BTDataType data;
}BTNode;
//创建一个节点
BTNode* BuyNode(BTDatatype x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
assert(node);
node->data = x;
node->left = NULL;
node->right = NULL;
return node;
}
//创建一棵树
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
return node1;
}
注:上述代码并不是正确的创建二叉树的方式。
1、二叉树的遍历:二叉树遍历是按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
* 前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发生在遍历其左右子树之前。
即:根——左子树——右子树
// 二叉树前序遍历
void PreOrder(BTNode* root)
{
if(root == NULL)
{
printf("# ");
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
* 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中。
即:左子树——根——右子树
// 二叉树中序遍历
void InOrder(BTNode* root)
{
if(root == NULL)
{
printf("# ");
return;
}
InOrder(root->left)
printf("%d ", root->data);
InOrder(root->right);
}
void Postorder(BTNode* root)
{
if (root == NULL)
{
printf("# ");
return;
}
Postorder(root->left);
Postorder(root->right);
printf("%d ", root->data);
}
2、求二叉树结点个数:
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if(root == NULL)
return 0;
int left = BinaryTreeSize(root->left); //左子树结点个数
int right = BinaryTreeSize(root->right); //右子树结点个数
return left + right + 1;
}
3、求二叉树叶子节点个数:
//叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if(root == NULL)
return 0;
if(root->left == NULL && root->right == NULL)
return 1;
int left = BinaryTreeLeafSize(root->left); //左子树叶子结点个数
int right = BinaryTreeLeafSize(root->right); //右子树叶子结点个数
return left + right;
}
4、求二叉树第K层节点个数:递归求子树的第K-1层节点个数,直到k为1
//第K层节点个数:递归求子树的第K-1层节点个数,直到k为1
int BinaryTreeLevelkSize(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
int left = BinaryTreeLevelkSize(root->left, k - 1);
int right = BinaryTreeLevelkSize(root->right, k - 1);
return left + right;
}
5、二叉树查找结点值为x的结点
//二叉树查找结点值为x的结点
BTNode* BinaryTreeFind(BTNode* root, int x)
{
if(root == NULL)
return NULL;
if(root->data == x)
return root;
return BinaryTreeFind(root->left, x) ||
BinaryTreeFind(root->right, x);
}
6、二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
if(root == NULL)
return;
BinaryTreeDestory(root->left);
BinaryTreeDestory(root->right);
free(root);
}
7、求树的深度 :左子树深度与右子树深度的和
int TreeDepth(BTNode* root)
{
if(root == NULL)
return 0;
int leftDepth = TreeDepth(root->left);
int rightDepth = TreeDepth(root->right);
return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
8、层序遍历:层序遍历是指从最上层开始,一层一层的从左到右遍历,而不再是先左后右或先右后左。层序遍历和广度优先搜索的思路比较像。 这里我们就需要一个队列来帮助我们实现这个算法。
基本思路如下图所示 :如果根不为空,则入队列。然后当队列不为空时,就取出并删除队头数据,打印,并判断其左子树和右子树为不为空,不为空就入队列,如此反复。直至队列为空。
* 我们先简单实现一个队列
typedef int Datatype;
typedef struct QueueNode
{
struct QueueNode* next;
Datatype data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
}Queue;
//初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
//销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
//队尾入队列
void QueuePush(Queue* pq, Datatype x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
printf("malloc is fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
//队头出队列
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head);
QNode* cur = pq->head->next;
if (cur == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
free(pq->head);
pq->head = cur;
}
}
//取队头数据
Datatype QueueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
//取队尾的数据
Datatype QueueBack(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->tail->data;
}
//计算数据的个数
Datatype Queuesize(Queue* pq)
{
assert(pq);
int size = 0;
QNode* cur = pq->head;
while (cur)
{
cur = cur->next;
size++;
}
return size;
}
//判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
* 接着我们使用队列来实现二叉树的层序遍历
//层序遍历
void levelOrder(BTNode* root)
{
Queue q;//创建一个队列
QueueInit(&q);
if(root)
QueuePush(&q, root);
while(!QueueEmpty(&q))
{
BTNode* cur = QueueFront(&q);
printf("%d ", cur->data);
QueuePop(&q);
if(root->left)
QueuePush(&q, root->left);
if(root->right)
QueuePush(&q, root->right);
}
printf("\n");
QueueDestory(&q);
}
9、判断二叉树是否是完全二叉树(也是使用层序遍历的思想)
//判断一个二叉树是不是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
Queue* q;
QueueInit(&q);
if(root)
QueuePush(root);
while(!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
//取队头数据,如果队头数据不为空,就将其左子树和右子树入队列
if(front)
{
QueuePush(front->left);
QueuePush(front->right);
}
//遇到取出的数据为空,就结束层序遍历
else
{
break;
}
}
//结束上个层序遍历后,我们就开始出队头数据
while(!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if(front)
return false;
}
//如果最后队列为空出循环了,就说明此树是完全二叉树
return true;
QueueDestroy(&q);
}