二叉树概念
一棵二叉树是结点的有限集合,该集合或者为空, 或者是由根结点加上两棵分别称为左子树和右子树的二叉树构成
二叉树的特点:
- 每个结点最多有两棵子树,即二叉树不存在度大于2的结点
- 二叉树的子树有左右之分,其子树的次序不能颠倒
满二叉树、完全二叉树
满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子节点都在同一层上
完全二叉树: 如果一棵具有N个结点的二叉树的结构与满二叉树的前N个 结点的结构相同,称为完全二叉树
二叉树性质
- 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 (i>0)个结点
- 若规定只有根节点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 (k>=0)
- 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数 为 n2,则有n0=n2+1
- 具有n个结点的完全二叉树的深度k为上取整
对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序 对所有节点从0开始编号,则对于序号为i的结点有:
- 若i>0,双亲序号:(i-1)/2;
- i=0,i为根节点编号,无双亲结点
- 若2i+1<n,左孩子序号:2i+1,否则无左孩子
- 若2i+2<n,右孩子序号:2i+2,否则无右孩子
二叉树存储结构
二叉树有顺序存储和链式存储结构
顺序存储结构:
对于一棵完全二叉树所有结点按照层序自顶向下,同一层自左向右顺 序编号,就得到一个节点的顺序序列
优点:存储完全二叉树,简单省空间
缺点:存储一般二叉树尤其单支树,存储空间利用不高
链式存储结构:
二叉链结构结点的定义:
typedef int DataType;
typedef struct BinaryTreeNode //二叉树结构定义
{
struct BinaryTreeNode* _left; //指向当前结点左孩子
struct BinaryTreeNode* _right; //指向当前结点右孩子
DataType _data; //当前结点数据
}BTNode;
三叉链结构结点定义:
typedef int DataType;
typedef struct BinaryTreeNode //二叉树结构定义
{
struct BinaryTreeNode* _pParent; //指向当前节点的双亲结点
struct BinaryTreeNode* _pLeft; //指向当前结点左孩子
struct BinaryTreeNode* _pRight; //指向当前结点右孩子
ataType _data; //当前结点数据
}BTNode;
二叉树的操作 (面试题)
创建二叉树:
对二叉树进行其他操作的基础是要创建出二叉树,我们采用递归的方式创建二叉树,从根结点开始先处理它的左子树,再处理它的右子树(对子树也是同样的),递归是比较抽象的,所以我先给出实现的代码,然后详细说一下递归是如何实现二叉树创建的
// 创建二叉树
//a:数组指针 pIndex:下标指针 invalib:给的非结点元素,用其表示一个结点无孩子(左、右)
BTNode* CreateBTree(DataType* a, size_t* pIndex, DataType invalid)
{
assert(a);
if (a[*pIndex] == invalid)
return NULL;
BTNode* root = BuyBTNode(a[*pIndex]);
++(*pIndex);
root->_left = CreateBTree(a,pIndex,invalid);
++(*pIndex);
root->_right = CreateBTree(a, pIndex, invalid);
return root;
}
思考:创建二叉树是参数为什么给的是指针变量 pIndex,而不是变量index?
我在走递归过程时以上图中已经很好的体现了,相信对这块的理解没有问题
前序遍历
- 递归实现
void BTreePrevOrder(BTNode* root) //前序遍历
{
if (root == NULL)
return;
printf("%d ",root->_data);
BTreePrevOrder(root->_left);
BTreePrevOrder(root->_right);
}
- 非递归实现
void BTreePrevOrderNonR(BTNode* root) //非递归前序遍历
{
assert(root);
Stack s;
StackInit(&s);
BTNode* cur = root;
while (cur != NULL || StackEmpty(&s) != 0) {
while (cur) {
printf("%d ",cur->_data);
StackPush(&s,cur);
cur = cur->_left;
}
BTNode* top = StackTop(&s);
cur = top->_right;
StackPop(&s);
}
}
中序遍历
- 递归实现
void BTreeInOrder(BTNode* root) //中序遍历
{
if (root == NULL)
return;
BTreeInOrder(root->_left);
printf("%d ",root->_data);
BTreeInOrder(root->_right);
}
- 非递归实现
void BTreeInOrderNonR(BTNode* root) //非递归中序遍历
{
assert(root);
Stack s;
StackInit(&s);
BTNode* cur = root;
while (cur != NULL || StackEmpty(&s) != 0) {
while (cur) {
StackPush(&s, cur);
cur = cur->_left;
}
BTNode* top = StackTop(&s);
printf("%d ", top->_data);
cur = top->_right;
StackPop(&s);
}
}
后序遍历
- 递归实现
void BTreePostOrder(BTNode* root) //后序遍历
{
if (root == NULL)
return;
BTreePostOrder(root->_left);
BTreePostOrder(root->_right);
printf("%d ",root->_data);
}
- 非递归实现
void BTreePostOrderNonR(BTNode* root) //非递归后序遍历
{
Stack s;
StackInit(&s);
BTNode* cur = root;
BTNode* prev = NULL; //记录被遍历的前一个结点
while (cur != NULL || StackEmpty(&s) != 0) {
while (cur) {
StackPush(&s, cur);
cur = cur->_left;
}
BTNode* top = StackTop(&s);
if (top->_right == NULL || top->_right == prev)
{
printf("%d ", top->_data);
StackPop(&s);
prev = top;
}
else
cur = top->_right;
}
}
层序遍历
void BTreeLevelOrder(BTNode* root) //层序遍历
{
assert(root);
Queue q;
QueueInit(&q);
QueuePush(&q,root);
while (QueueEmpty(&q) != 0) {
BTNode* head = QueueHead(&q);
printf("%d ",head->_data);
QueuePop(&q);
if (head->_left != NULL)
QueuePush(&q,head->_left);
if (head->_right != NULL)
QueuePush(&q, head->_right);
}
}
结点个数
size_t BTreeSize(BTNode* root) //结点个数
{
if (root == NULL)
return 0;
//左子树的个数+右子树的个数
return 1 + BTreeSize(root->_left) + BTreeSize(root->_right);
}
叶子结点个数
size_t BTreeLeafSize(BTNode* root) //叶子结点的个数
{
if (root == NULL) //这个返回条件不能不写
return 0;
if (root->_left == NULL && root->_right == NULL)
return 1;
//左子树叶结点+右子树叶结点
return BTreeLeafSize(root->_left) + BTreeLeafSize(root->_right);
}
第K层结点个数
size_t BTreeKLevelSize(BTNode* root, size_t k) //第k层的结点个数
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
//左子树第k-1层的结点数+右子树第k-1层的结点树
return BTreeKLevelSize(root->_left,k-1) + BTreeKLevelSize(root->_right,k-1);
}
二叉树的深度
size_t BTreeDepth(BTNode* root) //二叉树的深度
{
if (root == NULL)
return 0;
//返回左子树和右子树中较深的(加1:根节点)
return (1 + BTreeDepth(root->_left)) >= (1 + BTreeDepth(root->_right)) ? (1+BTreeDepth(root->_left)) : (1+BTreeDepth(root->_right));
}
判断一个节点是否在一棵二叉树中
BTNode* BTreeFind(BTNode* root, BTNDataType x) //查看某个结点是否存在
{
if (root == NULL)
return NULL;
if (root->_data == x)
return root;
BTNode* ret;
ret = BTreeFind(root->_left,x);
if (ret != NULL)
return ret;
ret = BTreeFind(root->_right,x);
return ret;
}
判断一棵二叉树是否是完全二叉树
int IsCompleteBTree(BTNode* root) //判断是否是完全二叉树
{
assert(root);
Queue q;
QueueInit(&q);
QueuePush(&q,root);
while (QueueEmpty(&q) != 0) {
BTNode* head = QueueHead(&q);
QueuePop(&q);
if (head == NULL)
break;
if (head->_left != NULL || head->_right != NULL)
{
QueuePush(&q,head->_left);
QueuePush(&q,head->_right);
}
}
while (QueueEmpty(&q) != 0) {
BTNode* head = QueueHead(&q);
if (head == NULL)
{
if (QueueEmpty(&q) != 0)
return 0; //返回0表示不是完全二叉树
else
return 1; //返回1表示是完全二叉树
}
else
QueuePop(&q);
}
return 1;
}
求二叉树中两个结点的最近公共祖先结点
判断一棵二叉树是否是平衡二叉树
求二叉树中最远的两个结点之间的距离
有前序遍历和中序遍历重建二叉树(前序遍历结果:1,2,3,4,5,6 中序 遍历结果:4,2,5,1,6,3)
求二叉树的镜像
将二叉搜索树转换成一个排序的双向链表。要求:不能创建任何新的 结点,只能调整树种结点指针的指向