目录

二叉树

二叉树的链式存储结构

二叉树先序遍历(递归)

二叉树先序遍历(非递归)

二叉树中序遍历(递归)

二叉树中序遍历(非递归)

二叉树后序遍历(递归)

二叉树后序遍历(非递归)

二叉树判定问题

二叉树判定是否是排序二叉树

二叉树判定是否是完全二叉树

二叉树判定是否是平衡二叉树

二叉树统计问题【递归+整体思维】

计算二叉树结点数

计算二叉树叶子结点数

计算二叉树单分支结点数

计算二叉树双分支结点数

计算二叉树各结点的子孙个数

计算二叉树高度(3种方法)

递归法

非递归法:后序遍历 || 层次遍历

二叉树构建问题

前序遍历构建二叉树

一棵具有n个节点的完全二叉树以顺序方式存储在数组中,设计一个算法构造该二叉树的二叉链式存储结构

前序遍历&中序遍历确定二叉树

后序遍历&中序遍历确定二叉树

层次遍历&中序遍历确定二叉树

二叉树-层次遍历专题

层次遍历

设计二叉树自下而上,自右至左的层次遍历

二叉链表存储结构,设计非递归算法求二叉树高度

二叉链表存储结构,设计递归算法求二叉树高度

二叉链表存储结构,求非空二叉树的宽度(即:具有结点数最多的那一层的结点个数)

二叉链表存储结构,判断给定二叉树是否是完全二叉树:

后序遍历专题

二叉链表存储结构,对树中每个元素值为x的结点,删除以它为根的子树,并释放相应空间;

二叉树-线索二叉树

线索二叉树存储结构的定义

中序线索化二叉树

后序线索化二叉树

先序线索化二叉树

中序线索二叉树找后继

中序线索二叉树遍历(中序遍历)

中序线索二叉树找中序前驱

二叉树的应用:

二叉排序树结点定义

在二叉排序树中查找值为key的结点

二叉排序树的插入

二叉排序树的构建

设计算法,判定给定排序二叉树中最小和最大关键字

设计算法,从大到小输出二叉排序树中所有值不小于K的关键字

哈夫曼树系列

哈夫曼树结构体定义

哈夫曼树的创建

哈夫曼编码:从叶子到根,逆向求每个字符的哈夫曼编码

在还未参与建树的结点中,查找权值最小的一个,返回其结点编号


二叉树

二叉树的链式存储结构

typedef struct BiNode
{  ElemType data;struct BiNode *lchild,*rchild;
}BiNode,*BiTree;

二叉树先序遍历(递归)

void PreOrder(BiTree bt)
{ if(bt!=NULL){visit(bt);               //=printf("%d",bt->data);PreOrder(bt->lchild);PreOrder(bt->rchild);}
}

二叉树先序遍历(非递归)

void PreOrder(BiTree bt)
{InitStack(s);BiNode *p=bt;    //1.初始化栈和指针pwhile(p || !IsEmpty(s))       //2.在p所指结点不空或者栈s不空的情况下,一直循环遍历{ if(p){                   //2.1p所指结点不空,访问,入栈,左孩子循环visit(p);push(s,p);p=p->lchild;}else{                    //2.2出栈元素赋给p,右孩子循环pop(s,p);p=p->rchild;}}
}

二叉树中序遍历(递归)

void InOrder(BiTree bt)
{ if(bt!=NULL){InOrder(bt->lchild);visit(bt);InOrder(bt->rchild);}
}
PEP:04

二叉树中序遍历(非递归)

算法思想:1.定义并初始化栈S和二叉结点P并指向根结点;2.在根结点不空且栈不空的情况下,循环:根结点不空入栈并循环访问其左孩子并入栈;若根结点空,弹出栈顶元素,访问它,并访问其右孩子;继续返回2开始;
void InOrder(BiNode *bt)
{ InitStack(s);BiNode *p=bt;while(p || !IsEmpty(S)){if(p){push(s,p);p=p->lchild;}else{pop(s,p);printf("%c",p->data);p=p->rchild;}
}

二叉树后序遍历(递归)

void PostOrder(BiTree bt)
{if(bt!=NULL){PostOrder(bt->lchild);PostOrder(bt->rchild);visit(bt);}
}

二叉树后序遍历(非递归)

//Algorithm-Thought:1.从根结点开始,沿着左孩子依次入栈,直至为空;2.“读取”栈顶元素,若其右孩子不空并且右孩子未被访问过,转1;3.否则,栈顶元素出栈并返回.
难点:分清2中返回根结点时,是从左子树还是右子树,因此:定义辅助指针r-指向最近刚访问过的结点.
void PostOrder(BiTree T)
{//1.初始化栈和指针;   InitStack(s);BiNOde *p=T,*r=NULL;//注:辅助指针r,这是区别非递归前序和中序的地方之一;//2.在指针p和栈s不空的情况下,遍历;while(p || !IsEmpty(s)){  //注:!IsEmpty(s),not IsEmpty(s);if(p){          //3.在指针不空的情况下,持续访问左孩子并入栈;push(s,p);p=p->lchild;}else{           //4.若指针为空,“读取”栈顶元素,若该元素右孩子存在且未被访问过,则右孩子入栈并返回第3步;GetTop(p);if(p->rchild && p->rchild!=r){p=p->rchild;push(s,p);p=p->lchild;}else{         //5.若指针为空,“读取”栈顶元素,若钙元素右孩子不存在或已访问过,则出栈并访问,并重新标记指针r和p的指向;pop(s,p);visit(p);r=p;p=NULL;}}}
}

二叉树判定问题

二叉树判定是否是排序二叉树

算法思想:中序遍历二叉树,若前驱始终小于当前节点,则是排序二叉树BST.1.若树根为空,则是:排序二叉树;2.递归判断左子树是否是二叉排序树并将结果返回;3.判断左子树结果以及当前结点与前驱结点是否满足排序二叉树条件;4.
int predt = -9999;  //定义全局变量,始终作为前驱
int JuBST(BiTree bt)
{ int btl,btr;//1.空树是排序二叉树if (bt==NULL) return 1;  else{//2.判断左子树是否是二叉排序树并将结果返回btl=JuBST(bt->lchild);  //3.判断当前节点是否是二叉树if (btl==0 || bt->data<=predt)  return 0;predt=bt->data;    //本轮的当前结点就是右子树的前驱结点;//4.判断右子树是否是二叉排序树并返回结果btr=JuBST(bt->rchild);return btr;    //程序如果可以走到这一步,二叉树是否是排序二叉树的重任,就只看:btr的值了;}
}
04:
void InOrder(BiTree bt)
{  BiNode* boot=bt;if(boot==NULL) return;else{visit(boot);InOrder(boot->lchild);InOrder(boot->rchild);}
}

二叉树判定是否是完全二叉树

//算法思想:层次遍历二叉树,将包括空结点在内的所有结点全部入队;若遇到空节点,判断其下一结点,若不为空,则不是完全二叉树.
1.空二叉树便是完全二叉树;
2.入队根结点;
3.在队列不空的情况下,出队一个结点,判断其是否为空;若不为空,则分别入队其左右孩子;
4.在队列不空的情况下,出队一个结点,判断其是否为空;若其为空,在队列不空的情况下,循环将结点出队,并判断结点是否存在不为空的情况,若存在,则不是完全二叉树;
int JuComplete(BiTree bt)
{BiTree p;InitQueue(Q);//1.空二叉树便是完全二叉树if (bt==NULL) return 1;//2.根节点入队EnQueue(Q,bt)while(!IsEmpty(Q)) //在队列不空的情况下,循环;{ DeQueue(Q,p)  //3.判断出队结点是否为空,若不空分别入队左右孩子结点;此处与一般层次遍历有区别;if(p){ EnQueue(Q,p->lchild);EnQueue(Q,p->rchild);}else{  //4.若出队结点为空,在队列还不空的情况下,循环出队后续所有结点,若存在不空,则非完全二叉树While(!IsEmpty(Q){ DeQueue(Q,p);if(p) return 0;}}}
}

二叉树判定是否是平衡二叉树

//算法思想:定义平衡因子balance和左右子树高度hl,hr;若balance=1且(hl-hr)<=1,则二叉树为平衡二叉树,否则:非平衡二叉树;
1.若根节点bt为空,则二叉平衡树,balance=1,h=0;
2.若根节点仅单独存在,则二叉平衡树,balance=1,h=1;紧接着,递归判断左右子树;
3.根据左右子树返回的左右子树高度,若高度差<=1且同时满足左右子树也是平衡二叉树的情况下,则平衡二叉树,否则:非平衡二叉树void JuAVL(BiTree bt,&h,&balance) //利用&返回balance和h
{  int bl,br,hr,hl; //定义左右子树的平衡因子及高度if(bt==NULL)     {  balance=1;    //1.根空h=0;}else if((bt->lchild==NULL)&&(bt->rchild==NULL))    //2.仅根节点存在{  balance=1;h=1;}else{JuAVL(bt->lchild,hl,bl);    //3.递归遍历左右子树,判断JuAVL(bt->rchild,hr,br);h=(hl>hr?hl:hr)+1;                //因为递归存在,所以此句必须存在:整体树高=最高左右子树高度+1if(abs(hl-hr)<=1) balance=bl&&br; //核心:在整体树高度h<=1的情况下,同时左右子树balance=1,则:平衡二叉树else balance=0;}
}

二叉树统计问题【递归+整体思维】

计算二叉树结点数

//算法思想:递归*(二叉树结点数=根1+左子树结点数+右子树结点数)int CountAll(BiTree bt)   //函数参数定义中:BiTree bt = BiNode *bt
{  int btl,btr;if(bt==NULL) return 0;   //递归出口else{  btl=CountALL(bt->lchild);   //递归左子树btr=CountALL(bt->rchild);   //递归右子树return (btl+bt2+1);}
}

计算二叉树叶子结点数

//算法思想:首先,根节点为空 ;其次,仅根节点存在;最后:递归*(二叉树结点数=左子树结点数+右子树结点数)int CountLeaf(BiTree bt)
{  int btl,btr;    //基本变量定义if(bt==NULL) return 0;   //根结点为空else if((bt->lchild==NULL)&&(bt->rchild==NULL)) return 1;   //仅仅只有根结点存在else{  btl=CountLeaf(bt->lchild);   //左子树,右子树btr=CountLeaf(bt->rchild);return (btl+btr);}}

计算二叉树的非叶子结点数

//算法思想:1.空树;2.左右子树不存在;3.递归调用左右子树;int CountNotLeaf(BiTree bt)
{int btl,btr;if(bt==NULL) return 0;else if((bt->lchild==NULL)&&(bt->rchild==NULL)) return 0;else{btl=CountNotLeaf(bt->lchild);btr=CountNotLeaf(bt->rhcild);}return (btl+btr+1);
}

计算二叉树单分支结点数

//算法思想:首先,根节点为空;其次,若根节点左右子树仅存在任意一个,则:单分支结点数=根节点1+左子树单分支结点数+右子树单分支结点数;否则,单分支结点数=左子树单分支结点数+右子树单分支结点数.int CountSimple(BiTree bt)
{ int btl,btr;if(bt==NULL) return 0;//根结点左右子树仅任意一个存在的情况:单分支结点数=本结点1+左子树结点数+右子树结点数else if((bt->lchild==NULL && bt->rchild!=NULL)||(bt->lchild!=NULL && bt->rchild==NULL)){  btl=CountSimple(bt-lchild);btr=CountSimple(bt-rchild);return 1+btl+btr;}//根结点左右子树非仅任意一个存在的情况:单分支结点数=左子树结点数+右子树结点数else { btl=CountSimple(bt-lchild);btr=CountSimple(bt-rchild);return btl+btr;}
}

计算二叉树双分支结点数

//算法思想:首先,根节点为空;其次,若根节点左右子树都存在,则:双分支结点数=根节点1+左子树双分支结点数+右子树双分支结点数;否则,双分支结点数=左子树双分支结点数+右子树双分支结点数;int CountDouble(BiTree bt)
{  int btl,btr;if(bt==NULL) return 0;  //1.根结点为空//2.根结点左右子树都存在的情况:双分支结点数=根节点1+左子树双分支结点数+右子树双分支结点数else if(bt->lchild!=NULL && bt-rchild!=NULL)  //双分支{  btl=CountDouble(bt->lchild);btr=CountDouble(bt->rchild);return 1+btl+btr;}//3.根结点左右子树非都存在的情况:双分支结点数=左子树双分支结点数+右子树双分支结点数else  //单分支{  btl=CountDouble(bt->lchild);btr=CountDouble(bt->rchild);return btl+btr;}
}

计算二叉树各结点的子孙个数

//算法思想:定义两个函数:函数1:计算所指结点的所有子孙数;函数2:在使用函数1的基础上,递归遍历所有结点并输出各结点所有子孙数;函数1:首先,根节点为空;其次,递归遍历左子树并逐个+1;之后,递归遍历右子树并逐个+1;最后,左右子树子孙数求和;int CountSub(BiTree bt)
{ int btl=0,btr=0;if(bt==NULL) return 0;  //空树if(bt->lchild) btl=CountSub(bt->lchild)+1;
//递归左子树结点数:一直遍历到左子树最左端,然后:从btl=0开始,逐个返回并+1,同时判断下边条件是否满足情况【递归本质的描述】if(bt->rchild) btr=CountSub(bt->rchild)+1;return (btl+btr);
}
//函数2:在根不空的情况下:首先,输出根的子孙数;其次,递归遍历左右子树同时调用CountSub函数,统计计算每个节点的子孙数;void CountEvery(BiTree bt)
{ if(bt==NULL) return;else{printf("结点值%d的结点数:%d\n",bt->data,CountSub(bt));  //首先输出根结点的子孙数,之后:根据遍历的情况,逐个输出结点子孙数CountEvery(bt-lchild);  //递归遍历左右子树CountEvery(bt-rchild);}
}
算法思想:定义函数1:计算当下结点的子孙数;定义函数2:在函数1的基础上,递归遍历所有结点并逐个计算输出结点子孙个数.函数1:
算法思想:首先,根节点空;其次,递归遍历左右子树并加1;最后,返回左右子树之和;
int CountNow(BiTree bt)
{  int btl=0,btr=0;if(bt==NULL) return 0;if (btl->lchild) btl=CountNow(bt->lchild)+1;if (btr->rchild) btr=CountNow(bt->rchild)+1;return (btl+btr);
}函数2:
算法思想:从根结点开始,首先输出根节点子孙个数;然后,逐个遍历所有的结点并同时在函数1的基础上计算并输出每个结点的子孙数.
int CountEvery(BiTree bt)
{  if(bt==NULL) return 0;else{ printf("结点值为%d的子孙结点数是:%d\n",bt->data,CountNow(bt));CountEvery(bt->lchild);CountEvery(bt->rchild);}
}

计算二叉树高度(3种方法)

递归法

//算法思想:递归法:首先,根结点为空;其次,分别递归左右子树并返回左右子树高度;最后,比较左右子树高度,二叉树高度=较高者+1.int CountHigh(BiTree bt)
{  if(bt==NULL) return 0;  //根空highl=CountHigh(bt->lchild);  //遍历左子树,计算高度highr=CountHigh(bt->rchild);h=(highl>highr?highl:highr)+1;  //二叉树高度=左右子树高度较大者+1return h;
}

非递归法:后序遍历 || 层次遍历

#include<iostream>
#include<stack>
#include<queue>
using namespace std;
typedef struct BiTNode{ char data; struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree; void CreateTree(BiTree &T)
{ char ch; cin>>ch; if(ch=='#') T=NULL; else { T=(BiTree)malloc(sizeof(BiTNode)); if(!T)  cout<<"生成结点错误!"<<endl; T->data=ch; CreateTree(T->lchild); CreateTree(T->rchild); }
} //法1:后序遍历,结点最大栈长即为树的高度
int BT_high(BiTree T)
{ BiTree p=T,r=NULL; int max=0;                                     //树高 stack<BiTree> s; while(p||!s.empty()) { if(p!=NULL) { s.push(p); p=p->lchild; } else { p=s.top(); if(p->rchild!=NULL && p->rchild!=r) p=p->rchild; else { if(s.size()>max) max=s.size();//最大层次即为高度 r=p; s.pop(); p=NULL; } } } return max;
} //法2:层次遍历,层次即为高度
int BT_level_depth(BiTree T)
{ if(!T)  return 0; BiTree p=T,Q[100]; int front=-1,rear=-1,last=0,level=0; Q[++rear]=p; while(front<rear)   //队列不空的情况下,出队一个元素,左右孩子不为空,则入队;{ p=Q[++front]; if(p->lchild) Q[++rear]=p->lchild; if(p->rchild) Q[++rear]=p->rchild; if(front==last)      { last=rear; level++;               //层次+1 } } return level;
} //法3:递归求树高1
int max1=0;//树高
int BT_depth1(BiTree T,int depth)
{ if(T) { if(T->lchild) BT_depth1(T->lchild,depth+1); if(T->rchild) BT_depth1(T->rchild,depth+1); } if(depth>max1)    max1=depth; return depth;
} //法3:递归求树高2
int Height (BiTree T)
{   if(T==NULL) return 0; else  { int m = Height ( T->lchild ); int n = Height(T->rchild); return (m > n) ? (m+1) : (n+1);  }
} int main()
{ BiTree T=NULL; CreateTree(T); cout<<"后序遍历求树高:"<<endl; cout<<BT_high(T)<<endl; cout<<"层次遍历求树高:"<<endl; cout<<BT_level_depth(T)<<endl; cout<<"递归求树高1:"<<endl; BT_depth1(T,1); cout<<max1<<endl; cout<<"递归求树高2:"<<endl; cout<<Height(T)<<endl; return 0;
}

二叉树构建问题

前序遍历构建二叉树

//算法思想:为了建立目标二叉树,所以:在目标二叉树的基础上建立扩展二叉树,
也就是:给目标二叉树的每个结点的空左右孩子以“#”代替,并通过前序遍历获得扩展二叉树的前序遍历序列;
该扩展二叉树的遍历序列便是前序扩展二叉树的输入序列;每次逐个输入该前序扩展二叉树的序列的一个字符,递归遍历过程中:若不为“#”,则构建新结点并赋值,否则,返回空值.BiNode * PreStr(BiNode *bt)
{  input(ch);  //保证每次读取一个字符if(ch=='#') bt=NULL;  //如果读取字符为'#',则赋空else{bt=new BiNode;bt->data=ch;        //创建一个新的结点并赋值bt->lchild=PreStr(bt->lchild);    //区别于一般的递归,此处一定是:bt-lchild=...;bt->rchild=PreStr(bt->rchild);}   //递归创建右子树结点return bt;
}
//递归的本质:进入递归入口时,系统自动创建堆栈,记录:当前程序语句所在行+接受递归返回状态的变量C++版本
void PreStr(BiNode*&bt) //使用引用型&,传入下一层的参数根据下一层引用型参数的变化,实时变化;
{  input(ch);if(ch=='#') bt=Null;  //此处注意:bt==NULL,以便后续可以创建空结点else{bt=new BiNode;bt->data=ch;PreStr(bt-lchild);PreStr(bt-rchild);}return ;
}

一棵具有n个节点的完全二叉树以顺序方式存储在数组中,设计一个算法构造该二叉树的二叉链式存储结构

算法思想:完全二叉树的顺序存储便是完全二叉树层次遍历的结果,
数组从0开始,针对一个父节点a[i]所在i位,其左右孩子分别处于:2*i+1和2*i+2位;
逐个递归的从完全二叉树中读取左右孩子的位置,便可以创建完全二叉树的二叉链式存储结构BiNode * CreateTree(int i,BiNode a[])
{  BiNode *bt;if(i>strlen(a)-1 || a[i]=='0') bt=NULL;else{bt=new BiNode; //bt=(BiNode*)malloc(sizeof(BiNode));bt->data=a[i];bt->lchild=CreateTree(2*i+1,a);  //根结点为i,其左孩子序列2*i+1[层次遍历序列的规律]bt->rchild=CreateTree(2*i+2,a);}return bt;
}

前序遍历&中序遍历确定二叉树

算法思想:
(1)根据前序序列确定根结点;
(2)在中序序列中找到根节点位置,确定根结点左右子树的中序序列;
(3)在前序序列中找到左右子树的前序序列;
(4)由左子树的前序序列和中序序列确定左子树;
(5)由右子树的前序序列和中序序列确定右子树;BiNode * PreInCtree(char pre[],char in[],int l1,int r1,int l2,int r2)
{  if(l1>r1) return NULL;  //1.作为递归的出口BiNode *bt=(BiNode*)malloc(sizeof(BiNode));  //2.创建一个左右孩子为空的根节点并赋根节点值bt->lchild=NULL;bt->rchild=NULL;bt->data=pre[l1];for(int i=l2;i<=r2;i++)  //3.在in中序序列中,找到根节点所在的位置{ if(in[i]==pre[l1]) break;}bt->lchild=PreInCtree(pre,in,l1+1,l1+i-l2,l2,i-1);//4.根据根节点的位置,确定左子树前序序列中序序列范围,递归处理[注:范围的坐标一定要熟练掌握]bt->rchild=PreInCtree(pre,in,l1+i-l2+1,r1,i+1,r2);//根据根节点的位置,确定右子树前序序列中序序列范围,递归处理return bt;
}

后序遍历&中序遍历确定二叉树

算法思想:(1)根据后序遍历确定根节点;(2)在中序序列中找到根节点位置,确定根节点左右子树的中序序列;(3)在后序序列中找到左右子树的后序序列;(4)由左子树的后序序列和中序序列确定左子树;(5)由右子树的后序序列和中序序列确定右子树;BiNode *PostInCtree(char post[],char in[],int l1,int r1,int l2,int r2)
{  //1.作为一个出口if(l1>r1) return NULL;//2.构建一个左右孩子为空的根节点并赋根节点值BiNode *bt=(BiNode*)malloc(sizeof(BiNode));bt-lchild=NULL;bt->rchild=NULL;bt->data=post[r1];//3.找到根节点在中序序列中的位置for(int i=l2;i<r2;++i)if(post[r1]==in[i]) break;    //4.根据根节点的位置,计算出左孩子后序序列&中序序列的范围,递归处理bt->lchild=PostInCtree(post,in,l1,l1+i-l2-1,l2,i-1);//根据根节点的位置,计算出右孩子后序序列&中序序列的范围,递归处理bt->rchild=PostInCtree(post,in,l1+i-l2,r1-1,i+1,r2);return bt;
}

层次遍历&中序遍历确定二叉树

算法思想:主函数步骤中已经写明int search(char a[],int key,int l,int r) //在范围为l-r的数组a中,寻找key所在的位置并返回
{ for(int i=l;l<=r;++l)if(a[i]==key) return i;return -1;
}
//逐个遍历level数组中n个元素,并利用search()函数判断:是否在in数组l-r范围内存在;若存在,则将其置于sublevel数组中且随着k++,有序且连续地排列;
void GetSubLevel(char sublevel[],char level[],char in[],int n,int l,int r)
{  int k=0;for(int i=0;i<n;++i){if(search(in,level[i],l,r)!=-1)sublevel[++k]=level[i];}
}
BiNode * LevInCtree(char level[],char in[],int n,int l,int r)
{  //1.递归出口if(l>r) return NULL;//2.创建一个左右子树为空的根节点并赋根节点值【根节点值=level[0]】BiNode *bt=(BiNode*)malloc(sizeof(BiNode));bt->lchild=NULL;bt->rchild=NULL;bt->data=level[0];/*3.此处与前序中序构建二叉树和后序中序构建二叉树不同,因为在层次遍历中寻找到根节点并在中序遍历中区分左右子树集合,但再次返回层次遍历发现:左右子树集合不再连续;
所以,不能通过之前的计算范围的方式,进行处理了;
所以,我们的解决措施是:将不连续的左右子树集合通过GetSubLevel()函数提取出来,
分别连续有序地存储在LN长度的llevel数组和RN长度的rlevel数组,最后可以同之前一样,
利用递归继续处理;*///为此:我们需要先构建LN和RN长度的level和rlevel数组int i=search(in,level[0],l,r);int LN=i-l;char llevel[LN];int RN=r-i;char rlevel[RN];//接着:在level数组中逐个遍历n个元素,同时在in数组的有效范围内,进行比较;若存在,则:有序连续地存放在llevel和rlevel数组中GetSubLevel(llevel,level,in,n,l,i-1);GetSubLevel(rlevel,level,in,n,i+1,r);//4.针对处理后产生的有序连续的llevel和rlevel数组,同前序中序构建二叉树和后序中序构建二叉树一样,递归处理bt-lchild=LevInCtree(llevel,in,LN,l,i-1);bt-rchild=LevInCtree(rlevel,in,RN,i+1,r);return bt;
}

二叉树-层次遍历专题

层次遍历

算法思想:1.创建一个辅助队列,根结点入队;2.在队列不空的情况下,循环:出队根结点,访问,若根结点左右孩子不空,则分别入队;//二叉树结点的定义
typedef struct BiNode
{ElemType data;struct BiNode *lchild,*rchild;
}BiNode,*BiTree;//层次遍历
void LevelOrder(BiNode *bt)
{InitQueue(s);BiNode *p;Enqueue(s,bt);      //注意:push(s,bt)是栈的用法,此处是队列的入队的写法,不许混淆;while(!IsEmpty(s)){p=DeQueue(s);if(p->lchild) EnQueue(s,p->lchild);if(p->rchild) EnQueue(s,p->rchild);}
}

设计二叉树自下而上,自右至左的层次遍历

[凡是逆序,正反颠倒,3秒内必须想到:栈]

//算法思想:在一般层次遍历的基础上,将出队的元素压入栈中;最后,逐个弹出;
void ReLevel(BiNode *bt)
{if(bt==NULL) return;InitStack(s);InitQueue(q);BiNode *p;EnQueue(q,bt);while(!IsEmpty(q)){DeQueue(q,p);        //错误写法:p=DeQueue(q);push(s,p);if(p->lchild) EnQueue(q,p->lchild);if(p->rchild) EnQueue(q,p->rchild);}while(!IsEmpty(s))pop(s,p);visit(p);
}    

二叉链表存储结构,设计非递归算法求二叉树高度

 //算法思想:在一般层次遍历的基础上,定义level表示当前结点所在层,定义last表示当前结点所在层最右端结点;层次遍历,每次出队元素,就与last比较,若相等,则level++;last移动到下一层的最右端结点;void LevelHigh(BiNode *bt)
{BiNode* Q[Maxsize];int front=-1;rear=-1;BiNode* p;int last=0,level=0;Q[++rear]=bt;while(front<rear){p=Q[++front];if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){   //注:front在父结点一层遍历,rear在左右孩子结点一层遍历;当front==last时,rear也刚好抵达左右孩子所在层的最后一个结点,所以才有:last=rear;level++;last=rear;}}return level;}

二叉链表存储结构,设计递归算法求二叉树高度

//算法思想:(1)递归法:首先,根结点为空;其次,分别递归左右子树并返回左右子树高度;最后,比较左右子树高度,二叉树高度=较高者+1.int RecurOrder(BiNode *bt)
{if(bt==NULL) return 0;   //空树,高度为0;BiNode *btl,*btr;btl=RecurOrder(bt->lchild);   //递归左右子树并返回左右子树高度;btr=RecurOrder(bt->rchild);h=(btl>btr?btl:btr)+1;   //二叉树高度=左右子树高度较高者+1return h;
}

二叉链表存储结构,求非空二叉树的宽度(即:具有结点数最多的那一层的结点个数)

//算法思想:在一般层次遍历的基础上,定义MaxW记录最大宽度层,定义NowW记录当前层最大宽度;层次遍历进行至:front==last时,判断NowW和MaxW的大小并根据情况,决定是否交换;void MaxW(BiNode *bt)
{BiNode *p;BiNode *Q[Maxsize];int front=-1,rear=-1,NowW=0,MaxW=0;Q[++rear]=bt;while(front<rear){p=Q[++front];NowW++;     //计算当前层的宽度if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){    //front在父结点一层活动,直至遇见最后的结点:last;last=rear;     //rear在左右孩子结点一层活动;last重新定位rear;if(NowW>MaxW){MaxW=NowW;NowW=0;}}}return MaxW;
}

二叉链表存储结构,判断给定二叉树是否是完全二叉树:

算法思想:在一般层次遍历的基础上,将二叉树包括空结点在内的所有结点,若遇见空结点,则判断其后是否有非空结点,若有:非完全二叉树;bool IsComplete(BiNode *bt)
{if(bt==NULL) return;   //根空,则:完全二叉树BiNode *p;InitEmpty(Q);EnQueue(Q,bt):while(!IsEmpty(Q)){p=DeQueue(Q);if(p){    //区别:一般层次遍历,此处左右孩子存在的,入队;此处,父结点存在,无论左右孩子是否为空,入队;EnQueue(Q,p->lchild);EnQueue(Q,p->rchild);}else{while(!IsEmpty(Q)){   //结点为空,判断其后结点若不为空,则非完全二叉树;DeQueue(Q,p);if(p) return false;}}}
}

后序遍历专题

二叉链表存储结构,对树中每个元素值为x的结点,删除以它为根的子树,并释放相应空间;

算法思想:因为删除元素值为x的结点,所以:要找到元素值为x的结点,所以:层次遍历;因为要删除元素值为x的结点为根点的子树,所以:要先删除它的左右孩子,所以:后序遍历。//结点删除函数:后序递归删除
void Del(BiTree bt)
{if(bt==NULL) return;         //判空,出口Del(bt->lchild);             //递归,删除左右孩子;Del(bt->rchild);                free(bt);                    //一定要释放结点,不然瞎递归;
}//结点查找函数:层次递归查找
void SeaLev(BiNode *bt,int x)
{if(bt==NULL) return;         //判空,出口if(bt->data==x){             //找到x,删除结点,出口;Del(bt); return;}InitQueue(Q);BiNode *p;      //以下内容,纯粹在层次遍历的基础上,递归调用Del函数删除结点Enqueue(Q,bt);       while(!IsEmpty(Q)){Dequeue(Q,p);if(p->lchild){             //在结点p左右孩子不空的情况下,判断其值==x;是,则:删除,置空;不是,则:入队;if(p->lchild->data==x){Del(p->lchild);p->lchild=Null;}else{Enqueue(Q,p->lchild); }if(p->rchild){if(p->rchild->data==x){Del(p->rchild);p->rchild=Null;}else{Enqueue(Q,p->rchild);}}
}

二叉树-线索二叉树

  • 线索二叉树存储结构的定义

  • 中序线索化二叉树

  • 后序线索化二叉树

  • 先序线索化二叉树

  • 中序线索二叉树找后继

  • 中序线索二叉树遍历(中序遍历)

  • 中序线索二叉树找中序前驱


二叉树的应用:

  1. 二叉排序树结点定义

  2. 在二叉排序树中查找值为key的结点

  3. 二叉排序树的插入

  4. 二叉排序树的构建

  5. 设计算法,判定给定排序二叉树中最小和最大关键字

  6. 设计算法,从大到小输出二叉排序树中所有值不小于K的关键字


哈夫曼树系列-哈夫曼树--->哈夫曼数组--->哈夫曼编码(顺序)

  • 哈夫曼树结构体定义:

设置一个数组huffTree[2n-1],保存哈夫曼树中各个结点的信息;
typedef struct HuffTNode
{int weight;int parent,lchild,rchild;
}HuffTNode,*HuffTree;weight:权值域(一般指对象出现的频率);
parent:指针域,结点的双亲结点在数组中的下标;
lchild:指针域,结点的左孩子结点在数组中的下标;二叉树的性质3:在一棵二叉树中,如果二叉树的叶子结点数为n0,度为2的结点数为n2,则有:n0=n2+1;
所以:n2=n0-1
因为哈夫曼树只有度为0和2的结点,所以哈夫曼结点数n=n0+n2=n0+n0-1=2*n0-1;路径: 树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
路径长度:路径上的分枝数目称作路径长度。
树的路径长度:从树根到每一个结点的路径长度之和。结点的带权路径长度:在一棵树中,如果其结点上附带有一个权值,通常把该结点的路径长度与该结点上的权值之积称为该结点的带权路径长度(weighted path length)树的带权路径长度:如果树中每个叶子上都带有一个权值,则把树中所有叶子的带权路径长度之和称为树的带权路径长度。
  • 哈夫曼树的创建

/*哈夫曼树的构造算法思想:
1.由给定的N个权值,分别构造N个只有根结点的二叉树,从而得到一个集合F;
2.从F中选取2个权值最小的结点,分别作为新二叉树的左右孩子,且新二叉树的根结点权值=左右孩子权值之和;
3.删除F中选取的2个权值最小的结点,纳入新建的二叉树,并重复1和2步骤,直至F仅存1个二叉树,即:哈夫曼树;
*/
void HuffTree(HuffTree T[],int w[],int n)
{for(int i=0;i<2*n-1;i++){   //1.初始化-前(2*n-1)个结点的parent,lchild,rchild为-1;T[i].parent=-1;T[i].lchild=-1;T[i].rchild=-1;}for(int i=0;i<n;i++){     //1.初始化-前n个结点的权值,初始化;T[i].weight=w[i];}for(int k=n;k<2*n-1;k++){   //2.从第n个结点,直至2*n-1个结点,构建哈夫曼树select(HuffTree,k,i1,i2);      //注:找到parent为-1的最小和次小结点;T[k].weight=T[i1].weight+T[i2].weight;T[k].lchild=i1;T[k].rchild=i2;T[i1].parent=k;T[i2].parent=k;}
}//注意:次处求最小和次小值的算法,不能确保正确;在之后复习:排序后,利用排序可以找出最小和次小值;
void select(HuffTree T[],int k,int &i1,int &i2)   //找parent为-1的最小和最次结点,并返回;
{int min1=1000,min2=1000;    //1.初始化变量,其实这里不需要也可以;因为哈夫曼树只需要最小和最次结点的数组序列;for(int i=0;i<k;i++){    //2.从0到k-哈夫曼初试化后,拥有权值的结点的最后一位;if(T[i].parent==-1){      //3.找parent=-1的权值;if(T[i].weight<min1){         //4.寻找最小值和次小值的数组下标;min2=min1;i2=i1;min1=T[i].weight;i1=i;}else if(T[i].weight<min2){min2=T[i].weight;i2=i;}}}
}
  • 哈夫曼编码:从叶子到根,逆向求每个字符的哈夫曼编码,并求出其WPL值

//算法思想:从叶子结点开始,向根结点遍历;逐个判断每一个路径是左孩子路径还是右孩子路径;左孩子路径标0,右孩子路径标1,逆序存储在temp数组中,当一轮结束后,便得到一个结点的哈夫曼编码;
void HuffCoding(HTNode HuffTree[],char *HuffCode[],int n)
{char *temp=(char*)malloc(sizeof(char*)*n);  //注意:*:指向当前节点的哈夫曼编码串;1.创建工作数组,存储临时产生的编码串temp[n-1]='\0';WPL=0;for(int i=0;i<n;i++){  //2.遍历哈夫曼数组,生成哈夫曼编码;start=n-1;//工作数组指针,逆向存放哈夫曼编码(从叶子接地那到根,逆向遍历)pos=i;//pos正在处理的节点的位置parent=HuffTree[i].parent; //正在处理的结点的父结点的位置level=0;  //记录路径长度while(parent!=-1){  //3.当parent!=-1时,持续判断当前结点是左孩子(0),还是右孩子(1)if(HuffTree[parent].lchild==pos){temp[--start]='0';}else{temp[--start]='1';}level++;  //累加当前结点的路径长度pos=parent;  //pos移动到父结点parent的位置;parent=HuffTree[parent].parent;//parent移动到其父结点的位置;}WPL+=level*HuffTree[i].weight;  //根据WPL计算公式可得;HuffCode[i]=(char)malloc(sizeof(char)*(n-start));//4.创建哈夫曼编码实际需要的编码存储空间strcpy(HuffCode[i],&temp[start,n-1);//临时存储的哈夫曼编码存储到huffCode中;}free(temp);
}
  • 在还未参与建树的结点中,查找权值最小的一个,返回其结点编号

HTNode H[maxsize];void find_min(int n)
{int min=9999,flag=-1;     //flag:记录权值最小结点的结点标号;for(int i=0;i<=n;i++){if(H[i].parent==-1){ //h[i].parent==-1,标志着:结点还未参与建树;if(H[i].weight<min){min=H[i].weight;flag=i;}}}
}


二叉树真题内容

  • 树转二叉树

  • 算法思想:二叉树的“双亲和右孩子”对应树的“兄弟关系”,二叉树的“双亲和左孩子”对应树的“双亲和长子”//树结点结构体定义
    typedef struct CSNode{
    Elemtype data;
    struct CSNode *firstchild,*nextsibing;
    }CSNode,*CSTree;//二叉树结构体定义
    typedef struct BiTNode{
    Elemtype data;
    struct BiTNode *lchild,*rchild;
    }BiTNode,*BiTree;BiTNode * trans(CSNode*p)
    {  BiTNode *bt=NULL;if(p){bt=(BiTNode*)malloc(sizeof(BiTNode));bt->data=p->data;bt->lchild=trans(p->firstchild); //二叉树的“双亲和左孩子”对应树的“双亲和长子”bt->rchild=trans(p->nextsibling); //二叉树的“双亲和右孩子”对应树的“兄弟关系”}return bt;
    }
  • 在二叉树中查找结点值为x,打印输出其所有祖先(假定该结点x仅有一个)

  • //算法思想:采用递归算法-1.若为空,直接返回false;2.若找到结点值为x的结点,返回true;3.递归遍历根结点左右子树;bool RecurOrder(BiNode *bt,int x)
    {if(bt==NULL) return false;    //递归出口1:根结点为空的情况或者左右子树访问至空结点的情况,退出;if(bt->data==x) return true;  //递归出口2:根结点就是x的情况或者左右子树任一个找到x,打印,返回上一层,打印,,,,,循环退出递归;if(RecurOrder(bt->lchild,x) || RecurOrder(bt->rchild,x)){  //找到x之前,遇到的所有结点printf("%d",bt->data);    //输出顺序:从x结点开始打印,直到根结点;return true;}return false;   //如果左右子树没有一个可以找到x结点,返回false;
    }InitStack(s);
    bool RecurOrder(BiNode *bt,int x)
    {if(bt==NULL) return false;    //递归出口1:根结点为空的情况或者左右子树访问至空结点的情况,退出;if(bt->data==x) return true;  //递归出口2:根结点就是x的情况或者左右子树任一个找到x,打印,返回上一层,打印,,,,,循环退出递归;while(RecurOrder(bt->lchild,x) || RecurOrder(bt->rchild,x)){  //找到x之前,遇到的所有结点stack.push(s,bt->data);printf("%d",bt->data);    //输出顺序:从根结点开始打印,直到x结点;return true;}return false;           //如果左右子树没有一个可以找到x结点,返回false;
    }  
  • 写出树向二叉树转化的算法,并用中序遍历给出转换后的二叉树的结点序列和树的深度
//树转二叉树:树的兄弟关系---二叉树的双亲和右孩子关系;树的双亲和长子关系---二叉树的双亲和左孩子的关系;
步骤:1.兄弟连线;2.保留双亲和长子的连线,删除双亲和其他孩子的连线;3.顺时针旋转,使层次分明;//二叉树的结构体定义
typedef struct BiNode
{char data;struct BiNode *lchild;struct BiNode *rchild;
}BiNode,*BiTree;//树的结构体定义
typedef struct TNode
{char data;struct TNode *firstchild;struct TNode *nextsibling;
}TNode,*TTree;BiNode* Trans(TTree t)
{BiNode* bt=NULL;if(t){bt=(BiNode*)malloc(sizeof(BiNode));bt->data=t->data;bt->lchild=Trans(t->firstchild);bt->rchild=Trans(t->nextsibling);}return bt;
}//中序遍历该二叉树
递归算法:
void InOrder(BiNode *bt)
{BiNode* p=bt;if(p){InOrder(p->lchild);visit(p);InOrder(p->rchild);}
}
非递归算法:【见到“非递归”,必须想到栈;因为递归的本质,就是对栈的应用】
void InOrder(BiTree bt)
{InitStack(s);BiNode *p=bt;while(p || !IsEmpty(s)){if(p){push(s,p);p=p->lchild;}else{pop(s,p);visit(p);p=p->rchild;}}
}//求该二叉树的高度
递归法求二叉树高度:
void BiHigh(BiTree bt)
{int btl=0,btr=0,h=0;if(bt==NULL) return 0;btl=BiHigh(bt->lchild);btr=BiHigh(bt->rchild);h=(btl>btr?btl:btr)+1;return h;
}//非递归求二叉树的高度:层次遍历求二叉树高度
int BiHigh(BiNode *bt)
{BiTree Q[Maxsize];int front=-1,rear=-1;int level=0,last=0;BiNode *p;    Q[++rear]=bt;while(front<rear){p=Q[++front];if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front=last){level++;last=rear;}}return level;
}
  • 给定二叉树T有2个结点N1和N2,我们应该选择树T结点的前序,中序和后序,哪两个序列来判定结点N1必定是结点N2的祖先,并给出判断方法。详细算法+程序
//在前序序列中,N1为N2的祖先,根据NLR,则:N1一定在N2的前面;但,我们根据前序遍历,确定N1在N2的前面,不一定确定N1就是在N处,也可能在L处;所以,我们利用后序遍历LRN,若N1为N2的祖先,则N1一定在N2的后面;但,我们根据后序序列,确定N1在N2的后面,不一定确定N1就是在N处,也可能在R处;所以,当我们对前序遍历和后序遍历取交集时,我们便可以确定:N1是N2的祖先;
利用前序遍历和后序遍历,若前序遍历N1在前,后序遍历N1在后,那么:N1作为祖先;
  • 二叉链表表示二叉树,其中指针t指向根结点,试从根开始,按层次遍历二叉树的算法,每层的结点按从左到右的次序访问
//算法思路:凡是涉及层次遍历,必用到队列;1.初始化队列;2.根结点入队,在队列不空的情况下,出队一个元素,访问,若其左右孩子不为空,则分别入队;
void LevelOrder(BiTree bt)
{InitQueue(Q);EnQueue(Q,bt);BiNode* p;while(!IsEmpty(Q)){DeQueue(Q,p);visit(p);if(p->lchild) EnQueue(Q,p->lchild);if(p->rchild) EnQueue(Q,p->rchild);}
}
  • 根据二叉树的前序序列和中序序列构建二叉树【真题已经重复出现的超高频考点】
算法思想:
(1)根据前序序列确定根结点;
(2)在中序序列中找到根节点位置,确定根结点左右子树的中序序列;
(3)在前序序列中找到左右子树的前序序列;
(4)由左子树的前序序列和中序序列确定左子树;
(5)由右子树的前序序列和中序序列确定右子树;BiNode * PreInCtree(char pre[],char in[],int l1,int r1,int l2,int r2)
{  if(l1>r1) return NULL;  //1.作为递归的出口BiNode *bt=(BiNode*)malloc(sizeof(BiNode));  //2.创建一个左右孩子为空的根节点并赋根节点值bt->lchild=NULL;bt->rchild=NULL;bt->data=pre[l1];    //注意:此处是pre[l1],不是pre[0],因为考虑到递归for(int i=l2;i<=r2;i++)  //3.在in中序序列中,找到根节点所在的位置{ if(in[i]==pre[l1]) break;}//注意:左子树前序和中序确定的左子树,返回给bt->lchild;右子树,同理;bt->lchild=PreInCtree(pre,in,l1+1,l1+i-l2,l2,i-1);//4.根据根节点的位置,确定左子树前序序列中序序列范围,递归处理[注:范围的坐标一定要熟练掌握]bt->rchild=PreInCtree(pre,in,l1+i-l2+1,r1,i+1,r2);//根据根节点的位置,确定右子树前序序列中序序列范围,递归处理return bt;
}
  • C语言定义二叉树的数据结构,并给出先序和中序遍历的算法【真题已经重复出现的超高频考点】
typedef struct BiNode
{ElemType data;struct BiNode *lchild,*rchild;
}BiNode,*BiTree;//先序序列
void PreOrder(BiTree bt)
{if(!bt){visit(bt);PreOrder(bt->lchild);PreOrder(bt->rchild);}
}//中序序列
void InOrder(BiTree bt)
{if(!bt){InOrder(bt->lchild);visit(bt);InOrder(bt->rchild);}
}
  • 计算二叉树的非叶子结点
//算法思想:1.空树;2.左右子树不存在;3.递归调用左右子树;int CountNotLeaf(BiTree bt)
{int btl,btr;if(bt==NULL) return 0;else if((bt->lchild==NULL)&&(bt->rchild==NULL)) return 0;else{btl=CountNotLeaf(bt->lchild);btr=CountNotLeaf(bt->rhcild);}return (btl+btr+1);
}
  • 一棵二叉树的繁茂度定义为各层结点数的最大值与树的高度的乘积。试编写一算法,求二叉树的繁茂度。

//算法思想:树的繁茂度=树的宽度*树的高度//树的宽度&高度-算法思想:采用层次遍历,利用队列对每层结点数进行统计比较,选取最大结点数所在层的结点数;1.定义front,rear,last,maxWidth,level;
int BiWidthHight(BiTree bt)
{BiNode* p;BiNode* Q[Maxsize];int front=-1,rear=-1,last=0,level=0,nowWidth=0,maxWidth=0;Q[++rear]=bt;while(front<rear){p=Q[++front];nowWidth++;if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){level++;last=rear;if(num>maxWidth) maxWidth=nowWidth;nowWidth=0;   //注:对下一轮的宽度进行累加}return (level*maxWidth);
}//算法思想:在一般层次遍历的基础上,定义MaxW记录最大宽度层,定义NowW记录当前层最大宽度;层次遍历进行至:front==last时,判断NowW和MaxW的大小并根据情况,决定是否交换;void MaxW(BiNode *bt)
{BiNode *p;BiNode *Q[Maxsize];int front=-1,rear=-1,NowW=0,MaxW=0;Q[++rear]=bt;while(front<rear){p=Q[++front];NowW++;     //计算当前层的宽度if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){    //front在父结点一层活动,直至遇见最后的结点:last;last=rear;     //rear在左右孩子结点一层活动;last重新定位rear;if(NowW>MaxW){MaxW=NowW;NowW=0;}}}return MaxW;
}//算法思想:在一般层次遍历的基础上,定义level表示当前结点所在层,定义last表示当前结点所在层最右端结点;层次遍历,每次出队元素,就与last比较,若相等,则level++;last移动到下一层的最右端结点;void LevelHigh(BiNode *bt)
{BiNode* Q[Maxsize];int front=-1;rear=-1;BiNode* p;int last=0,level=0;Q[++rear]=bt;while(front<rear){p=Q[++front];if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){   //注:front在父结点一层遍历,rear在左右孩子结点一层遍历;当front==last时,rear也刚好抵达左右孩子所在层的最后一个结点,所以才有:last=rear;level++;last=rear;}}return level;}
  • 如果一棵哈夫曼树T有n个叶子结点,那么树T有多少结点?详细算法+程序【本算法是在创建哈夫曼树的同时,统计哈夫曼的总结点数】+WPL的算法

算法思想:根据n个叶子结点,创建哈夫曼树;一边创建,一边统计,哈哈哈;//模块一:哈夫曼树的结点定义
#define Maxsize 100
typedef struct HuffTNode
{int parent;int lchild;int rchild;int weight;
}HuffTNode,*HuffTree;HuffTNode H[Maxsize];    //生成一个给定结点类型数组//模块二:在未被选取的结点中找到权值最小的一个,返回其编号;
int find_min(int n)
{int tag=-1,min=9999;   //tag记录权值最小的结点的标号;另外,一旦返回-1,说明找不到无双亲的结点,树已建立完毕for(int i=0;i<=n;i++){if(H[i].parent==0){     //在还未参与建树的结点中查找if(H[i].weight<min){min=H[i].weight;tag=i;}}}return tag;
}//模块三:创建哈夫曼树
int mian()
{int n,minl,minr;scanf("%d",&n);  //叶子结点数for(int i=0;i<n;i++){          //对叶子结点进行初始化,其权值设为其序号,双亲左右孩子均设为0H[i].parent=0;H[i].lchild=0;H[i].rchild=0;H[i].weight=i;   //权值设为其序号;}i--;  //因为i的实际范围是n-1,当最近上边的循环,最后使得i=n,所以:n-1while(1){//查找左子树minl=find_min(i);H[minl].parent=i+1;     //本轮最小值结点的parent赋值i+1(i+1此时,作为父结点的序号)//查找右子树minr=find_min(i);if(minr==-1){break;}else{H[minr].parent=i+1;}H[i+1].lchild=minl;  //父结点序号:i+1,同时也是父结点的权值;给父结点的左右孩子赋值;H[i+1].rchild=minr;H[i+1].parent=0;H[i+1].weight=H[minl].weight+H[minr].weight;i++;  //下一轮循环}printf("结点总数是:%d\n",i+1);
}//模块四:求WPL
int WPL(int n)
{int WPL=0;                for(int i=0;i<n;i++){   //从叶子向根走int level=0;   //记录本轮结点距离根结点的距离current=i;   //定义当前访问的结点father=H[i].parent;   //定义当前访问结点的父结点while(father!=0){   //从叶子结点访问直到根结点current=parent;parent=H[i].parent;level++;}WPL+=level*H[i].weight;  //WPL计算公式}return WPL;
}
  • 假设用于通信的电文仅由8个字母{a,b,c,d,e,f,g,h}构成,它们在电文中出现的概率分别为{0.07,0.19,0.02,0.06,0.32,0.03,0.21,0.10},试画出哈夫曼树,并写出哈夫曼编码方案

    常规 问题:哈夫曼编码问题【注意:严格按照构建哈夫曼树的4步骤走,尤其“每一步,必须从集合中仔细看清楚:最小的和次小的啊!自己已经错过好几次了,很简单的东西,不要丢分啊!】

  • 哈夫曼代码-一个综合所有问题的代码题-真题180【此处真的超级重要,超级重要,超级完美,对于现在的我来说;】

算法思想:根据n个叶子结点,创建哈夫曼树;一边创建,一边统计,哈哈哈;//模块一:哈夫曼树的结点定义
#define Maxsize 100
typedef struct Huu
{int parent;int lchild;int rchild;int data;
}Huu;Huu Hu[Maxsize];    //生成一个给定结点类型数组//模块二:在未被选取的结点中找到权值最小的一个,返回其编号;
int find_min(int i)
{int tag=-1,min=9999;   //tag记录权值最小的结点的标号;另外,一旦返回-1,说明找不到无双亲的结点,树已建立完毕for(int j=0;j<=n;j++){if(Hu[j].parent==0){     //在还未参与建树的结点中查找if(Hu[j].data<min){min=Hu[j].data;tag=j;}}}return tag;
}//模块三:霍夫曼编码+wpl计算
int HuffmanCoding(int n, char *word)
{//用来保存指向每个哈弗曼编码串的指针(指针的指针)char **HC=(char **)malloc(n*sizeof(char*)); //字符串数组 HC是一个指针,指向另一个指针,那个指针指向一个字符(串)/临时空间,用来保存每次求得的一个哈弗曼编码串char *code=(char*)malloc(30*sizeof(char)); //30根据情况自定义的一个数字code[29]='\0'; //编码结束符,即:字符数组的结束标志//下面求每个字符的哈夫曼编码,同时顺带可以求WPL【WPL也要会手动计算】int i;for(int i=0;i<n;i++){   //从叶子结点走int wpl=0;int current =i,level=0; //level定义本轮结点距离根结点的距离   //current定义当前访问的结点int father=Hu[i].parent.  //定义当前结点的父结点int start=29;(n-1)//初始化为:编码结束符的位置   暂存数组的辅助索引//从叶子结点遍历哈夫曼树直到根结点while(father!=0){if(Hu[father].lchild==current)code[--start]='0';elsecode[--start]='1';current=father;father=Hu[current].parent;}//为第i个字符的编码串分配存储空间HC[i]=(char*)malloc((29-start+1)*sizeof(char));  //+1是为了保存'\0';strcpy(HC[i],code+start); //编码串复制      wpl+=level*Hu[i].data;   //计算WPL公式}return wpl;
//}如果单独考察,这里可以直接作为一个函数封装起来了;//输出各字符的编码串for(i=0;i<n;i++){printf("\n");int j=0;printf("%c编码为:",word[i]);while(HC[i][j]!='\0'){printf("%c",HC[i][j]);j++;}printf("%c",HC[i][j]);}
}//模块三:创建哈夫曼树+结点数统计
int mian()
{int i,min_1,min_2,n,weigh;n=5;char word[5]="ABCDE";//对叶子结点进行初始化,双亲,左右孩子均为0;for(int i=0;i<n;i++){          //对叶子结点进行初始化,其权值设为其序号,双亲左右孩子均设为0Hu[i].parent=0;Hu[i].lchild=0;Hu[i].rchild=0;Hu[i].weight=i;   //权值设为其序号;}i--;  //因为i的实际范围是n-1,当最近上边的循环,最后使得i=n,所以:n-1while(1){//查找左子树min_l=find_min(i);Hu[min_l].parent=i+1;     //本轮最小值结点的parent赋值i+1(i+1此时,作为父结点的序号)//查找右子树min_2=find_min(i);if(min_2==-1){Hu[min_1].parent=0;break;}else{Hu[min2].parent=i+1;}Hu[i+1].lchild=min_1;  //父结点序号:i+1,同时也是父结点的权值;给父结点的左右孩子赋值;Hu[i+1].rchild=min_2;Hu[i+1].parent=0;Hu[i+1].data=Hu[min_1].data+H[min_2].data;i++;  //下一轮循环    统计结点数}printf("结点总数是:%d\n",i+1);
}//模块四:求WPL
int WPL(int n)
{int WPL=0;                for(int i=0;i<n;i++){   //从叶子向根走int level=0;   //记录本轮结点距离根结点的距离current=i;   //定义当前访问的结点father=H[i].parent;   //定义当前访问结点的父结点while(father!=0){   //从叶子结点访问直到根结点current=parent;parent=H[i].parent;level++;}WPL+=level*H[i].weight;  //WPL计算公式}return WPL;
}

目录

SuperKey1- 二叉树
(1) 二叉树基本功
⚫ 二叉树的链式存储结构
⚫ 二叉树的先序遍历(递归)
⚫ 二叉树的中序遍历(递归)
⚫ 二叉树的后序遍历(递归)
⚫ 二叉树的先序遍历(非递归):
⚫ 二叉树的中序遍历(非递归):
⚫ 二叉树的后序遍历(非递归):
(2) 二叉树判定问题【代码+手动画图】
⚫ 二叉树判定是否是排序二叉树:
⚫ 二叉树判定是否是完全二叉树:
⚫ 二叉树判定是否是平衡二叉树:
(3) 二叉树统计问题【递归+整体思维】
⚫ 计算二叉树结点数:
⚫ 计算二叉树叶子结点数:
⚫ 计算二叉树单分支结点个数:
⚫ 计算二叉树双分支结点个数:
⚫ 计算二叉树各结点的子孙个数:
⚫ 计算二叉树的高度(3 种方法):
(4) 二叉树构建问题
⚫ 前序遍历构建二叉树:【根本】
⚫ 前序遍历&中序遍历构建二叉树:【涉及“前序遍历构建二叉树”的关键点】
⚫ 后序遍历&中序遍历构建二叉树:【涉及“前序遍历构建二叉树”的关键点】
⚫ 层次遍历&中序遍历构建二叉树:
(5) 二叉树-层次遍历专题:
⚫ 层次遍历:
⚫ 设计二叉树自下而上,自右至左的层次遍历:[凡是逆序,正反颠倒,3 秒内必须想到:
栈]
⚫ 二叉链表存储结构,设计非递归算法求二叉树高度:
⚫ 二叉链表存储结构,设计递归算法求二叉树高度:
⚫ 二叉链表存储结构,求非空二叉树的宽度:
⚫ 二叉链表存储结构,判断给定二叉树是否是完全二叉树:
(6) 二叉树-后序遍历专题
⚫ 二叉链表存储结构,对树中每个元素值为 x 的结点,删除以它为根的子树,并释放相应
空间;
(7) 二叉树-线索二叉树专题
(8) 二叉树真题问题
⚫ 一棵具有 n 个节点的完全二叉树以顺序方式存储在数组中,设计一个算法构造该二叉树
的二叉链式存储结构:【与“前序遍历二叉树”有很大关联】
⚫ 树转二叉树:
⚫ 在二叉树中查找结点值为 x,打印输出其所有祖先(假定该结点 x 仅有一个)
⚫ 写出树向二叉树转化的算法,并用中序遍历给出转换后的二叉树的结点序列和树的深度
⚫ 给定二叉树 T 有 2 个结点 N1 和 N2,我们应该选择树 T 结点的前序,中序和后序,哪两个
序列来判定结点 N1 必定是结点 N2 的祖先,并给出判断方法。详细算法+程序
⚫ 二叉链表表示二叉树,其中指针 t 指向根结点,试从根开始,按层次遍历二叉树的算法,
每层的结点按从左到右的次序访问
⚫ 根据二叉树的前序序列和中序序列构建二叉树【真题已经重复出现的超高频考点】
⚫ C 语言定义二叉树的数据结构,并给出先序和中序遍历的算法【真题已经重复出现的超高
频考点】
⚫ 计算二叉树的非叶子结点
⚫ 一棵二叉树的繁茂度定义为各层结点数的最大值与树的高度的乘积。试编写一算法,求
二叉树的繁茂度。
⚫ 哈夫曼代码-一个综合所有问题的代码题-真题 180【此处真的超级重要,超级重要,超级
完美,对于现在的我来说;】+手动求解哈夫曼【画图】
SuperKey2- 图
⚫ 邻接矩阵&邻接表&图的创建
⚫ DFS(图的深度优先遍历)
⚫ BFS(图的广度优先遍历)
⚫ 迷宫问题
⚫ 八皇后问题
⚫ 最小生成树-(要求邻接矩阵,就用:Prim 算法;没要求,也用它;)
⚫ 最小生成树-Kruskal 克鲁斯卡尔算法(改进的)
⚫ 最短路径 Dijkstra 算法
⚫ 最短路径 Floyd 算法
⚫ 冲刺模拟卷-各大图的各大算法手动求解
SuperKey3- 查找
⚫ 二叉排序树【代码+手工画】-考的较少【最后掌握】
⚫ 哈希表问题【冲刺预测 D 卷-10 页+2019+2017】重点必须掌握
SuperKey3- 排序
⚫ 二分查找
⚫ 冒泡排序 2017【栈&递归】
⚫ 平均时间&最坏情况&辅助存储&稳定性角度—>各种内部排序方法比较【窍门记】2010+
⚫ 各种内部排序画出排序结果【2012+2017+21.1】
SuperKey4- 单链表
⚫ 单链表查找+插入和删除问题【超级基础】
⚫ 单链表合并问题【超级重点】
⚫ 单链表逆置【高频考点】
⚫ 链表排序+去重
⚫ 链表交集问题【递增有序链表】【新型考点】【若无序,先排序,再交集】灵活一点
⚫ 单链表最大值问题【递归】
⚫ 循环链表【约瑟夫问题】【超级重点】-【数组||链表实现】
⚫ 之前的手写代码,再过一遍
SuperKey5- 串
⚫ 顺序存储-最长字串&位置【高频考点】
⚫ 整数转化字符串【递归实现】【高频考点】+atoi 函数
⚫ KMP 算法-判断 n 是否是 m 的字串【超级重点】
⚫ 字符串逆序【递归】
⚫ 几个字符串处理的经典函数【超级重点&超级基础】
⚫ 最大子对称字符串长度问题(回文数)+字符串单词出现频率统计问题【今年新型问题】
SuperKey6- 队列&栈 栈 【 低频】
⚫ 2 个栈模拟队列
⚫ 循环链队模拟队的队的初始化&出队&入队
⚫ 逆序循环队列所有元素
SuperKey7- 重要 专题&C 语言
⚫ 120 题抽典型问题&举一反三问题解决
⚫ 排列组合问题
⚫ 经典算法问题
⚫ 最大公约&最小公倍
⚫ 重要结构体定义
⚫ 前中后缀表达式转化与计算
⚫ 方程求根


SuperKey1- 二叉树
(1) 二叉树基本功
⚫ 二叉树的链式存储结构

typedef struct BiNode
{  ElemType data;struct BiNode *lchild,*rchild;
}BiNode,*BiTree;

⚫ 二叉树的先序遍历(递归)

void PreOrder(BiTree bt)
{ if(bt!=NULL){visit(bt);               //=printf("%d",bt->data);PreOrder(bt->lchild);PreOrder(bt->rchild);}
}

⚫ 二叉树的中序遍历(递归)

void InOrder(BiTree bt)
{ if(bt!=NULL){InOrder(bt->lchild);visit(bt);InOrder(bt->rchild);}
}
PEP:04

⚫ 二叉树的后序遍历(递归)

void PostOrder(BiTree bt)
{if(bt!=NULL){PostOrder(bt->lchild);PostOrder(bt->rchild);visit(bt);}
}

⚫ 二叉树的先序遍历(非递归):

void PreOrder(BiTree bt)
{InitStack(s);BiNode *p=bt;    //1.初始化栈和指针pwhile(p || !IsEmpty(s))       //2.在p所指结点不空或者栈s不空的情况下,一直循环遍历{ if(p){                   //2.1p所指结点不空,访问,入栈,左孩子循环visit(p);push(s,p);p=p->lchild;}else{                    //2.2出栈元素赋给p,右孩子循环pop(s,p);p=p->rchild;}}
}

⚫ 二叉树的中序遍历(非递归):

算法思想:1.定义并初始化栈S和二叉结点P并指向根结点;2.在根结点不空且栈不空的情况下,循环:根结点不空入栈并循环访问其左孩子并入栈;若根结点空,弹出栈顶元素,访问它,并访问其右孩子;继续返回2开始;
void InOrder(BiNode *bt)
{ InitStack(s);BiNode *p=bt;while(p || !IsEmpty(S)){if(p){push(s,p);p=p->lchild;}else{pop(s,p);printf("%c",p->data);p=p->rchild;}
}

⚫ 二叉树的后序遍历(非递归):

//Algorithm-Thought:1.从根结点开始,沿着左孩子依次入栈,直至为空;2.“读取”栈顶元素,若其右孩子不空并且右孩子未被访问过,转1;3.否则,栈顶元素出栈并返回.
难点:分清2中返回根结点时,是从左子树还是右子树,因此:定义辅助指针r-指向最近刚访问过的结点.
void PostOrder(BiTree T)
{//1.初始化栈和指针;   InitStack(s);BiNOde *p=T,*r=NULL;//注:辅助指针r,这是区别非递归前序和中序的地方之一;//2.在指针p和栈s不空的情况下,遍历;while(p || !IsEmpty(s)){  //注:!IsEmpty(s),not IsEmpty(s);if(p){          //3.在指针不空的情况下,持续访问左孩子并入栈;push(s,p);p=p->lchild;}else{           //4.若指针为空,“读取”栈顶元素,若该元素右孩子存在且未被访问过,则右孩子入栈并返回第3步;GetTop(p);if(p->rchild && p->rchild!=r){p=p->rchild;push(s,p);p=p->lchild;}else{         //5.若指针为空,“读取”栈顶元素,若钙元素右孩子不存在或已访问过,则出栈并访问,并重新标记指针r和p的指向;pop(s,p);visit(p);r=p;p=NULL;}}}
}

(2) 二叉树判定问题【代码+手动画图】
⚫ 二叉树判定是否是排序二叉树:

算法思想:中序遍历二叉树,若前驱始终小于当前节点,则是排序二叉树BST.1.若树根为空,则是:排序二叉树;2.递归判断左子树是否是二叉排序树并将结果返回;3.判断左子树结果以及当前结点与前驱结点是否满足排序二叉树条件;4.
int predt = -9999;  //定义全局变量,始终作为前驱
int JuBST(BiTree bt)
{ int btl,btr;//1.空树是排序二叉树if (bt==NULL) return 1;  else{//2.判断左子树是否是二叉排序树并将结果返回btl=JuBST(bt->lchild);  //3.判断当前节点是否是二叉树if (btl==0 || bt->data<=predt)  return 0;predt=bt->data;    //本轮的当前结点就是右子树的前驱结点;//4.判断右子树是否是二叉排序树并返回结果btr=JuBST(bt->rchild);return btr;    //程序如果可以走到这一步,二叉树是否是排序二叉树的重任,就只看:btr的值了;}
}

⚫ 二叉树判定是否是完全二叉树:

//算法思想:层次遍历二叉树,将包括空结点在内的所有结点全部入队;若遇到空节点,判断其下一结点,若不为空,则不是完全二叉树.
1.空二叉树便是完全二叉树;
2.入队根结点;
3.在队列不空的情况下,出队一个结点,判断其是否为空;若不为空,则分别入队其左右孩子;
4.在队列不空的情况下,出队一个结点,判断其是否为空;若其为空,在队列不空的情况下,循环将结点出队,并判断结点是否存在不为空的情况,若存在,则不是完全二叉树;
int JuComplete(BiTree bt)
{BiTree p;InitQueue(Q);//1.空二叉树便是完全二叉树if (bt==NULL) return 1;//2.根节点入队EnQueue(Q,bt)while(!IsEmpty(Q)) //在队列不空的情况下,循环;{ DeQueue(Q,p)  //3.判断出队结点是否为空,若不空分别入队左右孩子结点;此处与一般层次遍历有区别;if(p){ EnQueue(Q,p->lchild);EnQueue(Q,p->rchild);}else{  //4.若出队结点为空,在队列还不空的情况下,循环出队后续所有结点,若存在不空,则非完全二叉树While(!IsEmpty(Q){ DeQueue(Q,p);if(p) return 0;}}}
}

⚫ 二叉树判定是否是平衡二叉树:

//算法思想:定义平衡因子balance和左右子树高度hl,hr;若balance=1且(hl-hr)<=1,则二叉树为平衡二叉树,否则:非平衡二叉树;
1.若根节点bt为空,则二叉平衡树,balance=1,h=0;
2.若根节点仅单独存在,则二叉平衡树,balance=1,h=1;紧接着,递归判断左右子树;
3.根据左右子树返回的左右子树高度,若高度差<=1且同时满足左右子树也是平衡二叉树的情况下,则平衡二叉树,否则:非平衡二叉树void JuAVL(BiTree bt,&h,&balance) //利用&返回balance和h
{  int bl,br,hr,hl; //定义左右子树的平衡因子及高度if(bt==NULL)     {  balance=1;    //1.根空h=0;}else if((bt->lchild==NULL)&&(bt->rchild==NULL))    //2.仅根节点存在{  balance=1;h=1;}else{JuAVL(bt->lchild,hl,bl);    //3.递归遍历左右子树,判断JuAVL(bt->rchild,hr,br);h=(hl>hr?hl:hr)+1;                //因为递归存在,所以此句必须存在:整体树高=最高左右子树高度+1if(abs(hl-hr)<=1) balance=bl&&br; //核心:在整体树高度h<=1的情况下,同时左右子树balance=1,则:平衡二叉树else balance=0;}
}

(3) 二叉树统计问题【递归+整体思维】
⚫ 计算二叉树结点数:

//算法思想:递归*(二叉树结点数=根1+左子树结点数+右子树结点数)int CountAll(BiTree bt)   //函数参数定义中:BiTree bt = BiNode *bt
{  int btl,btr;if(bt==NULL) return 0;   //递归出口else{  btl=CountALL(bt->lchild);   //递归左子树btr=CountALL(bt->rchild);   //递归右子树return (btl+bt2+1);}
}

⚫ 计算二叉树叶子结点数:

//算法思想:首先,根节点为空 ;其次,仅根节点存在;最后:递归*(二叉树结点数=左子树结点数+右子树结点数)int CountLeaf(BiTree bt)
{  int btl,btr;    //基本变量定义if(bt==NULL) return 0;   //根结点为空else if((bt->lchild==NULL)&&(bt->rchild==NULL)) return 1;   //仅仅只有根结点存在else{  btl=CountLeaf(bt->lchild);   //左子树,右子树btr=CountLeaf(bt->rchild);return (btl+btr);}}

⚫ 计算二叉树非叶子结点数

//算法思想:1.空树;2.左右子树不存在;3.递归调用左右子树;int CountNotLeaf(BiTree bt)
{int btl,btr;if(bt==NULL) return 0;else if((bt->lchild==NULL)&&(bt->rchild==NULL)) return 0;else{btl=CountNotLeaf(bt->lchild);btr=CountNotLeaf(bt->rhcild);}return (btl+btr+1);
}

⚫ 计算二叉树单分支结点个数:

//算法思想:首先,根节点为空;其次,若根节点左右子树仅存在任意一个,则:单分支结点数=根节点1+左子树单分支结点数+右子树单分支结点数;否则,单分支结点数=左子树单分支结点数+右子树单分支结点数.int CountSimple(BiTree bt)
{ int btl,btr;if(bt==NULL) return 0;//根结点左右子树仅任意一个存在的情况:单分支结点数=本结点1+左子树结点数+右子树结点数else if((bt->lchild==NULL && bt->rchild!=NULL)||(bt->lchild!=NULL && bt->rchild==NULL)){  btl=CountSimple(bt-lchild);btr=CountSimple(bt-rchild);return 1+btl+btr;}//根结点左右子树非仅任意一个存在的情况:单分支结点数=左子树结点数+右子树结点数else { btl=CountSimple(bt-lchild);btr=CountSimple(bt-rchild);return btl+btr;}
}

⚫ 计算二叉树双分支结点个数:

//算法思想:首先,根节点为空;其次,若根节点左右子树都存在,则:双分支结点数=根节点1+左子树双分支结点数+右子树双分支结点数;否则,双分支结点数=左子树双分支结点数+右子树双分支结点数;int CountDouble(BiTree bt)
{  int btl,btr;if(bt==NULL) return 0;  //1.根结点为空//2.根结点左右子树都存在的情况:双分支结点数=根节点1+左子树双分支结点数+右子树双分支结点数else if(bt->lchild!=NULL && bt-rchild!=NULL)  //双分支{  btl=CountDouble(bt->lchild);btr=CountDouble(bt->rchild);return 1+btl+btr;}//3.根结点左右子树非都存在的情况:双分支结点数=左子树双分支结点数+右子树双分支结点数else  //单分支{  btl=CountDouble(bt->lchild);btr=CountDouble(bt->rchild);return btl+btr;}
}

⚫ 计算二叉树各结点的子孙个数:

//算法思想:定义两个函数:函数1:计算所指结点的所有子孙数;函数2:在使用函数1的基础上,递归遍历所有结点并输出各结点所有子孙数;函数1:首先,根节点为空;其次,递归遍历左子树并逐个+1;之后,递归遍历右子树并逐个+1;最后,左右子树子孙数求和;int CountSub(BiTree bt)
{ int btl=0,btr=0;if(bt==NULL) return 0;  //空树if(bt->lchild) btl=CountSub(bt->lchild)+1;
//递归左子树结点数:一直遍历到左子树最左端,然后:从btl=0开始,逐个返回并+1,同时判断下边条件是否满足情况【递归本质的描述】if(bt->rchild) btr=CountSub(bt->rchild)+1;return (btl+btr);
}
//函数2:在根不空的情况下:首先,输出根的子孙数;其次,递归遍历左右子树同时调用CountSub函数,统计计算每个节点的子孙数;void CountEvery(BiTree bt)
{ if(bt==NULL) return;else{printf("结点值%d的结点数:%d\n",bt->data,CountSub(bt));  //首先输出根结点的子孙数,之后:根据遍历的情况,逐个输出结点子孙数CountEvery(bt-lchild);  //递归遍历左右子树CountEvery(bt-rchild);}
}
算法思想:定义函数1:计算当下结点的子孙数;定义函数2:在函数1的基础上,递归遍历所有结点并逐个计算输出结点子孙个数.函数1:
算法思想:首先,根节点空;其次,递归遍历左右子树并加1;最后,返回左右子树之和;
int CountNow(BiTree bt)
{  int btl=0,btr=0;if(bt==NULL) return 0;if (btl->lchild) btl=CountNow(bt->lchild)+1;if (btr->rchild) btr=CountNow(bt->rchild)+1;return (btl+btr);
}函数2:
算法思想:从根结点开始,首先输出根节点子孙个数;然后,逐个遍历所有的结点并同时在函数1的基础上计算并输出每个结点的子孙数.
int CountEvery(BiTree bt)
{  if(bt==NULL) return 0;else{ printf("结点值为%d的子孙结点数是:%d\n",bt->data,CountNow(bt));CountEvery(bt->lchild);CountEvery(bt->rchild);}
}

⚫ 计算二叉树的高度(3 种方法):

  • 递归法
//算法思想:递归法:首先,根结点为空;其次,分别递归左右子树并返回左右子树高度;最后,比较左右子树高度,二叉树高度=较高者+1.int CountHigh(BiTree bt)
{  if(bt==NULL) return 0;  //根空highl=CountHigh(bt->lchild);  //遍历左子树,计算高度highr=CountHigh(bt->rchild);h=(highl>highr?highl:highr)+1;  //二叉树高度=左右子树高度较大者+1return h;
}
  • 非递归法-后序遍历 || 层次遍历
#include<iostream>
#include<stack>
#include<queue>
using namespace std;
typedef struct BiTNode{ char data; struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree; void CreateTree(BiTree &T)
{ char ch; cin>>ch; if(ch=='#') T=NULL; else { T=(BiTree)malloc(sizeof(BiTNode)); if(!T)  cout<<"生成结点错误!"<<endl; T->data=ch; CreateTree(T->lchild); CreateTree(T->rchild); }
} //法1:后序遍历,结点最大栈长即为树的高度
int BT_high(BiTree T)
{ BiTree p=T,r=NULL; int max=0;                                     //树高 stack<BiTree> s; while(p||!s.empty()) { if(p!=NULL) { s.push(p); p=p->lchild; } else { p=s.top(); if(p->rchild!=NULL && p->rchild!=r) p=p->rchild; else { if(s.size()>max) max=s.size();//最大层次即为高度 r=p; s.pop(); p=NULL; } } } return max;
} //法2:层次遍历,层次即为高度
int BT_level_depth(BiTree T)
{ if(!T)  return 0; BiTree p=T,Q[100]; int front=-1,rear=-1,last=0,level=0; Q[++rear]=p; while(front<rear)   //队列不空的情况下,出队一个元素,左右孩子不为空,则入队;{ p=Q[++front]; if(p->lchild) Q[++rear]=p->lchild; if(p->rchild) Q[++rear]=p->rchild; if(front==last)      { last=rear; level++;               //层次+1 } } return level;
} //法3:递归求树高1
int max1=0;//树高
int BT_depth1(BiTree T,int depth)
{ if(T) { if(T->lchild) BT_depth1(T->lchild,depth+1); if(T->rchild) BT_depth1(T->rchild,depth+1); } if(depth>max1)    max1=depth; return depth;
} //法3:递归求树高2
int Height (BiTree T)
{   if(T==NULL) return 0; else  { int m = Height ( T->lchild ); int n = Height(T->rchild); return (m > n) ? (m+1) : (n+1);  }
} int main()
{ BiTree T=NULL; CreateTree(T); cout<<"后序遍历求树高:"<<endl; cout<<BT_high(T)<<endl; cout<<"层次遍历求树高:"<<endl; cout<<BT_level_depth(T)<<endl; cout<<"递归求树高1:"<<endl; BT_depth1(T,1); cout<<max1<<endl; cout<<"递归求树高2:"<<endl; cout<<Height(T)<<endl; return 0;
}

(4) 二叉树构建问题
⚫ 前序遍历构建二叉树:【根本】

//算法思想:为了建立目标二叉树,所以:在目标二叉树的基础上建立扩展二叉树,
也就是:给目标二叉树的每个结点的空左右孩子以“#”代替,并通过前序遍历获得扩展二叉树的前序遍历序列;
该扩展二叉树的遍历序列便是前序扩展二叉树的输入序列;每次逐个输入该前序扩展二叉树的序列的一个字符,递归遍历过程中:若不为“#”,则构建新结点并赋值,否则,返回空值.BiNode * PreStr(BiNode *bt)
{  input(ch);  //保证每次读取一个字符if(ch=='#') bt=NULL;  //如果读取字符为'#',则赋空else{bt=new BiNode;bt->data=ch;        //创建一个新的结点并赋值bt->lchild=PreStr(bt->lchild);    //区别于一般的递归,此处一定是:bt-lchild=...;bt->rchild=PreStr(bt->rchild);}   //递归创建右子树结点return bt;
}
//递归的本质:进入递归入口时,系统自动创建堆栈,记录:当前程序语句所在行+接受递归返回状态的变量C++版本
void PreStr(BiNode*&bt) //使用引用型&,传入下一层的参数根据下一层引用型参数的变化,实时变化;
{  input(ch);if(ch=='#') bt=Null;  //此处注意:bt==NULL,以便后续可以创建空结点else{bt=new BiNode;bt->data=ch;PreStr(bt-lchild);PreStr(bt-rchild);}return ;
}

⚫ 前序遍历&中序遍历构建二叉树:【涉及“前序遍历构建二叉树”的关键点】

算法思想:
(1)根据前序序列确定根结点;
(2)在中序序列中找到根节点位置,确定根结点左右子树的中序序列;
(3)在前序序列中找到左右子树的前序序列;
(4)由左子树的前序序列和中序序列确定左子树;
(5)由右子树的前序序列和中序序列确定右子树;BiNode * PreInCtree(char pre[],char in[],int l1,int r1,int l2,int r2)
{  if(l1>r1) return NULL;  //1.作为递归的出口BiNode *bt=(BiNode*)malloc(sizeof(BiNode));  //2.创建一个左右孩子为空的根节点并赋根节点值bt->lchild=NULL;bt->rchild=NULL;bt->data=pre[l1];for(int i=l2;i<=r2;i++)  //3.在in中序序列中,找到根节点所在的位置{ if(in[i]==pre[l1]) break;}bt->lchild=PreInCtree(pre,in,l1+1,l1+i-l2,l2,i-1);//4.根据根节点的位置,确定左子树前序序列中序序列范围,递归处理[注:范围的坐标一定要熟练掌握]bt->rchild=PreInCtree(pre,in,l1+i-l2+1,r1,i+1,r2);//根据根节点的位置,确定右子树前序序列中序序列范围,递归处理return bt;
}

⚫ 后序遍历&中序遍历构建二叉树:【涉及“前序遍历构建二叉树”的关键点】

算法思想:(1)根据后序遍历确定根节点;(2)在中序序列中找到根节点位置,确定根节点左右子树的中序序列;(3)在后序序列中找到左右子树的后序序列;(4)由左子树的后序序列和中序序列确定左子树;(5)由右子树的后序序列和中序序列确定右子树;BiNode *PostInCtree(char post[],char in[],int l1,int r1,int l2,int r2)
{  //1.作为一个出口if(l1>r1) return NULL;//2.构建一个左右孩子为空的根节点并赋根节点值BiNode *bt=(BiNode*)malloc(sizeof(BiNode));bt-lchild=NULL;bt->rchild=NULL;bt->data=post[r1];//3.找到根节点在中序序列中的位置for(int i=l2;i<r2;++i)if(post[r1]==in[i]) break;    //4.根据根节点的位置,计算出左孩子后序序列&中序序列的范围,递归处理bt->lchild=PostInCtree(post,in,l1,l1+i-l2-1,l2,i-1);//根据根节点的位置,计算出右孩子后序序列&中序序列的范围,递归处理bt->rchild=PostInCtree(post,in,l1+i-l2,r1-1,i+1,r2);return bt;
}

⚫ 层次遍历&中序遍历构建二叉树:

算法思想:主函数步骤中已经写明int search(char a[],int key,int l,int r) //在范围为l-r的数组a中,寻找key所在的位置并返回
{ for(int i=l;l<=r;++l)if(a[i]==key) return i;return -1;
}
//逐个遍历level数组中n个元素,并利用search()函数判断:是否在in数组l-r范围内存在;若存在,则将其置于sublevel数组中且随着k++,有序且连续地排列;
void GetSubLevel(char sublevel[],char level[],char in[],int n,int l,int r)
{  int k=0;for(int i=0;i<n;++i){if(search(in,level[i],l,r)!=-1)sublevel[++k]=level[i];}
}
BiNode * LevInCtree(char level[],char in[],int n,int l,int r)
{  //1.递归出口if(l>r) return NULL;//2.创建一个左右子树为空的根节点并赋根节点值【根节点值=level[0]】BiNode *bt=(BiNode*)malloc(sizeof(BiNode));bt->lchild=NULL;bt->rchild=NULL;bt->data=level[0];/*3.此处与前序中序构建二叉树和后序中序构建二叉树不同,因为在层次遍历中寻找到根节点并在中序遍历中区分左右子树集合,但再次返回层次遍历发现:左右子树集合不再连续;
所以,不能通过之前的计算范围的方式,进行处理了;
所以,我们的解决措施是:将不连续的左右子树集合通过GetSubLevel()函数提取出来,
分别连续有序地存储在LN长度的llevel数组和RN长度的rlevel数组,最后可以同之前一样,
利用递归继续处理;*///为此:我们需要先构建LN和RN长度的level和rlevel数组int i=search(in,level[0],l,r);int LN=i-l;char llevel[LN];int RN=r-i;char rlevel[RN];//接着:在level数组中逐个遍历n个元素,同时在in数组的有效范围内,进行比较;若存在,则:有序连续地存放在llevel和rlevel数组中GetSubLevel(llevel,level,in,n,l,i-1);GetSubLevel(rlevel,level,in,n,i+1,r);//4.针对处理后产生的有序连续的llevel和rlevel数组,同前序中序构建二叉树和后序中序构建二叉树一样,递归处理bt-lchild=LevInCtree(llevel,in,LN,l,i-1);bt-rchild=LevInCtree(rlevel,in,RN,i+1,r);return bt;
}

(5) 二叉树-层次遍历专题:
⚫ 层次遍历:

算法思想:1.创建一个辅助队列,根结点入队;2.在队列不空的情况下,循环:出队根结点,访问,若根结点左右孩子不空,则分别入队;//二叉树结点的定义
typedef struct BiNode
{ElemType data;struct BiNode *lchild,*rchild;
}BiNode,*BiTree;//层次遍历
void LevelOrder(BiNode *bt)
{InitQueue(s);BiNode *p;Enqueue(s,bt);      //注意:push(s,bt)是栈的用法,此处是队列的入队的写法,不许混淆;while(!IsEmpty(s)){p=DeQueue(s);if(p->lchild) EnQueue(s,p->lchild);if(p->rchild) EnQueue(s,p->rchild);}
}

⚫ 设计二叉树自下而上,自右至左的层次遍历:[凡是逆序,正反颠倒,3 秒内必须想到:

//算法思想:在一般层次遍历的基础上,将出队的元素压入栈中;最后,逐个弹出;
void ReLevel(BiNode *bt)
{if(bt==NULL) return;InitStack(s);InitQueue(q);BiNode *p;EnQueue(q,bt);while(!IsEmpty(q)){DeQueue(q,p);        //错误写法:p=DeQueue(q);push(s,p);if(p->lchild) EnQueue(q,p->lchild);if(p->rchild) EnQueue(q,p->rchild);}while(!IsEmpty(s))pop(s,p);visit(p);
}    

⚫ 二叉链表存储结构,设计非递归算法求二叉树高度:

 //算法思想:在一般层次遍历的基础上,定义level表示当前结点所在层,定义last表示当前结点所在层最右端结点;层次遍历,每次出队元素,就与last比较,若相等,则level++;last移动到下一层的最右端结点;void LevelHigh(BiNode *bt)
{BiNode* Q[Maxsize];int front=-1;rear=-1;BiNode* p;int last=0,level=0;Q[++rear]=bt;while(front<rear){p=Q[++front];if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){   //注:front在父结点一层遍历,rear在左右孩子结点一层遍历;当front==last时,rear也刚好抵达左右孩子所在层的最后一个结点,所以才有:last=rear;level++;last=rear;}}return level;}

⚫ 二叉链表存储结构,设计递归算法求二叉树高度:

//算法思想:(1)递归法:首先,根结点为空;其次,分别递归左右子树并返回左右子树高度;最后,比较左右子树高度,二叉树高度=较高者+1.int RecurOrder(BiNode *bt)
{if(bt==NULL) return 0;   //空树,高度为0;BiNode *btl,*btr;btl=RecurOrder(bt->lchild);   //递归左右子树并返回左右子树高度;btr=RecurOrder(bt->rchild);h=(btl>btr?btl:btr)+1;   //二叉树高度=左右子树高度较高者+1return h;
}

⚫ 二叉链表存储结构,求非空二叉树的宽度:

//算法思想:在一般层次遍历的基础上,定义MaxW记录最大宽度层,定义NowW记录当前层最大宽度;层次遍历进行至:front==last时,判断NowW和MaxW的大小并根据情况,决定是否交换;void MaxW(BiNode *bt)
{BiNode *p;BiNode *Q[Maxsize];int front=-1,rear=-1,NowW=0,MaxW=0;Q[++rear]=bt;while(front<rear){p=Q[++front];NowW++;     //计算当前层的宽度if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){    //front在父结点一层活动,直至遇见最后的结点:last;last=rear;     //rear在左右孩子结点一层活动;last重新定位rear;if(NowW>MaxW){MaxW=NowW;NowW=0;}}}return MaxW;
}

⚫ 二叉链表存储结构,判断给定二叉树是否是完全二叉树:

算法思想:在一般层次遍历的基础上,将二叉树包括空结点在内的所有结点,若遇见空结点,则判断其后是否有非空结点,若有:非完全二叉树;bool IsComplete(BiNode *bt)
{if(bt==NULL) return;   //根空,则:完全二叉树BiNode *p;InitEmpty(Q);EnQueue(Q,bt):while(!IsEmpty(Q)){p=DeQueue(Q);if(p){    //区别:一般层次遍历,此处左右孩子存在的,入队;此处,父结点存在,无论左右孩子是否为空,入队;EnQueue(Q,p->lchild);EnQueue(Q,p->rchild);}else{while(!IsEmpty(Q)){   //结点为空,判断其后结点若不为空,则非完全二叉树;DeQueue(Q,p);if(p) return false;}}}
}

(6) 二叉树-后序遍历专题
⚫ 二叉链表存储结构,对树中每个元素值为 x 的结点,删除以它为根的子树,并释放相应
空间;

算法思想:因为删除元素值为x的结点,所以:要找到元素值为x的结点,所以:层次遍历;因为要删除元素值为x的结点为根点的子树,所以:要先删除它的左右孩子,所以:后序遍历。//结点删除函数:后序递归删除
void Del(BiTree bt)
{if(bt==NULL) return;         //判空,出口Del(bt->lchild);             //递归,删除左右孩子;Del(bt->rchild);                free(bt);                    //一定要释放结点,不然瞎递归;
}//结点查找函数:层次递归查找
void SeaLev(BiNode *bt,int x)
{if(bt==NULL) return;         //判空,出口if(bt->data==x){             //找到x,删除结点,出口;Del(bt); return;}InitQueue(Q);BiNode *p;      //以下内容,纯粹在层次遍历的基础上,递归调用Del函数删除结点Enqueue(Q,bt);       while(!IsEmpty(Q)){Dequeue(Q,p);if(p->lchild){             //在结点p左右孩子不空的情况下,判断其值==x;是,则:删除,置空;不是,则:入队;if(p->lchild->data==x){Del(p->lchild);p->lchild=Null;}else{Enqueue(Q,p->lchild); }if(p->rchild){if(p->rchild->data==x){Del(p->rchild);p->rchild=Null;}else{Enqueue(Q,p->rchild);}}
}

(7) 二叉树-线索二叉树专题
(8) 二叉树真题问题
⚫ 一棵具有 n 个节点的完全二叉树以顺序方式存储在数组中,设计一个算法构造该二叉树
的二叉链式存储结构:【与“前序遍历二叉树”有很大关联】

算法思想:完全二叉树的顺序存储便是完全二叉树层次遍历的结果,
数组从0开始,针对一个父节点a[i]所在i位,其左右孩子分别处于:2*i+1和2*i+2位;
逐个递归的从完全二叉树中读取左右孩子的位置,便可以创建完全二叉树的二叉链式存储结构BiNode * CreateTree(int i,BiNode a[])
{  BiNode *bt;if(i>strlen(a)-1 || a[i]=='0') bt=NULL;else{bt=new BiNode; //bt=(BiNode*)malloc(sizeof(BiNode));bt->data=a[i];bt->lchild=CreateTree(2*i+1,a);  //根结点为i,其左孩子序列2*i+1[层次遍历序列的规律]bt->rchild=CreateTree(2*i+2,a);}return bt;
}

⚫ 树转二叉树:

算法思想:二叉树的“双亲和右孩子”对应树的“兄弟关系”,二叉树的“双亲和左孩子”对应树的“双亲和长子”//树结点结构体定义
typedef struct CSNode{
Elemtype data;
struct CSNode *firstchild,*nextsibing;
}CSNode,*CSTree;//二叉树结构体定义
typedef struct BiTNode{
Elemtype data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;BiTNode * trans(CSNode*p)
{  BiTNode *bt=NULL;if(p){bt=(BiTNode*)malloc(sizeof(BiTNode));bt->data=p->data;bt->lchild=trans(p->firstchild); //二叉树的“双亲和左孩子”对应树的“双亲和长子”bt->rchild=trans(p->nextsibling); //二叉树的“双亲和右孩子”对应树的“兄弟关系”}return bt;
}

⚫ 在二叉树中查找结点值为 x,打印输出其所有祖先(假定该结点 x 仅有一个)

//算法思想:采用递归算法-1.若为空,直接返回false;2.若找到结点值为x的结点,返回true;3.递归遍历根结点左右子树;bool RecurOrder(BiNode *bt,int x)
{if(bt==NULL) return false;    //递归出口1:根结点为空的情况或者左右子树访问至空结点的情况,退出;if(bt->data==x) return true;  //递归出口2:根结点就是x的情况或者左右子树任一个找到x,打印,返回上一层,打印,,,,,循环退出递归;if(RecurOrder(bt->lchild,x) || RecurOrder(bt->rchild,x)){  //找到x之前,遇到的所有结点printf("%d",bt->data);    //输出顺序:从x结点开始打印,直到根结点;return true;}return false;   //如果左右子树没有一个可以找到x结点,返回false;
}InitStack(s);
bool RecurOrder(BiNode *bt,int x)
{if(bt==NULL) return false;    //递归出口1:根结点为空的情况或者左右子树访问至空结点的情况,退出;if(bt->data==x) return true;  //递归出口2:根结点就是x的情况或者左右子树任一个找到x,打印,返回上一层,打印,,,,,循环退出递归;while(RecurOrder(bt->lchild,x) || RecurOrder(bt->rchild,x)){  //找到x之前,遇到的所有结点stack.push(s,bt->data);printf("%d",bt->data);    //输出顺序:从根结点开始打印,直到x结点;return true;}return false;           //如果左右子树没有一个可以找到x结点,返回false;
}  

⚫ 写出树向二叉树转化的算法,并用中序遍历给出转换后的二叉树的结点序列和树的深度

//树转二叉树:树的兄弟关系---二叉树的双亲和右孩子关系;树的双亲和长子关系---二叉树的双亲和左孩子的关系;
步骤:1.兄弟连线;2.保留双亲和长子的连线,删除双亲和其他孩子的连线;3.顺时针旋转,使层次分明;//二叉树的结构体定义
typedef struct BiNode
{char data;struct BiNode *lchild;struct BiNode *rchild;
}BiNode,*BiTree;//树的结构体定义
typedef struct TNode
{char data;struct TNode *firstchild;struct TNode *nextsibling;
}TNode,*TTree;BiNode* Trans(TTree t)
{BiNode* bt=NULL;if(t){bt=(BiNode*)malloc(sizeof(BiNode));bt->data=t->data;bt->lchild=Trans(t->firstchild);bt->rchild=Trans(t->nextsibling);}return bt;
}//中序遍历该二叉树
递归算法:
void InOrder(BiNode *bt)
{BiNode* p=bt;if(p){InOrder(p->lchild);visit(p);InOrder(p->rchild);}
}
非递归算法:【见到“非递归”,必须想到栈;因为递归的本质,就是对栈的应用】
void InOrder(BiTree bt)
{InitStack(s);BiNode *p=bt;while(p || !IsEmpty(s)){if(p){push(s,p);p=p->lchild;}else{pop(s,p);visit(p);p=p->rchild;}}
}//求该二叉树的高度
递归法求二叉树高度:
void BiHigh(BiTree bt)
{int btl=0,btr=0,h=0;if(bt==NULL) return 0;btl=BiHigh(bt->lchild);btr=BiHigh(bt->rchild);h=(btl>btr?btl:btr)+1;return h;
}//非递归求二叉树的高度:层次遍历求二叉树高度
int BiHigh(BiNode *bt)
{BiTree Q[Maxsize];int front=-1,rear=-1;int level=0,last=0;BiNode *p;    Q[++rear]=bt;while(front<rear){p=Q[++front];if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front=last){level++;last=rear;}}return level;
}

⚫ 给定二叉树 T 有 2 个结点 N1 和 N2,我们应该选择树 T 结点的前序,中序和后序,哪两个
序列来判定结点 N1 必定是结点 N2 的祖先,并给出判断方法。详细算法+程序

//在前序序列中,N1为N2的祖先,根据NLR,则:N1一定在N2的前面;
但,我们根据前序遍历,确定N1在N2的前面,不一定确定N1就是在N处,
也可能在L处;所以,我们利用后序遍历LRN,若N1为N2的祖先,则N1一定在N2的后面;
但,我们根据后序序列,确定N1在N2的后面,不一定确定N1就是在N处,也可能在R处;
所以,当我们对前序遍历和后序遍历取交集时,我们便可以确定:N1是N2的祖先;
利用前序遍历和后序遍历,若前序遍历N1在前,后序遍历N1在后,那么:N1作为祖先;

⚫ 二叉链表表示二叉树,其中指针 t 指向根结点,试从根开始,按层次遍历二叉树的算法,
每层的结点按从左到右的次序访问

//算法思路:凡是涉及层次遍历,必用到队列;1.初始化队列;2.根结点入队,在队列不空的情况下,出队一个元素,访问,若其左右孩子不为空,则分别入队;
void LevelOrder(BiTree bt)
{InitQueue(Q);EnQueue(Q,bt);BiNode* p;while(!IsEmpty(Q)){DeQueue(Q,p);visit(p);if(p->lchild) EnQueue(Q,p->lchild);if(p->rchild) EnQueue(Q,p->rchild);}
}

⚫ 根据二叉树的前序序列和中序序列构建二叉树【真题已经重复出现的超高频考点】

算法思想:
(1)根据前序序列确定根结点;
(2)在中序序列中找到根节点位置,确定根结点左右子树的中序序列;
(3)在前序序列中找到左右子树的前序序列;
(4)由左子树的前序序列和中序序列确定左子树;
(5)由右子树的前序序列和中序序列确定右子树;BiNode * PreInCtree(char pre[],char in[],int l1,int r1,int l2,int r2)
{  if(l1>r1) return NULL;  //1.作为递归的出口BiNode *bt=(BiNode*)malloc(sizeof(BiNode));  //2.创建一个左右孩子为空的根节点并赋根节点值bt->lchild=NULL;bt->rchild=NULL;bt->data=pre[l1];    //注意:此处是pre[l1],不是pre[0],因为考虑到递归for(int i=l2;i<=r2;i++)  //3.在in中序序列中,找到根节点所在的位置{ if(in[i]==pre[l1]) break;}//注意:左子树前序和中序确定的左子树,返回给bt->lchild;右子树,同理;bt->lchild=PreInCtree(pre,in,l1+1,l1+i-l2,l2,i-1);//4.根据根节点的位置,确定左子树前序序列中序序列范围,递归处理[注:范围的坐标一定要熟练掌握]bt->rchild=PreInCtree(pre,in,l1+i-l2+1,r1,i+1,r2);//根据根节点的位置,确定右子树前序序列中序序列范围,递归处理return bt;
}

⚫ C 语言定义二叉树的数据结构,并给出先序和中序遍历的算法【真题已经重复出现的超高
频考点】

typedef struct BiNode
{ElemType data;struct BiNode *lchild,*rchild;
}BiNode,*BiTree;//先序序列
void PreOrder(BiTree bt)
{if(!bt){visit(bt);PreOrder(bt->lchild);PreOrder(bt->rchild);}
}//中序序列
void InOrder(BiTree bt)
{if(!bt){InOrder(bt->lchild);visit(bt);InOrder(bt->rchild);}
}

⚫ 计算二叉树的非叶子结点

//算法思想:1.空树;2.左右子树不存在;3.递归调用左右子树;int CountNotLeaf(BiTree bt)
{int btl,btr;if(bt==NULL) return 0;else if((bt->lchild==NULL)&&(bt->rchild==NULL)) return 0;else{btl=CountNotLeaf(bt->lchild);btr=CountNotLeaf(bt->rhcild);}return (btl+btr+1);
}

⚫ 一棵二叉树的繁茂度定义为各层结点数的最大值与树的高度的乘积。试编写一算法,求
二叉树的繁茂度。

//算法思想:树的繁茂度=树的宽度*树的高度//树的宽度&高度-算法思想:采用层次遍历,利用队列对每层结点数进行统计比较,选取最大结点数所在层的结点数;1.定义front,rear,last,maxWidth,level;
int BiWidthHight(BiTree bt)
{BiNode* p;BiNode* Q[Maxsize];int front=-1,rear=-1,last=0,level=0,nowWidth=0,maxWidth=0;Q[++rear]=bt;while(front<rear){p=Q[++front];nowWidth++;if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){level++;last=rear;if(num>maxWidth) maxWidth=nowWidth;nowWidth=0;   //注:对下一轮的宽度进行累加}return (level*maxWidth);
}//算法思想:在一般层次遍历的基础上,定义MaxW记录最大宽度层,定义NowW记录当前层最大宽度;层次遍历进行至:front==last时,判断NowW和MaxW的大小并根据情况,决定是否交换;void MaxW(BiNode *bt)
{BiNode *p;BiNode *Q[Maxsize];int front=-1,rear=-1,NowW=0,MaxW=0;Q[++rear]=bt;while(front<rear){p=Q[++front];NowW++;     //计算当前层的宽度if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){    //front在父结点一层活动,直至遇见最后的结点:last;last=rear;     //rear在左右孩子结点一层活动;last重新定位rear;if(NowW>MaxW){MaxW=NowW;NowW=0;}}}return MaxW;
}//算法思想:在一般层次遍历的基础上,定义level表示当前结点所在层,定义last表示当前结点所在层最右端结点;层次遍历,每次出队元素,就与last比较,若相等,则level++;last移动到下一层的最右端结点;void LevelHigh(BiNode *bt)
{BiNode* Q[Maxsize];int front=-1;rear=-1;BiNode* p;int last=0,level=0;Q[++rear]=bt;while(front<rear){p=Q[++front];if(p->lchild) Q[++rear]=p->lchild;if(p->rchild) Q[++rear]=p->rchild;if(front==last){   //注:front在父结点一层遍历,rear在左右孩子结点一层遍历;当front==last时,rear也刚好抵达左右孩子所在层的最后一个结点,所以才有:last=rear;level++;last=rear;}}return level;}

⚫ 哈夫曼代码-一个综合所有问题的代码题-真题 180【此处真的超级重要,超级重要,超级
完美,对于现在的我来说;】+手动求解哈夫曼【画图】

算法思想:根据n个叶子结点,创建哈夫曼树;一边创建,一边统计,哈哈哈;//模块一:哈夫曼树的结点定义
#define Maxsize 100
typedef struct Huu
{int parent;int lchild;int rchild;int data;
}Huu;Huu Hu[Maxsize];    //生成一个给定结点类型数组//模块二:在未被选取的结点中找到权值最小的一个,返回其编号;
int find_min(int i)
{int tag=-1,min=9999;   //tag记录权值最小的结点的标号;另外,一旦返回-1,说明找不到无双亲的结点,树已建立完毕for(int j=0;j<=n;j++){if(Hu[j].parent==0){     //在还未参与建树的结点中查找if(Hu[j].data<min){min=Hu[j].data;tag=j;}}}return tag;
}//模块三:霍夫曼编码+wpl计算
int HuffmanCoding(int n, char *word)
{//用来保存指向每个哈弗曼编码串的指针(指针的指针)char **HC=(char **)malloc(n*sizeof(char*)); //字符串数组 HC是一个指针,指向另一个指针,那个指针指向一个字符(串)/临时空间,用来保存每次求得的一个哈弗曼编码串char *code=(char*)malloc(30*sizeof(char)); //30根据情况自定义的一个数字code[29]='\0'; //编码结束符,即:字符数组的结束标志//下面求每个字符的哈夫曼编码,同时顺带可以求WPL【WPL也要会手动计算】int i;for(int i=0;i<n;i++){   //从叶子结点走int wpl=0;int current =i,level=0; //level定义本轮结点距离根结点的距离   //current定义当前访问的结点int father=Hu[i].parent.  //定义当前结点的父结点int start=29;(n-1)//初始化为:编码结束符的位置   暂存数组的辅助索引//从叶子结点遍历哈夫曼树直到根结点while(father!=0){if(Hu[father].lchild==current)code[--start]='0';elsecode[--start]='1';current=father;father=Hu[current].parent;}//为第i个字符的编码串分配存储空间HC[i]=(char*)malloc((29-start+1)*sizeof(char));  //+1是为了保存'\0';strcpy(HC[i],code+start); //编码串复制      wpl+=level*Hu[i].data;   //计算WPL公式}return wpl;
//}如果单独考察,这里可以直接作为一个函数封装起来了;//输出各字符的编码串for(i=0;i<n;i++){printf("\n");int j=0;printf("%c编码为:",word[i]);while(HC[i][j]!='\0'){printf("%c",HC[i][j]);j++;}printf("%c",HC[i][j]);}
}//模块三:创建哈夫曼树+结点数统计
int mian()
{int i,min_1,min_2,n,weigh;n=5;char word[5]="ABCDE";//对叶子结点进行初始化,双亲,左右孩子均为0;for(int i=0;i<n;i++){          //对叶子结点进行初始化,其权值设为其序号,双亲左右孩子均设为0Hu[i].parent=0;Hu[i].lchild=0;Hu[i].rchild=0;Hu[i].weight=i;   //权值设为其序号;}i--;  //因为i的实际范围是n-1,当最近上边的循环,最后使得i=n,所以:n-1while(1){//查找左子树min_l=find_min(i);Hu[min_l].parent=i+1;     //本轮最小值结点的parent赋值i+1(i+1此时,作为父结点的序号)//查找右子树min_2=find_min(i);if(min_2==-1){Hu[min_1].parent=0;break;}else{Hu[min2].parent=i+1;}Hu[i+1].lchild=min_1;  //父结点序号:i+1,同时也是父结点的权值;给父结点的左右孩子赋值;Hu[i+1].rchild=min_2;Hu[i+1].parent=0;Hu[i+1].data=Hu[min_1].data+H[min_2].data;i++;  //下一轮循环    统计结点数}printf("结点总数是:%d\n",i+1);
}//模块四:求WPL
int WPL(int n)
{int WPL=0;                for(int i=0;i<n;i++){   //从叶子向根走int level=0;   //记录本轮结点距离根结点的距离current=i;   //定义当前访问的结点father=H[i].parent;   //定义当前访问结点的父结点while(father!=0){   //从叶子结点访问直到根结点current=parent;parent=H[i].parent;level++;}WPL+=level*H[i].weight;  //WPL计算公式}return WPL;
}

SuperKey2- 图
⚫ 邻接矩阵&邻接表&图的创建
⚫ DFS(图的深度优先遍历)
⚫ BFS(图的广度优先遍历)
⚫ 迷宫问题
⚫ 八皇后问题
⚫ 最小生成树-(要求邻接矩阵,就用:Prim 算法;没要求,也用它;)
⚫ 最小生成树-Kruskal 克鲁斯卡尔算法(改进的)
⚫ 最短路径 Dijkstra 算法
⚫ 最短路径 Floyd 算法
⚫ 冲刺模拟卷-各大图的各大算法手动求解
SuperKey3- 查找
⚫ 二叉排序树【代码+手工画】-考的较少【最后掌握】
⚫ 哈希表问题【冲刺预测 D 卷-10 页+2019+2017】重点必须掌握
SuperKey3- 排序
⚫ 二分查找
⚫ 冒泡排序 2017【栈&递归】
⚫ 平均时间&最坏情况&辅助存储&稳定性角度—>各种内部排序方法比较【窍门记】2010+
⚫ 各种内部排序画出排序结果【2012+2017+21.1】
SuperKey4- 单链表
⚫ 单链表查找+插入和删除问题【超级基础】
⚫ 单链表合并问题【超级重点】
⚫ 单链表逆置【高频考点】
⚫ 链表排序+去重
⚫ 链表交集问题【递增有序链表】【新型考点】【若无序,先排序,再交集】灵活一点
⚫ 单链表最大值问题【递归】
⚫ 循环链表【约瑟夫问题】【超级重点】-【数组||链表实现】
⚫ 之前的手写代码,再过一遍
SuperKey5- 串
⚫ 顺序存储-最长字串&位置【高频考点】
⚫ 整数转化字符串【递归实现】【高频考点】+atoi 函数
⚫ KMP 算法-判断 n 是否是 m 的字串【超级重点】
⚫ 字符串逆序【递归】
⚫ 几个字符串处理的经典函数【超级重点&超级基础】
⚫ 最大子对称字符串长度问题(回文数)+字符串单词出现频率统计问题【今年新型问题】
SuperKey6- 队列&栈 栈 【 低频】
⚫ 2 个栈模拟队列
⚫ 循环链队模拟队的队的初始化&出队&入队
⚫ 逆序循环队列所有元素
SuperKey7- 重要 专题&C 语言
⚫ 120 题抽典型问题&举一反三问题解决
⚫ 排列组合问题
⚫ GIS 经典算法问题
⚫ 最大公约&最小公倍
⚫ 重要结构体定义
⚫ 前中后缀表达式转化与计算
⚫ 方程求根

数据结构算法-二叉树相关推荐

  1. python处理mysql数据结构_python环境下使用mysql数据及数据结构和二叉树算法(图)...

    python环境下使用mysql数据及数据结构和二叉树算法(图): 1 python环境下使用mysql 2使用的是 pymysql库 3 开始-->创建connection-->获取cu ...

  2. 数据结构与算法--二叉树第k个大的节点

    二叉树第k个大的节点 二叉树文章列表: 数据结构与算法–面试必问AVL树原理及实现 数据结构与算法–二叉树的深度问题 数据结构与算法–二叉堆(最大堆,最小堆)实现及原理 数据结构与算法–二叉查找树转顺 ...

  3. 数据结构与算法-- 二叉树中和为某一值的路径

    二叉树中和为某一值的路径 题目:输入一颗二叉树和一个整数,打印出二叉树中节点值的和为给定值的所有路径.从树的根节点开始往下一只到叶子节点所经过的节点形成一条路径. 我们用二叉树节点的定义沿用之前文章中 ...

  4. 数据结构与算法-- 二叉树后续遍历序列校验

    二叉树后续遍历序列校验 题目:输入一个整数数组,判断改数组是否是某个二叉搜索树的后续遍历结果,如果是返回true否则false,假设输入数组的任意两个数字不相同. 例如输入{5,7,6,9,11,10 ...

  5. javascript数据结构与算法--二叉树遍历(中序)

    javascript数据结构与算法--二叉树遍历(中序) 中序遍历按照节点上的键值,以升序访问BST上的所有节点 代码如下: /**二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中*** ...

  6. 43. 盘点那些必问的数据结构算法题之二叉树基础

    盘点那些必问的数据结构算法题之二叉树基础 0 概述 1 定义 2 基本操作 1) 创建结点 2) BST 插入结点 3) BST 删除结点 4) BST 查找结点 5)BST 最小值结点和最大值结点 ...

  7. python实现mysql二叉树_python环境下使用mysql数据及数据结构和二叉树算法(图)...

    python环境下使用mysql数据及数据结构和二叉树算法(图): 1 python环境下使用mysql 2使用的是 pymysql库 3 开始-->创建connection-->获取cu ...

  8. 数据结构之二叉树(遍历、建立、深度)

    数据结构之二叉树(遍历.建立.深度) 1.二叉树的深度遍历 二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树的所有结点,使得每个结点被访问一次且仅被访问一次. 对于二叉树的深度遍历,有前序遍历 ...

  9. 【关于封装的那些事】 缺失封装 【关于封装的那些事】 泄露的封装 【关于封装的那些事】 不充分的封装 【图解数据结构】二叉查找树 【图解数据结构】 二叉树遍历...

    [关于封装的那些事] 缺失封装 目录 - 缺失封装 为什么不能缺失封装? 缺失封装潜在的原因 未意识到关注点会不断变化 混合关注点 幼稚的设计决策 示例分析一 示例分析二 总结 缺失封装 没有将实现变 ...

最新文章

  1. Android IJKPlayer缓冲区设置以及播放一段时间出错解决方案
  2. 汽车线束测试软件,Aigtek线束测试仪,汽车线束测试_高精度自动测试_操作简单...
  3. java file pathname_int compareTo(File pathname)
  4. Java程序员:不要因未知而让云成本大涨
  5. 数据同步到redis中时候需要 需要给关联的表增加id 如果是一对多 则增加list存储id 如果是一个 则增加一个字段 ;目的是便于取值...
  6. Mysql中Event的一些测试
  7. iOS category解析
  8. java学习(三)内部类
  9. matlab colorbar 颜色范围,Matlab对数范围colorbar imagesc
  10. 常用的浏览器及其内核
  11. matlab仿真放入直流电源,用Matlab/Simulink软件包建模电容滤波直流电源
  12. layui 模板引擎
  13. 计算机替换的快捷键,Autointo Hotkey Changer电脑快捷键替换工具
  14. android编程拨号界面,在Android4.0中Contacts拨号盘界面剖析(源码)
  15. epub 电子书文件如何使用浏览器打开
  16. FICO凭证错误:BKPFF$PRDCLN800在FI中达到的项目最大编号
  17. 黑客入侵自我保护手册
  18. iOS每日总结博客版:iOS开发历程中了解和学习的文章
  19. 神奇的“TexturePacker”
  20. SQL中的sql%rowcount

热门文章

  1. QorIQ LX2160A安全引擎操作模式
  2. 初识vue——vue的发展历程
  3. 【奥特曼迪迦表情包】
  4. OPENCV计算机视觉图像处理频域傅里叶 DFT 变换低通滤波逆变换IDFT
  5. 玩转iOS开发:iOS 10 新特性《Siri Kit Intents Extension UI》
  6. i9 10900k和i7 9700k的区别大吗
  7. linux打开文件脚本,linux脚本文件实现的功能有哪些剧本用工具打开
  8. 移动开发中的仿真器(Emulator)与模拟器(Simulator)
  9. 美团一面(时间1.10h)
  10. vscode 侧边栏源代码管理不见了