1.创建二叉树
(1)创建结构体
typedef int BTDataType;typedef struct BinaryTreeNode
{BTDataType _data;struct BinaryTreeNode* _left;struct BinaryTreeNode* _right;
}BTNode;
(2)根据前序遍历构建树
//通过前序遍历的数组"123##45##6##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{if (*pi >= n || *pi == '#'){(*pi)++;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->_data = a[(*pi)++];root->_left = BinaryTreeCreate(a, n, pi);root->_right = BinaryTreeCreate(a, n, pi);return root;
}
2.前序、中序及后序遍历
学习二叉树结构,最简单的就是遍历,所谓二叉树遍历是按照某中特定结构的规则,依次对二叉树中的结点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一,也是二叉树上其他运算的基础。
按照规则,二叉树的遍历有:前序、中序、后序的递归结构遍历。
1.前序遍历:访问根节点的操作在左右子树之前。
2.中序遍历:访问根节点的操作发生在遍历其左右子树之间。
3.后序编辑:访问根节点的操作发生在遍历其左右子树之后。
由于被访问的节点必是某子树的根,所以N、L、R又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别被称为先根遍历、中根遍历和后根遍历。
递归的本质
1. 当前问题
2. 子问题
3. 返回条件:最小规模的子问题
(1)二叉树前序遍历
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL)return;printf("%d ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}
图解:
(2)中序遍历
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL)return;BinaryTreePrevOrder(root->_left);printf("%d ", root->_data);BinaryTreePrevOrder(root->_right);
}
(3)后序遍历
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL)return;BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);printf("%d ", root->_data);
}
前序遍历结果:1 2 3 4 5 6中序遍历结果:3 2 1 5 4 6后序遍历结果:3 2 5 6 4 1
3. 节点个数及高度等
(4)二叉树节点个数
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL)return 0;return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}
(5)二叉树叶子节点个数
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->_left == NULL && root->_right == NULL)return 1;return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
(6)二叉树第K层节点个数
空节点:返回0
K == 1时,返回1
K > 1 时,转化成找左右子树的K-1层的节点个数
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;if (k > 1)return BinaryTreeLevelKSize(root->_left, k - 1) +BinaryTreeLevelKSize(root->_right, k - 1);
}
(7)二叉树查找值为x的节点
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->_data == x)return root;//要用left接收递归BTNode* left = BinaryTreeFind(root->_left, x);if (left)//如果在左树找到x节点,就直接返回,不用再进右子树中找return left;return BinaryTreeFind(root->_right, x);
}
4. 二叉树的销毁
// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{if (root == NULL)return;BinaryTreeDestory(root->_left);BinaryTreeDestory(root->_right);free(root);
}
5.层序遍历
设二叉树的根结点所在 层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层 上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
思路:
用到队列(先入先出),队列结构中的数据data指向的是二叉树结构体。
二叉树根节点不为空时,入队列,根节点出队列时带着左右孩子入队列。
注意:这里根节点出队列时,释放的是队列的节点,不影响树的节点
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//结构体指针
typedef struct BinaryTreeNode* QDataType;
// 链式结构:表示队列
typedef struct QListNode
{struct QListNode* next;QDataType data;
}QNode;//队列的结构
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);// 初始化队列
void QueueInit(Queue* q)
{assert(q);q->phead = NULL;q->ptail = NULL;q->size = 0;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return;}newnode->next = NULL;newnode->data = data;if (q->ptail == NULL){q->phead = q->ptail = newnode;}else{q->ptail->next = newnode;q->ptail = newnode;}q->size++;
}
// 队头出队列
void QueuePop(Queue* q)
{assert(q);assert(q->size);if (q->phead->next == NULL)//只有最后一个节点{free(q->phead);q->phead = q->ptail = NULL;}else//多个节点{QNode* next = q->phead->next;free(q->phead);q->phead = next;}q->size--;
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{assert(q);return q->phead->data;
}// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q)
{assert(q);return q->size == 0;
}
// 销毁队列
void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}q->phead = q->ptail = NULL;q->size = 0;
}// 层序遍历
//用队列,上一层带下一层
void BinaryTreeLevelOrder(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 != NULL)QueuePush(&q, front->_left);if (front->_right != NULL)QueuePush(&q, front->_right);}QueueDestroy(&q);
}
6.判断二叉树是否为完全二叉树
完全二叉树是基于数组存储的,其存储特点是数组中最后一层数据是连续存放的(可以不存满),空节点和非空节点是连续的(如果后面全空就是完全二叉树,后面有非空就不是完全二叉树)。
思路:参考上面层序遍历的思想,用队列来实现
根节点出队列时带着左右孩子进队列,当队列出现第一个空节点就开始判断,当后序再出现非空节点就说明不是完全二叉树,后面全空就是完全二叉树。
注意:
不可能出现遇到空节点,但后面还有非空节点没进队列的情况。
当空节点后面有非空,那么该非空是前面非空节点的孩子。
当层序出到空时,前面非空都出完了,那他的孩子一定进队列了。
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//结构体指针
typedef struct BinaryTreeNode* QDataType;
// 链式结构:表示队列
typedef struct QListNode
{struct QListNode* next;QDataType data;
}QNode;//队列的结构
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);// 初始化队列
void QueueInit(Queue* q)
{assert(q);q->phead = NULL;q->ptail = NULL;q->size = 0;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return;}newnode->next = NULL;newnode->data = data;if (q->ptail == NULL){q->phead = q->ptail = newnode;}else{q->ptail->next = newnode;q->ptail = newnode;}q->size++;
}
// 队头出队列
void QueuePop(Queue* q)
{assert(q);assert(q->size);if (q->phead->next == NULL)//只有最后一个节点{free(q->phead);q->phead = q->ptail = NULL;}else//多个节点{QNode* next = q->phead->next;free(q->phead);q->phead = next;}q->size--;
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{assert(q);return q->phead->data;
}// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q)
{assert(q);return q->size == 0;
}
// 销毁队列
void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}q->phead = q->ptail = NULL;q->size = 0;
}// 判断二叉树是否是完全二叉树
//数组中最后一层数据是连续存放的(可以不满)
//空和非空分别是连续的
//出上一层带下一层进去,当遇到第一个空节点开始判断
//如果后面全空就是完全二叉树,但如果有非空就不是完全二叉树//不可能出现,遇到空时,后面还有非空没进队列
//后面非空,一定是前面非空项的孩子
//当层序出到空时,前面非空都出完了,那他的孩子一定进队列了
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root != NULL)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){//再遇到第一个空后,出现了非空,说明不是完全二叉树BinaryTreeDestory(front);return false;}QueuePush(&q, front->_left);QueuePush(&q, front->_right);}return true;
}
7.练习题
已知二叉树的前序遍历和中序遍历,画出其图: