typedef struct BiTNode {
int data;
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
非递归版的后续遍历
算法思想:
先访问左节点,然后右节点,最后根结点。
首先无脑访问做左的节点,中间遇到的非叶子结点全部记录在栈中,当已经访问到最左结点的时候,从栈中取栈顶一个元素,访问其右节点,然后重复一的步骤,然后弹出栈顶一个元素,访问根结点。
上诉过程中,两次访问根结点,第一次是从左子树过来要找到右子树,此时并不对其访问。第二次,从右子树结点过来,此时需要访问根结点了。因此可以设定一个标记位置,记录是从哪个节点过来的,来决定是否需要访问根结点。
void postOrder(BiTree T) {
initStack(S);
BiTree p = T, r = NULL; // r指针用来记录p指针指向的前一个结点,为了判断
while(p || !isEmpty(S)) {
if(p) {
push(S, p);
p = p->lchild;
} else {
getTop(S, p);
if(p->rchild && p->rchild != r) {
p = p->rchild;
} else { // 右子树不存在,或者右子树已经访问过了 直接访问根结点
pop(S, p);
visit(p);
r = p;
p = NULL; // 将p置为NULL是为了 继续向栈中取结点
}
}
}
}
非递归版的前中序遍历
void InOrder(BiTree T) {
initStack(S);
BiTree p = T;
while(p || isEmpty(S)) {
if(p) {
push(S, p);
p = p->lchild;
} else {
pop(S, p);
visit(p);
p = p->rchild;
}
}
}
void preOrder(BiTree T) {
initStack(S);
BiTree p = T;
while(p || isEmpty(S)) {
if(p) {
visit(p);
push(S, p);
p = p->lchild;
} else {
pop(S, p);
p = p->rchild;
}
}
}
给出二叉树的自下而上、从右到左的层次遍历算法。
算法思路:按照层序遍历的顺序,然后依次入栈即可,最后出栈的顺序就是自下而上、从右往左的访问顺序
void InvertLevel(BiTree T) {
BiTree p;
initStack(S);
initQueue(Q);
if(T) {
enqueue(Q, bt);
while(!isEmpty(Q)) { // 当队列不空时
dequeue(Q, p);
push(p);
if(p->lchild) enqueue(Q, p->lchild);
if(p->rchild) enqueue(Q, p->rchild);
}
}
while(!isEmpty(S)) {
pop(S, p);
visit(p);
}
}
如果是自上而下,从右往左。这个就需要去记录每一层的结点个数,对于同一层次的结点 进行全入栈然后出栈 再入队,即可实现同一层次的逆序。
void InvertLevel(BiTree T) {
BiTree p;
initStack(S);
initQueue(Q);
initQueue(ans); // 该队列为最终存的答案队列
if(T) {
enqueue(Q, T);
while(Q) {
int len = getLen(Q); // 计算当前队列的长度
while(len --) {
dequeue(Q, p);
push(S, p);
if(p->lchild) enqueue(Q, p->lchild);
if(p->rchild) enqueue(Q, p->rchild);
}
while(!isEmpty(S)) {
pop(S, p);
enqueue(ans, p);
}
}
}
while(!isEmpty(ans)) { // 访问ans队列的结果即可
dequeue(ans, p);
visit(p);
}
}
非递归算法计算 二叉树的高度
算法思路:宽搜
int getTreeHigh(BiTree T) {
BiTree p;
initQueue(Q);
int high = 0;
if(T) {
enqueue(Q, T);
while(Q) {
int len = getLen(Q); // 计算当前队列的长度
while(len --) {
dequeue(Q, p);
if(p->lchild) enqueue(Q, p->lchild);
if(p->rchild) enqueue(Q, p->rchild);
}
high ++; // 访问完一层 树的高度加1
}
}
return high;
}
还有一种逻辑类似的方法
int Btdepth(BiTree T) {
if(!T) return 0; // 树空 直接返回0
// 定义个静态队列
int hh = -1, rr = -1;
int last = 0, level = 0; // last 指向的是当前层最右结点
BiTree Q[MAXSIZE];
Q[++ rear] = T;
BiTree p;
while(hh < rr) { // 当队列不为空的时候
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;
}
递归版
int getTreeLevel(BiTree T) {
if(!T) return 0;
ldep = getTreeLevel(T->lchild);
rdep = getTreeLevel(T->rchild);
return ldep < rdep ? rdep + 1 : ldep + 1;
}
根据给定的先序和中序遍历序列,然后构建一个二叉链表。
算法思路:每次先找到根结点,然后递归分解成构建左子树和右子树
// 数组下标从1开始 假设有n个结点 所以 l1~r1属于[1,n]
BiTree preInCreate(int A[],int B[],int l1,int r1,int l2,int r2) {
BiTree root = new BiTNode;
root->data = A[l1]; // 先序遍历的顺序第一个元素一定是根结点
int i;
for(i = l2; B[i] != root->data; i ++); // 在中序遍历中找到A中的第一个元素,然后分块 继续递归
llen = i - l2;
rlen = r2 - i;
if(llen) root->lchild = preInCreate(A,B,l1 + 1, l1 + llen,l2,l2 + llen - 1);
else root->lchild = NULL;
if(rlen) root->rchild = preInCreate(A,B,r1 - rlen + 1, r1, r2-rlen + 1,r2);
else root->rchild = NULL;
return root;
}
// 最后类似的给出后续遍历和中序遍历, 只需要调整参数的区间即可
判断一个二叉树是否是完全二叉树
算法思路:完全二叉树 只需要考虑其最后一层,只能是最左边有元素,当然可能该层全满
用一个队列 记录当前层次的所有后继结点,然后遍历后继结点,如果出现NULL,那么后面所有结点都是NULL
但凡出现一个非NULL那么该二叉树一定是非完全二叉树
bool isComplete(BiTree T){
BiTree p;
initQueue(q);
if(!T) return 1; // 空树特判为满二叉树
enqueue(q, T);
while(!isEmpty(q)){
dequeue(q, p);
if(p){
enqueue(q,p->lchild);
enqueue(q,p->rchild);
}else{
while(!isEmpty(q)){
dequeue(q, p);
if(p) return 0;
}
}
}
return 1;
}
计算一个二叉树所有双分支结点个数
int DsonNodes(BiTree T){
if(T == NULL) return 0;
else if(T->lchild != NULL && T->rchild != NULL) return DsonNodes(T->lchild) + DsonNodes(T->rchild) + 1;
else return DsonNodes(T->lchild) + DsonNodes(T->rchild;)
}
交换左右子树
算法思路:使用递归算法实现,先交换T结点的左孩子的左右子树,然后交换T结点右孩子的左右子树,最后交换T结点的左右孩子
void swap(BiTree T){
if(T){
swap(T->lchild);
swap(T->rchild);
auto tmp = T->lchild;
T->lchild = T->rchild;
T->rchild = tmp;
}
}
求先序遍历的第K个结点的值
算法思路:可以定义一个全局变量去记录当前已经访问过的结点个数
int cnt = 0; // 全局变量 统计当前已经访问的结点个数
int preOrder(BiTree T,int k){
BiTree p = T;
initStack(S);
while(p || !isEmpty(S)){
if(p){
cnt ++;
if(cnt == k) return p->data;
push(S, p);
p = p->lchild;
}else{
pop(S, p);
p = p->rchild;
}
}
}
对于树中每个元素值为x的结点,删除以他为根的子树,并释放相应的空间。
算法思路:首先遍历树的所有结点(层次遍历),找到数据域为x的结点,递归删除释放其后代
// 递归删除子树的方法
void delete(BiTree T){
if(T){
delete(T->lchild);
delete(T->rchild);
free(T);
}
}
// 层次遍历删除
void searchX(BiTree T,int x){
initQueue(Q); // 定义一个队列
BiTree p; // 定义一个工作指针
if(T){
if(T->data == x) delete(T), return; // 如果是根结点那么 删除完可以直接退出了
enqueue(Q, T);
while(!isEmpty(Q)){
dequeue(Q, p);
if(p->lchild){
if(p->lchild->data == x){
delete(p->lchild);
p->lchild = NULL;
}else{
enqueue(Q, p->lchild);
}
}
if(p->rchild){
if(p->rchild->data == x){
delete(p->rchild);
p->rchild = NULL;
}else{
enqueue(Q, p->rchild);
}
}
}
}
}
值为x的结点的所有祖先,x不多于一个
算法思路:采用非递归的后续遍历,最后访问根结点,那么当访问到x的时候,栈中所有元素即为该结点的祖先结点
void PostOrder(BiTree T,int x){
initStack(S);
BiTree p = T, r = NULL;
while(p || !isEmpty(S)){
if(p){
push(S, p);
p = p->lchild;
}else{
getTop(S, p);
if(p->rchild && p->rchild != r){
p = p->rchild;
}else{
pop(S, p);
if(p->data == x) break; // 结束循环此时栈中均为x的祖宗结点
r = p;
p = NULL;
}
}
}
while(!isEmpty(S)){ // 输出栈中元素
pop(S, p);
cout << p->data << ' ';
}
}
两结点的最近公共祖先
采用后续遍历分别访问p和q,此时p和q的后续遍历的工作栈中即为该结点的祖宗序列,去匹配倒数第一个相同的元素。
Stack S1[], S2[];
BiTree ancestor(BiTree root, BiTree a, BiTree b){
initStack(S1);
BiTree p = T, r = NULL;
while(p || !isEmpty(S1)){
if(p){
push(S1, p);
p = p->lchild;
}else{
getTop(S1, p);
if(p->rchild && p->rchild != r){
p = p->rchild;
}else{
pop(S1, p);
if(p == a) break; // 结束循环此时栈中均为x的祖宗结点
r = p;
p = NULL;
}
}
}
initStack(S2);
BiTree p = T, r = NULL;
while(p || !isEmpty(S2)){
if(p){
push(S2, p);
p = p->lchild;
}else{
getTop(S2, p);
if(p->rchild && p->rchild != r){
p = p->rchild;
}else{
pop(S2, p);
if(p == b) break; // 结束循环此时栈中均为x的祖宗结点
r = p;
p = NULL;
}
}
}
for(int i = S1.top;i >= 0;i --){
for(int j = S2.top; j >= 0;j --){
if(S1[i] == S2[j]) return S1[i];
}
}
return NULL; // 没有找到相同元素 返回NULL
}
二叉树的宽度,即最大的层次结点的个数
算法思路:层次遍历
int getWidth(BiTree T){
int maxv = -1; // 初始化最大值
BiTree p;
initQueue(Q);
enqueue(Q, T);
while(!isEmpty(Q)){
int size = length(Q); // 计算当前队列Q的长度
maxv = max(maxv, size);
while(size --){
dequeue(Q, p);
if(p->lchild) enqueue(Q, p->lchild);
if(p->rchild) enqueue(Q, p->rchild);
}
}
return maxv;
}
已知一棵满二叉树的先序序列,求其后序序列
void pretToPost(char pre[],int l1, int r1,char post[], int l2, int r2){
int half;
if(r1 >= l1){
post[r2] = pre[l1];
half = (r1 - l1) / 2;
preToPost(pre, l1 + 1,l1 + half, post,l2, l2+half - 1); //
preToPost(pre, l1 + half + 1,r1, post, l2 + half, r2 - 1);
}
}
将二叉树的叶子结点按从左到右的顺序连成一个单链表,表头指针为head,链接时用叶结点的有指针来存放单链表的指针
算法思想:采用中序遍历,遇到结点为孩子,则上一个叶结点指向当前结点
LinkList inOrder(BiTree T){
BiTree pre = NULL;
LinkList head;
if(T){
inOrder(T->lchild);
if(T->lchild == NULL && T->rchild == NULL){
if(pre == NULL){
head = T;
pre = T;
}else{
pre->rchild = T;
pre = T;
}
}
inOrder(T->rchild);
}
return head;
}
判断二叉树的相似
算法思路:同时遍历两个二叉树
int similar(BiTree T1, BiTree T2){
if(T1 == NULL && T2 == NULL) return 1; // 如果他们同时走到为NULL的话,相似
else if (T1 == NULL || T2 == NULL) return 0; // 一个非空一个空 不相似
else return similar(T1->lchild,T2->lchild) && similar(T1->rchild,T2->rchild);
}
中序线索二叉树里查找指定结点p在后序的前驱结点的算法
算法思路:
BiTree InPostPre(BiTree T,BiTree p){
BiTree q;
if(p->rchild == 0) q = p->rchild;
else if(p->lchild == 0) q = p->lchild;
else if(p->lchild == NULL) q == NULL; // 该点为中序序列的第一个结点,无前驱
else{
if(p->ltag == 1 && p->lchild != NULL) p = p->lchild;
if(p->ltag == 0) q = p->lchild;
else q = NULL; // p结点就是第一个后序结点, 所以无前驱
}
}
计算二叉树的带权路径长度(WPL)
int wpl = 0; // 全局变量
int preOrder(BiTree root,int deep){
if(root->lchild == NULL && root->rchild == NULL) wpl += deep * root->data;
if(root->lchild != NULL) preOrder(root->lchild, deep+1);
if(root->lchild != NULL) preOrder(root->rchild, deep+1);
}
输出二叉树的中序序列,每一颗子树都用括号括起来
void ToExe(BiTree *root, int deep){
if(root == NULL) return;
else if(root->lchild == NULL && root->rchild == NULL) cout << root->data;
else{
if(deep > 1) cout << "(";
ToExe(root->lchild, deep+1);
cout << root->data;
ToExe(root->rchild, deep+1);
if(deep > 1) cout << ")";
}
}
判断一个用顺序存储的二叉树是否为二叉搜索树
算法思路:若一个树为二叉搜索树,则中序遍历得到的的序列一定是递增的,由此可以判断
int a[], cnt; // 用来存储访问到的序列 cnt为其元素个数
int inOrder(BiTree T){
if(T){
inOrder(T->lchild);
a[cnt++] = T->data;
inOrder(T->rchild);
}
}
bool judge(){
for(int i = 1;i < cnt;i ++){
if(a[i - 1] > a[i]) return false;
}
return true;
}