二叉树的基本概念
结点 : 即树的数据元素
结点的度 : 结点挂接的子树数(有几个直接后继就是几度)
结点的层次 : 从根到该结点的层数(根结点算第一层)
终端结点 :即度为0的结点,即叶子
分支结点 : 除树根以外的结点(也称为内部结点)
树的度 :所有结点度中的最大值(Max{各结点的度})
树的深度(或高度) : 指所有结点中最大的层数(Max{各结点的层次})
二叉树性质
1: 在二叉树的第i层上至多有2i-1个结点(i>0)
2: 深度为k的二叉树至多有2k-1个结点(k>0)
3: 对于任何一棵二叉树,若度为2的结点数有n2个,则叶子数(n0)必定为n2+1 (即n0=n2+1)
4 具有n个结点的完全二叉树的深度必为[log2n]+1,k=log2n+1==2的k次方-1=n,log2n求的是对数,也就是以2为底数的指数,例子:假如节点是7,那
2的k次方-1=7,k=3,深度就利用这个公式就求出来了
满二叉树:一颗深度为k的二叉树并且有2k-1个结点(k>0)的树(每层充满了节点)
如图:深度为3的满二叉树
完全二叉树:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点(k-1层与满二叉树完全相同,第k层结点尽力靠左)
如图:深度为4的完全二叉树
二叉树遍历
main.c
#include<stdio.h>
typedef struct TREENODE
{
char ch;
struct TREENODE* Left;
struct TREENODE* right;
}TreeNode;
//遍历二叉树
void ReverseTree(TreeNode* root)
{
if (NULL == root)//递归判断条件,每一个节点都为根结点遍历
{
return ;
}
ReverseTree(root->Left);
ReverseTree(root->right);
printf("%c\n",root->ch);//后序遍历
}
int main()
{
TreeNode Node1, Node2, Node3, Node4, Node5, Node6, Node7, Node8;
//初始化
Node1.ch = 'A'; Node1.Left = NULL; Node1.right = NULL;
Node2.ch = 'B'; Node2.Left = NULL; Node2.right = NULL;
Node3.ch = 'C'; Node3.Left = NULL; Node3.right = NULL;
Node4.ch = 'D'; Node4.Left = NULL; Node4.right = NULL;
Node5.ch = 'E'; Node5.Left = NULL; Node5.right = NULL;
Node6.ch = 'F'; Node6.Left = NULL; Node6.right = NULL;
Node7.ch = 'G'; Node7.Left = NULL; Node7.right = NULL;
Node8.ch = 'H'; Node8.Left = NULL; Node8.right = NULL;
//建立二叉树关系
Node1.Left = &Node2;
Node1.right = &Node6;
Node2.Left = NULL;
Node2.right = &Node3;
Node3.Left = &Node4;
Node3.right = &Node5;
Node6.Left = NULL;
Node6.right = &Node7;
Node7.Left = &Node8;
Node7.right = NULL;
//遍历二叉树
ReverseTree(&Node1);
getchar();
return 0;
}
节点关系如图:
打印结果:DECBHGFA
利用栈空间遍历二叉树
BinTree.h
#pragma once
class CBinarySerchTree
{
private:
struct TreeNode
{
TreeNode(const int& nVal) :m_nVal(nVal) {}
int m_nVal = 0; //数据
TreeNode* m_pParent = nullptr; //父亲
TreeNode* m_pLeft = nullptr; //左孩子
TreeNode* m_pRight = nullptr; //右孩子
};
public:
CBinarySerchTree();
~CBinarySerchTree();
void Insert(const int& nVal); //插入
//遍历
void Layer(); //层序遍历
void LDR(); //中序遍历
void _LDR(TreeNode* pNode); //中序遍历
void LDR_LOOP();//循环遍历
void DLR(); //前序遍历
void _DLR(TreeNode* pNode);
void DLR_LOOP();//循环遍历
void LRD(); //后序遍历
void _LRD(TreeNode* pNode);
void LRD_LOOP();//循环遍历
private:
TreeNode* m_pRoot = nullptr; //根结点
};
BinTree.cpp
/*
根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,
直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下:
对于任一结点P,
1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行入栈,这时对于每一颗子树都栈顶都是优先存储左孩子
2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子,这时出栈输出左节点和根节点,然后去
访问右子树,对右子树看做根节点循环1,2步骤
3)直到P为NULL并且栈为空则遍历结束
*/
void CBinarySerchTree::LDR_LOOP()
{
TreeNode* pNode = m_pRoot;
stack<TreeNode*> sk;
while (true)
{
//把跟节点和左节点依次保存到栈中,出栈后说明访问完了左跟节点,然后会保存右节点,每个节点都当做跟节点
while (pNode != nullptr)
{
sk.push(pNode);
pNode = pNode->m_pLeft;
}
//栈中为空说明遍历所有节点
if (sk.empty())
{
break;
}
//左跟出栈把左跟节点依次输出然后在把右边节点当做根节点继续循环
pNode = sk.top();
cout << pNode->m_nVal << " ";
pNode = pNode->m_pRight;
sk.pop();
}
}
/*
根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下:
对于任一结点P:
1)访问结点P,并将结点P入栈;
2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1); 若不为空,则将P的左孩子置为当前的结点P;
3)直到P为NULL并且栈为空,则遍历结束。
*/
void CBinarySerchTree::DLR_LOOP()
{
TreeNode* pNode = m_pRoot;
stack<TreeNode*> sk;
while (true)
{
//对于任意节点来说,先把节点入栈,如果有左孩子,此时把左孩子当做根节点入栈(pNode=PNode->left),如果左孩子为空,就取出栈顶跟节点
//把右节点看做跟节点入栈(pNode=pNode->right)
while (pNode != nullptr)
{
cout << pNode->m_nVal << " ";
sk.push(pNode);
pNode = pNode->m_pLeft;
}
//栈中为空说明遍历所有节点
if (sk.empty())
{
break;
}
//如果左孩子为null,者出栈取出跟节点把右节点入栈继续循环
pNode = sk.top();
pNode = pNode->m_pRight;
sk.pop();
}
}
//后序遍历,后序稍微复杂,注意右孩子先访问才去访问跟
void CBinarySerchTree::LRD_LOOP()
{
TreeNode* pNode = m_pRoot;
TreeNode* pLast = nullptr;
stack<TreeNode*> sk;
while (true)
{
while (pNode != nullptr)//首先访问左节点
{
sk.push(pNode);
pNode = pNode->m_pLeft;
}
//栈中为空说明遍历所有节点
if (sk.empty())
{
break;
}
pNode = sk.top();
if (pNode->m_pRight == nullptr || pLast == pNode->m_pRight)//叶子节点或者上一次已经处理过右节点(这是该输出跟节点)
{
pLast = pNode;
cout << pNode->m_nVal << " ";
sk.pop();
pNode = nullptr;//取栈
}
else
{
pNode = pNode->m_pRight;
}
}
}
模板实现平衡二叉树
TemplateAvl.h
#pragma once
#include <stack>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
using namespace std;
enum MyEnum
{
BIG,
SMALL,
EQUAL,
ID_EQUAL,
NAME_EQUAL
};
template<typename T>
class CBinaryTreeTemplate
{
public:
struct TreeNode
{
TreeNode(const T Val)
{
m_Data = Val;
//m_nOffset = 0;
m_nHeight = 1;
m_pParent = NULL;
m_pLeft = NULL;
m_pRight = NULL;
m_pNext = NULL;
}
T m_Data;//数据
//int m_nOffset;//存储在文件中的偏移量
int m_nHeight; //叶子结点的高度
TreeNode* m_pParent;
TreeNode* m_pLeft;
TreeNode* m_pRight;
TreeNode* m_pNext;
};
//用函数指针调用比较函数
typedef int(*pfnFun)(T, T);
//函数指针查找
typedef void(*pfnPrint)(T);
typedef int(*pfnWrite)(T, FILE*);
public:
CBinaryTreeTemplate()
{
m_pRoot = NULL;
}
~CBinaryTreeTemplate()
{
}
//打印
void PrintNode(TreeNode* pNode,pfnPrint pfnPrintFun)
{
if (IsList(pNode))
{
TreeNode* pTempNode = pNode;
while (pTempNode != NULL)
{
pfnPrintFun(pTempNode->m_Data);
pTempNode = pTempNode->m_pNext;
}
}
else
{
pfnPrintFun(pNode->m_Data);
}
}
//查找要修改的节点
T FindModifData(T Data, pfnFun pfnCompare)
{
T tmp;
TreeNode* pNode = _Find(Data, pfnCompare);
if (pNode == NULL)
{
return tmp;
}
else
{
return pNode->m_Data;
}
}
//查找后输出
int FindNode(T Data, pfnFun pfnCompare, pfnPrint pfnPrintFun)
{
TreeNode* pNode = _Find(Data, pfnCompare);
if (pNode == NULL)
{
return -1;
}
PrintNode(pNode, pfnPrintFun);
}
//获取节点
T GetDataNode(T Data, pfnFun pfnCompare, pfnFun pfnCompare2)
{
T tmp;
TreeNode* pNode = _Find(Data, pfnCompare);
if (pNode == NULL)
{
return tmp;
}
if (IsList(pNode))
{
//判断要删除的节点是否挂了链表 挂了链表说明有相同的,这是需要再次查找需要删除链表的哪一个节点
TreeNode* pCurrent = pNode;
//有链表说明其中一个数据不是唯一的,需要进行二次比较
while (pCurrent != NULL)
{
if (pfnCompare2(Data, pCurrent->m_Data) == EQUAL)
{
return pCurrent->m_Data;
}
else
{
pCurrent = pCurrent->m_pNext;
}
}
}
else
{
if (pfnCompare2(Data, pNode->m_Data) == EQUAL)
{
return pNode->m_Data;
}
}
}
int CheckId(T Data, pfnFun pfnCompare)
{
TreeNode* pTempNode = _Find(Data, pfnCompare);
if (pTempNode != NULL)
{
return -2;
}
return 0;
}
int CheckName(T Data, pfnFun pfnCompare)
{
T TreeNode* pTempNode = _Find(Data, pfnCompare);
if (pTempNode != NULL)
{
return -2;
}
return 0;
}
//插入
int Insert(const T Data, pfnFun pfnCompare)
{
TreeNode* pNewNode = new TreeNode(Data);
if (NULL == m_pRoot)
{
m_pRoot = pNewNode;
return 0;
}
//遍历的指针
TreeNode* pNode = m_pRoot;
while (true)
{
//大于放右边
if (pfnCompare(pNewNode->m_Data, pNode->m_Data) == BIG)
{
if (NULL == pNode->m_pRight)
{
pNode->m_pRight = pNewNode;
pNewNode->m_pParent = pNode;
break;
}
else
{
pNode = pNode->m_pRight;
}
}
//小于放左边
else if (pfnCompare(pNewNode->m_Data, pNode->m_Data) == SMALL)
{
if (NULL == pNode->m_pLeft)
{
pNode->m_pLeft = pNewNode;
pNewNode->m_pParent = pNode;
break;
}
else
{
pNode = pNode->m_pLeft;
}
}
//相等挂链表
else if (pfnCompare(pNewNode->m_Data, pNode->m_Data) == EQUAL)
{
while (pNode->m_pNext != NULL)
{
pNode = pNode->m_pNext;
}
//节点挂在后面
pNode->m_pNext = pNewNode;
break;
}
}
AdjustHeight(pNewNode);
return 0;
}
//调整更新高度和平衡树
void AdjustHeight(TreeNode* pNode)
{
TreeNode* pCurrent = pNode;
while (pCurrent != NULL)
{
//调整所有节点的高度
pCurrent->m_nHeight = std::max(GetHeight(pCurrent->m_pLeft), GetHeight(pCurrent->m_pRight)) + 1;
//左比右高右旋
if (GetHeight(pCurrent->m_pLeft) - GetHeight(pCurrent->m_pRight) > 1)
{
TreeNode* pB = pCurrent->m_pLeft;
if (GetHeight(pB->m_pLeft) >= GetHeight(pB->m_pRight))
{
/*
B的左孩子比右孩子高或者相等都只旋转一次那树的形状便是如下图,此时对C右旋一次就平衡了
C B
B --> A C
A
*/
RotateRight(pCurrent);
}
else
{
/*
B的左孩子比右孩子低那树的形状便是如下图,先对B进行左旋,再对C右旋
C C A
B --> A --> B C
A B
*/
RotateLeft(pB);
RotateRight(pCurrent);
}
}
//右比左高左旋
else if (GetHeight(pCurrent->m_pRight) - GetHeight(pCurrent->m_pLeft) > 1)
{
TreeNode* pB = pCurrent->m_pRight;
if (GetHeight(pB->m_pRight) >= GetHeight(pB->m_pLeft))
{
/*
B的右孩子比左孩子高那树的形状便是如下图,此时对C左旋一次就平衡了
C B
B --> C A
A
*/
RotateLeft(pCurrent);
}
else
{
/*
B的右孩子比左孩子低那树的形状便是如下图,先对B进行右旋,再对C左旋
C C A
B --> A --> C B
A B
*/
RotateRight(pB);
RotateLeft(pCurrent);
}
}
pCurrent = pCurrent->m_pParent;
}
}
//获取树高度
int GetHeight(TreeNode* pNode)
{
if (pNode == NULL)
{
return 0;
}
return pNode->m_nHeight;
}
//右旋
void RotateRight(TreeNode* pNode)
{
/*
C的左孩子与右孩子的高度差大于1, 向右旋转
(节点有k1,k2和p的时候包含三个节点为空的情况 C->B->A)
P P
C B
B k1 --> A C
A k2 k2 k1
*/
TreeNode* pP = pNode->m_pParent;
TreeNode* pC = pNode;
TreeNode* pB = pC->m_pLeft;
TreeNode* pA = pB->m_pLeft;
//TreeNode* pK1 = pC->m_pRight;
TreeNode* pK2 = pB->m_pRight;
if (pP == NULL)
{
m_pRoot = pB;
}
else
{
if (pP->m_pLeft == pC)
{
pP->m_pLeft = pB;
}
else
{
pP->m_pRight = pB;
}
}
//右旋改变c,b的位置和这两个节点子节点的位置
pC->m_pParent = pB;
pC->m_pLeft = pK2;
pB->m_pRight = pC;
pB->m_pParent = pP;
if (pK2 != nullptr)
{
pK2->m_pParent = pC;
}
pC->m_nHeight = std::max(GetHeight(pC->m_pLeft), GetHeight(pC->m_pRight)) + 1;
pB->m_nHeight = std::max(GetHeight(pB->m_pLeft), GetHeight(pB->m_pRight)) + 1;
}
//左旋转
void RotateLeft(TreeNode* pNode)
{
/*
C的右孩子与左孩子的高度差大于1, 向左旋转
P P
C B
k1 B --> C A
k2 A K1 k2
*/
TreeNode* pP = pNode->m_pParent;
TreeNode* pC = pNode;
TreeNode* pB = pC->m_pRight;
TreeNode* pA = pB->m_pRight;
//TreeNode* pK1 = pC->m_pLeft;
TreeNode* pK2 = pB->m_pLeft;
if (pP == NULL)
{
m_pRoot = pB;
}
else
{
if (pP->m_pLeft == pC)
{
pP->m_pLeft = pB;
}
else
{
pP->m_pRight = pB;
}
}
//左旋改变c,b的位置和这两个节点子节点的位置
pC->m_pParent = pB;
pC->m_pRight = pK2;
pB->m_pLeft = pC;
pB->m_pParent = pP;
if (pK2 != nullptr)
{
pK2->m_pParent = pC;
}
//先改变子高度,父节点高度才不会出错
pC->m_nHeight = std::max(GetHeight(pC->m_pLeft), GetHeight(pC->m_pRight)) + 1;
pB->m_nHeight = std::max(GetHeight(pB->m_pLeft), GetHeight(pB->m_pRight)) + 1;
}
//从学生id树中获取课程id,依次删除课程后在依次删除自己这棵树中的节点
int DeleteAll(T Val, pfnFun pfnCompare, pfnFun pfnCompare2, CBinaryTreeTemplate& SStudentNameTree, pfnWrite pfnFile,FILE* fp)
{
TreeNode* pNode = _Find(Val, pfnCompare);
if (pNode == NULL)
{
return -1;
}
if (IsList(pNode))
{
TreeNode* pSelfNodeNext = pNode->m_pNext;
TreeNode* pCurrent = pNode;
//遍历链表依次删除另外一棵树的节点
while (pCurrent != NULL)
{
SStudentNameTree.Delete(pCurrent->m_Data, pfnCompare2, pfnCompare);
pCurrent = pCurrent->m_pNext;
}
//删除自己的树中的链表节点,先把链表删了在删除第一个二叉树节点 这样可以保持树平衡
while (pSelfNodeNext != NULL)
{
pfnFile(pSelfNodeNext->m_Data, fp);//文件也同步删除
TreeNode* pTempNode = pSelfNodeNext->m_pNext;
delete pSelfNodeNext;
pSelfNodeNext = pTempNode;
}
//最后删除第一个二叉树节点
pfnFile(pNode->m_Data, fp);//文件也同步删除
_DeleteNode(pNode);
}
else
{
TreeNode* pParent = pNode->m_pParent;
SStudentNameTree.Delete(pNode->m_Data, pfnCompare2, pfnCompare);
pfnFile(pNode->m_Data, fp);//同步删除后的文件
_DeleteNode(pNode);
//删除之后直接判断是否平衡,平衡就旋转
AdjustHeight(pParent);
return 0;
}
}
//删除节点
//第一个参数是用来查找时(按什么插入就按什么查找)的比较,第二个是删除时的比较(比较第二个数据)
int Delete(T Val, pfnFun pfnCompare, pfnFun pfnCompare2)
{
TreeNode* pNode = _Find(Val, pfnCompare);
if (pNode == NULL)
{
return -1;
}
if (IsList(pNode))
{
//判断要删除的节点是否挂了链表 挂了链表说明有相同的,这是需要再次查找需要删除链表的哪一个节点
TreeNode* pCurrent = pNode;
TreeNode* pCurrentNext = pCurrent->m_pNext;
//有链表说明其中一个数据不是唯一的,需要进行二次比较
while (pCurrent->m_pNext != NULL)
{
//如果是查找的节点刚好与第一个节点相同就直接拷贝数据过去(这样就不需要平衡二叉树了),删除最后一个节点,其他情况只需要删除节点就可以
if (pfnCompare2(Val, pNode->m_Data) == EQUAL)
{
while (pCurrentNext->m_pNext != NULL)
{
//拷贝
pCurrent->m_Data = pCurrentNext->m_Data;
//pCurrent->m_nOffset = pCurrent->m_nOffset;
//移动指针
pCurrent = pCurrentNext;
pCurrentNext = pCurrent->m_pNext;
}
pCurrent->m_Data = pCurrentNext->m_Data;
//pCurrent->m_nOffset = pCurrent->m_nOffset;
pCurrent->m_pNext = NULL;
delete pCurrentNext;
return 0;
}
TreeNode* pCurrentBefor = pCurrent;
pCurrent = pCurrent->m_pNext;
//第一个节点不同就按删除链表的方式删除节点
if (pfnCompare2(Val, pCurrent->m_Data) == EQUAL)
{
TreeNode* pTempCurrentNext = pCurrent->m_pNext;
pCurrentBefor->m_pNext = pTempCurrentNext;
delete pCurrent;
return 0;
}
}
}
else
{
if (pfnCompare2(Val, pNode->m_Data) == EQUAL)
{
TreeNode* pParent = pNode->m_pParent;
_DeleteNode(pNode);
//删除之后直接判断是否平衡,平衡就旋转
AdjustHeight(pParent);
}
else
{
return -1;
}
return 0;
}
}
//查找节点
TreeNode* _Find(T Val, pfnFun pfnCompare)
{
TreeNode* pNode = m_pRoot;
while (true)
{
if (pNode == nullptr)
{
return nullptr;
}
if (pfnCompare(Val, pNode->m_Data) == EQUAL)
{
return pNode;
}
else if (pfnCompare(Val, pNode->m_Data) == BIG)
{
pNode = pNode->m_pRight;
}
else
{
pNode = pNode->m_pLeft;
}
}
return NULL;
}
//是否挂了链表
bool IsList(TreeNode* pNode)
{
if (pNode->m_pNext != NULL)
{
return true;
}
else
{
return false;
}
}
//删除后平衡
void _DeleteNode(TreeNode* pNodeDel)
{
//没有左孩子和右孩子
if (pNodeDel->m_pLeft == nullptr && pNodeDel->m_pRight == nullptr)
{
TreeNode* pParent = pNodeDel->m_pParent;
if (pParent == nullptr)
{
m_pRoot = nullptr;
delete pNodeDel;
return;
}
//被删除结点是父结点的左孩子,父结点左孩子置空
if (pParent->m_pLeft == pNodeDel)
{
pParent->m_pLeft = nullptr;
}
//被删除结点是父结点的右孩子,父结点右孩子置空
else
{
pParent->m_pRight = nullptr;
}
delete pNodeDel;
return;
}
//只有左孩子
if (pNodeDel->m_pLeft != nullptr && pNodeDel->m_pRight == nullptr)
{
/* 删除B
C C
B --> A
A
C C
B --> A
A
*/
TreeNode* pB = pNodeDel;
TreeNode* pC = pB->m_pParent;
TreeNode* pA = pB->m_pLeft;
//删除结点为根结点
if (pC == nullptr)
{
m_pRoot = pA;
pA->m_pParent = nullptr;
delete pB;
return;
}
if (pB == pC->m_pLeft)
{
pC->m_pLeft = pA;
}
else
{
pC->m_pRight = pA;
}
pA->m_pParent = pC;
delete pB;
return;
}
//只有右孩子
if (pNodeDel->m_pLeft == nullptr && pNodeDel->m_pRight != nullptr)
{
/* 删除B
C C
B --> A
A
C C
B --> A
A
*/
TreeNode* pB = pNodeDel;
TreeNode* pC = pB->m_pParent;
TreeNode* pA = pB->m_pRight;
//删除结点为根结点
if (pC == nullptr)
{
m_pRoot = pA;
pA->m_pParent = nullptr;
delete pB;
return;
}
if (pB == pC->m_pRight)
{
pC->m_pRight = pA;
}
else
{
pC->m_pLeft = pA;
}
pA->m_pParent = pC;
delete pB;
return;
}
//同时有左孩子和右孩子
//查找左子树中最大值的结点
TreeNode* pMaxNode = pNodeDel->m_pLeft;
while (pMaxNode->m_pRight != nullptr)
{
pMaxNode = pMaxNode->m_pRight;
}
pNodeDel->m_Data = pMaxNode->m_Data;
_DeleteNode(pMaxNode);
}
public:
TreeNode* m_pRoot;
};