目录
一、二叉树的基本概念
- 二叉树是n(n>=0)个结点的有限集合
1.或者为空二叉树,即n=0。
2.或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一颗二叉树。
特点:1.每个结点至多只有两棵子树。2.左右子树不能颠倒(二叉树是有序树)
- 二叉树的五种状态:
- 满二叉树:一棵高度为h,且含有2^h-1个结点的二叉树。
- 特点:
1.只有最后一层有叶子结点。
2.不存在度为1的结点(结点的度要么为2,要么就是0)。
3.按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1;结点i的父结点为[i/2](i/2然后舍去小数取整)(如果有的话)
- 完全二叉树:当且仅当其每个结点都与高度为h的满二叉树中编号为1到n的结点一一对应时,称为完全二叉树。
- 特点:
1.只有最后两层可能有叶子结点。
2.最多只有一个度为1的结点。
3.按层序从1开始编号,结点i的左孩子为2i,右孩子为2i + 1;结点i的父结点为[i / 2](i / 2然后舍去小数取整)(如果有的话)
完全二叉树:当且仅当其每个结点都与高度为h的满二叉树中编号为1到n的结点一一对应时,称为完全二叉树。
4.当i
- 二叉排序树:一棵二叉树或者空二叉树,或者是具有如下性质的二叉树
1.左子树上所有结点的关键字均小于根结点的关键字
2.右子树上所有结点的关键字均大于根结点的关键字
3.左子树和右子树又各是一棵二叉排序树
补充:二叉排序树可用于元素的排序、搜索
- 平衡二叉树:树上任一结点的左子树和右子树的深度之差不超过1
二、二叉树的性质
(一)二叉树的性质
- *考点1:设非空二叉树中度为0、1和2的结点个数分别为n0,n1和n2,则n0 = n2 + 1(叶子结点比二分支结点多一个)
分析:假设树中结点总数为n,则
1.n = n0 + n1 + n2(树的结点数 = 度为0的结点数 + 度为1的结点数 + 度为2的结点数)
2.n = n1 + 2n2 + 1(树的结点数 = 总度数 + 1,其中度为0的结点数有n0个,度数和为0×n0;度为1的结点数有n1个,度数和为n1;度为2的结点数有n2个,度数和为2n2。所以总度数为n1 + 2n2)
由上面两式相等得到:n0 + n1 + n2 = n1 + 2n2 + 1,所以n0 = n2 + 1
- 考点2:二叉树第i层至多有2 ^ (i – 1)个结点(i >= 1)
- *考点3:高度为h的二叉树至多有2 ^ h – 1个结点(满二叉树)
分析:第一层有1个结点;第二层有2个结点;第三层有2^2个结点…..第h层有2^(h-1)个结点
总共有1+2+2^2……+2^(h-1)=(1-2^h)/(1-2)=2^h-1
(二)完全二叉树的常考性质
- *考点1:具有n个(n>0)结点的完全二叉树的高度为h为[log2(n+1)](2的底)或[log2n]+1(2是底)
分析:
- *考点2:对于完全二叉树,可以由结点数推出度为0、1和2的结点个数为n0、n1和n2
分析:
完全二叉树最多只有一个度为1的结点,即n1=0或1
由于n0=n2+1,所以n0+n2=2n2+1一定是奇数
又推出若完全二叉树有2k个(偶数)个结点,则必有n1=1,n0=k,n2=k-1
若完全二叉树有2k-1个(奇数)个结点,则必有n1=0,n0=k,n2=k-1
三、二叉树的存储结构
(一)二叉树的顺序存储
- *存储完全二叉树:
按照一组地址连续的存储单元依次自上而下、自左至右存储完全二叉树上的结点元素
完全二叉树结点i的的左孩子是2i,右孩子是2i+1,父结点是[i/2](i/2后取整),所在层次是[log2(n+1)](2为底)
- *不是完全二叉树的顺序存储需要增加空结点
- *顺序存储结构
//第一种
typedef struct wqbtree
{
ElemType SBTree[MaxSize];//结点中的数据元素
int n;//记录结点个数
}Fbitree;
//第二种
typedef ElemType SBTree[MaxSize];
(二)二叉树的链式存储结构
二叉树中每一个结点用链表中的一个结点来存储,结构结点如上图。
其中data表示值域,用于存储对应的数据元素;lchild和rchild分别表示左指针域和右指针域 ,分别用于存储左孩子结点和右孩子结点的地址。
- *链式存储结构
typedef struct BTNode
{
ElemType data;//数据域
struct BTNode* lchild, * rchild;//左右孩子指针
}BTNode,*BTree;
四、二叉树的遍历
- *二叉树的递归特性:
1.要么是个空二叉树
2.要么就是由”根结点+左子树+右子树”组成的二叉树
- 二叉树的遍历是指按照一定的次序访问树中的所有结点,并且每个结点仅被访问一次的过程。
- 常见的4中递归遍历方法:
1.先序遍历(DLR)
第一步:访问根结点;第二步先序遍历左子树;第三步:先序遍历右子树。
2.中序遍历(LDR)
第一步:中序遍历左子树;第二步:访问根节点;第三步:中序遍历右子树。
3.后序遍历(LRD)
第一步:后序遍历左子树;第二步:后序遍历右子树;第三步:访问根结点。
4.层序遍历
第一步:访问根结点(第一层)
第二步:从左到右访问第2层所有结点。
第三步:从左到右访问第3层所有结点、….、第h层所有结点。
五、基本操作
(一)建立二叉链表的3种方法
BTree Creat(char str[])//先序序列建立二叉链表(方法1)
{
BTree T;
static int i = 0;
ElemType c = str[i++];
if (c == '#')//输入#表示此结点下不存在分支
{
T = NULL;
}
else
{
T = (BTree)malloc(sizeof(BTree));
if (T == NULL)
{
exit(0);
}
T->data = c;
T->lchild = Creat(str);//递归创建左子树
T->rchild = Creat(str);//到上一个结点的右边递归创建左右子树
}
return T;
}
void CreateBT(BTree& T) // 先序遍历建立二叉链表 (方法2)
{
ElemType ch;
cin >> ch;
if (ch == '^')
T = NULL;
else
{
T = (BTNode*)malloc(sizeof(BTNode));
T->data = ch;
cout << "请输入" << ch << "的左子树:";
CreateBT(T->lchild);
cout << "请输入" << ch << "的右子树:";
CreateBT(T->rchild);
}
}
void CreatBTNode(BTree& T, char* str)//(方法3)
{
BTNode* st[MAXSIZE];
BTNode* p = NULL;
int top = -1, k, j = 0;
ElemType ch;
ch = str[j];
while (ch != '\0')//str未扫描完的时候循环
{
switch (ch)
{
case '('://为左孩子结点
top++;
st[top] = p;
k = 1;
break;
case ')':
top--;
break;
case ','://为右孩子结点
k = 2;
break;
default:
p = (BTree)malloc(sizeof(BTree));
p->data = ch;
p->lchild = p->rchild = NULL;
if (T == NULL)//p结点第一次创建
{
T = p;
}
else
{
switch (k)
{
case 1:
st[top]->lchild = p;//p结点为栈顶结点的左孩子
break;
case 2:
st[top]->rchild = p;//p结点为栈顶结点的右孩子
break;
}
}
}
ch = str[++j];//继续扫描下一个字符
}
}
方法3解释:例如A(B(D(,G)),C(E,F))
(二)先序(递归)、中序(递归)、后序(递归)、层次遍历
- 层次遍历算法思想:遍历从二叉树的根结点开始,首先将根结点入队列,然后进行下面的操作
1.取出队头元素
2.访问该元素所指结点
3.若该元素所指结点的左、右孩子结点非空,则将该元素所指结点的左孩子和右孩子指针入队
4.若队列非空,重复1-3;当队列为空的时候,二叉树的层次遍历结束。
void PreOrder(BTree T)//先序遍历
{
if (T != NULL)
{
cout << T->data << " ";//访问根结点
PreOrder(T->lchild);//先序遍历左子树
PreOrder(T->rchild);//先序遍历右子树
}
}
void InOrder(BTree T)//中序遍历
{
if (T != NULL)
{
InOrder(T->lchild);
cout << T->data << " ";
InOrder(T->rchild);
}
}
void PostOrder(BTree T)//后序遍历
{
if (T != NULL)
{
PostOrder(T->lchild);
PostOrder(T->rchild);
cout << T->data << " ";
}
}
void LevelOrder(BTree T)//层序遍历
{
BTNode* p;
BTNode* qu[MAXSIZE];//定义循环队列,存放结点指针
int front, rear;//定义头指针和尾指针
front = rear=0;//置队列为空队列
rear++;//根结点指针进队
qu[rear] = T;
while (front != rear)//队列不为空
{
front = (front + 1) % MAXSIZE;//队头指针加1取模
p = qu[front];//队头结点出队
cout << p->data << " ";
if (p->lchild != NULL)
{
rear = (rear + 1) % MAXSIZE;//队尾指针加1取模
qu[rear] = p->lchild;//p结点的左孩子进栈
}
if (p->rchild != NULL)
{
rear = (rear + 1) % MAXSIZE;
qu[rear] = p->rchild;//p结点的右孩子进栈
}
}
}
(三)先序(非递归)、中序(非递归)、后序(非递归)
- 先序遍历的关键:在先序遍历某个结点的整个左子树后,如何找到该结点的右子树的根指针?解决方法:在访问完该结点后,将该结点的指针保存在栈中,以便以后能通过它找到该结点的右子树。
- 中序遍历:采用一个栈保存需要回溯的结点地址,先扫描(并非访问根结点)的所有左下结点并将它们一一进栈,然后出栈一个结点,该结点一定没有左孩子结点或者左子树已遍历过,则访问它;然后转向该结点的右孩子结点(如果有),将其进栈,再遍历该右孩子结点的所有左下结点并一一进栈;如此循环,直到栈空并且遍历指针p空为空。
- 后序遍历:采用一个栈保存需要回溯的结点地址,先扫描根结点的所有左下结点并且一一进栈。将栈顶结点T作为当前结点,如果结点T可以访问,则访问它;否则转向结点T的右子树遍历其右子树。
bool FirstOrder(BTree T)//先序遍历非递归算法
{
BTNode* st[MAXSIZE];//st数组存放栈元素
// BTNode* st[MAXSIZE]因为[]的优先级比*高,所以st先与[]结合,形成数组,再与*结合,
//所以这个数组的类型BTNode*,就是指向BTNode的指针,就是"元素是指向BTNode型数据的指针的数组"。
// 每个元素都是一个指针,一共有MAXSIZE个元素
int top = -1;//top为栈顶指针
BTNode* p = T;
while (p != NULL || top != -1)//当前结点不为空或者栈不为空
{
while (p != NULL)//当前结点不为空
{
cout << p->data << " ";
top++;
st[top] = p;//当前结点入栈
p = p->lchild;//左孩子作为当前结点
}
if (top != -1)//当栈不为空
{
p = st[top];//将栈顶元素赋值给p
top--;//栈顶元素出栈
p = p->rchild;//指向栈顶元素的右孩子
}
}
return true;
}
bool InOrder2(BTree T)//中序遍历非递归算法
{
BTNode* st[MAXSIZE];//st数组存放栈元素
int top = -1;
BTNode* p = T;
while (p != NULL || top != -1)
{
while (p != NULL)
{
top++;
st[top] = p;
p = p->lchild;
}
if (top != -1)
{
p = st[top];//获取栈顶结点
cout << p->data << " ";
top--;//栈顶结点出栈
p = p->rchild;//指向栈顶结点的右孩子
}
}
return true;
}
void PostOrder2(BTree T)//后序遍历非递归算法
{
BTNode* st[MAXSIZE];
BTNode* p ;
int flag = -1;
int top = -1;
do{
while (T != NULL)
{
top++;
st[top] = T;
T = T->lchild;
}
p = NULL;
flag = 1;//表示栈顶的结点的左孩子被处理过
while (top != -1 && flag == 1)//对栈顶结点进行连续判断
{
T = st[top];
if (T->rchild == p)
{
cout << T->data << " ";
top--;
p = T;
}
else
{
T = T->rchild;
flag = 0;//表示当前结点的左孩子没有被处理过
}
}
}while (top != -1);
cout << endl;
}
(三)完整代码
#include
#include
#include
using namespace std;
#define MAXSIZE 100
typedef char ElemType;
typedef struct BTNode
{
ElemType data;//数据域
struct BTNode* lchild, * rchild;//左右孩子指针
}BTNode, * BTree;
BTree Creat(char str[]);//先序序列建立二叉链表(方法1)
void CreateBT(BTree& T); // 先序遍历建立二叉链表(方法2)
void CreatBTNode(BTree& T, char* str);//利用ch扫描,采用括号表示法表示的二叉树字符串(方法3)
void PreOrder(BTree T);//先序遍历
void InOrder(BTree T);//中序遍历
void PostOrder(BTree T);//后序遍历
void LevelOrder(BTree T);//层序遍历
//非递归算法
bool FirstOrder(BTree T);//先序遍历非递归算法
bool InOrder2(BTree T);//中序遍历非递归算法
void PostOrder2(BTree T);//后序遍历非递归算法
//#include"TreeNode.h"
BTree Creat(char str[])//先序序列建立二叉链表(方法1)
{
BTree T;
static int i = 0;
ElemType c = str[i++];
if (c == '#')//输入#表示此结点下不存在分支
{
T = NULL;
}
else
{
T = (BTree)malloc(sizeof(BTree));
if (T == NULL)
{
exit(0);
}
T->data = c;
T->lchild = Creat(str);//递归创建左子树
T->rchild = Creat(str);//到上一个结点的右边递归创建左右子树
}
return T;
}
void CreateBT(BTree& T) // 先序遍历建立二叉链表 (方法2)
{
ElemType ch;
cin >> ch;
if (ch == '^')
T = NULL;
else
{
T = (BTNode*)malloc(sizeof(BTNode));
T->data = ch;
cout << "请输入" << ch << "的左子树:";
CreateBT(T->lchild);
cout << "请输入" << ch << "的右子树:";
CreateBT(T->rchild);
}
}
void CreatBTNode(BTree& T, char* str)//(方法3)
{
BTNode* st[MAXSIZE];
BTNode* p = NULL;
int top = -1, k, j = 0;
ElemType ch;
ch = str[j];
while (ch != '\0')//str未扫描完的时候循环
{
switch (ch)
{
case '('://为左孩子结点
top++;
st[top] = p;
k = 1;
break;
case ')':
top--;
break;
case ','://为右孩子结点
k = 2;
break;
default:
p = (BTree)malloc(sizeof(BTree));
p->data = ch;
p->lchild = p->rchild = NULL;
if (T == NULL)//p结点第一次创建
{
T = p;
}
else
{
switch (k)
{
case 1:
st[top]->lchild = p;//p结点为栈顶结点的左孩子
break;
case 2:
st[top]->rchild = p;//p结点为栈顶结点的右孩子
break;
}
}
}
ch = str[++j];//继续扫描下一个字符
}
}
void PreOrder(BTree T)//先序遍历
{
if (T != NULL)
{
cout << T->data << " ";//访问根结点
PreOrder(T->lchild);//先序遍历左子树
PreOrder(T->rchild);//先序遍历右子树
}
}
void InOrder(BTree T)//中序遍历
{
if (T != NULL)
{
InOrder(T->lchild);
cout << T->data << " ";
InOrder(T->rchild);
}
}
void PostOrder(BTree T)//后序遍历
{
if (T != NULL)
{
PostOrder(T->lchild);
PostOrder(T->rchild);
cout << T->data << " ";
}
}
void LevelOrder(BTree T)//层序遍历
{
BTNode* p;
BTNode* qu[MAXSIZE];//定义循环队列,存放结点指针
int front, rear;//定义头指针和尾指针
front = rear = 0;//置队列为空队列
rear++;
qu[rear] = T;//根结点指针进队
rear++;//队尾指针指向队尾元素的后一个位置
front = (front + 1) % MAXSIZE;//队头指针加1取模
while (front != rear)//队列不为空
{
p = qu[front];//取队头元素
front = (front + 1) % MAXSIZE;//队头元素出队,队头指针后移
cout << p->data << " ";
if (p->lchild != NULL)
{
qu[rear] = p->lchild;//p结点的左孩子进栈
rear = (rear + 1) % MAXSIZE;//队尾指针加1取模
}
if (p->rchild != NULL)
{
qu[rear] = p->rchild;//p结点的右孩子进栈
rear = (rear + 1) % MAXSIZE;
}
}
}
bool FirstOrder(BTree T)//先序遍历非递归算法
{
BTNode* st[MAXSIZE];//st数组存放栈元素
// BTNode* st[MAXSIZE]因为[]的优先级比*高,所以st先与[]结合,形成数组,再与*结合,
//所以这个数组的类型BTNode*,就是指向BTNode的指针,就是"元素是指向BTNode型数据的指针的数组"。
// 每个元素都是一个指针,一共有MAXSIZE个元素
int top = -1;//top为栈顶指针
BTNode* p = T;
while (p != NULL || top != -1)//当前结点不为空或者栈不为空
{
while (p != NULL)//当前结点不为空
{
cout << p->data << " ";
top++;
st[top] = p;//当前结点入栈
p = p->lchild;//左孩子作为当前结点
}
if (top != -1)//当栈不为空
{
p = st[top];//将栈顶元素赋值给p
top--;//栈顶元素出栈
p = p->rchild;//指向栈顶元素的右孩子
}
}
return true;
}
bool InOrder2(BTree T)//中序遍历非递归算法
{
BTNode* st[MAXSIZE];//st数组存放栈元素
int top = -1;
BTNode* p = T;
while (p != NULL || top != -1)
{
while (p != NULL)
{
top++;
st[top] = p;
p = p->lchild;
}
if (top != -1)
{
p = st[top];//获取栈顶结点
cout << p->data << " ";
top--;//栈顶结点出栈
p = p->rchild;//指向栈顶结点的右孩子
}
}
return true;
}
void PostOrder2(BTree T)//后序遍历非递归算法
{
BTNode* st[MAXSIZE];
BTNode* p ;
int flag = -1;
int top = -1;
do{
while (T != NULL)
{
top++;
st[top] = T;
T = T->lchild;
}
p = NULL;
flag = 1;//表示栈顶的结点的左孩子被处理过
while (top != -1 && flag == 1)//对栈顶结点进行连续判断
{
T = st[top];
if (T->rchild == p)
{
cout << T->data << " ";
top--;
p = T;
}
else
{
T = T->rchild;
flag = 0;//表示当前结点的左孩子没有被处理过
}
}
}while (top != -1);
cout << endl;
}
//#include"TreeNode.h"
int main()
{
cout << "二叉树建立的第一种方法" << endl;
BTree T;//T为指向根结点的指针
T=(BTree)malloc(sizeof(BTree));
char str[] = { 'a','b','d','#','#','e','#','#','c','f','#','#','g','#','#' };
T = Creat(str);
cout << "先序遍历结果:";
PreOrder(T);//先序遍历
cout << endl;
cout << "中序遍历结果:";
InOrder(T);//中序遍历
cout << endl;
cout << "后序遍历结果:";
PostOrder(T);//后序遍历
cout << endl ;
cout << "层次遍历的结果:";
LevelOrder(T);
cout << endl<
Original: https://blog.csdn.net/m0_63783532/article/details/127804395
Author: 我愿,我想
Title: 二叉树的学习
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/656200/
转载文章受原作者版权保护。转载请注明原作者出处!