数据结构考研-树-算法题

以下便是所总结的关于树的各种算法, 算法将以函数的形式给出, 关于树的初始化等操作已经完成, 程序可以直接运行.关于树的算法题的精髓在于递归.

#include<bits/stdc++.h>

using namespace std;

// 定义二叉树结点的数据类型
typedef int ElemType;
typedef struct binode {
    ElemType data;
    struct binode *lchild, *rchild;
} BiNode, *BiTree;


// 算法1:  递归计算叶子结点数目的函数
int countLeafNodes(BiTree root) {
    if (root == NULL) // 如果根节点为空,返回0
        return 0;
    else if (root->lchild == NULL && root->rchild == NULL){ // 如果是叶子节点,返回1
        //cout<<root->data<<endl;
        return 1+countLeafNodes(root->lchild) + countLeafNodes(root->rchild);
    }
    else // 递归计算左右子树的叶子节点数目,并相加
        return countLeafNodes(root->lchild) + countLeafNodes(root->rchild);
        //每一个分支节点最多只有两颗子树,从最后一层 countLeafNodes(root->lchild) + countLeafNodes(root->rchild)
        //相加返回上一层的 countLeafNodes(root->lchild)
}


//算法2:   递归计算结点总数的函数
int countNodes(BiTree root) {
    if (root == NULL)
        return 0;
    else return 1 + countNodes(root->lchild) + countNodes(root->rchild);
    //每每递归到最后一层比如到节点4,此时root指向4节点, countNodes(root->lchild) + countNodes(root->rchild),
    //分别进入这两个递归最后都return 0。
    // 1 + countNodes(root->lchild) + countNodes(root->rchild);可以理解为,一个双亲的节点加上左右子树的节点
}


//算法3:   递归计算单分支结点总数
int countdfznode(BiTree root) {
    if (root == NULL)
        return 0;
    if ((root->lchild == NULL && root->rchild != NULL) || (root->rchild == NULL && root->lchild != NULL))
       return 1 + countdfznode(root->lchild) + countdfznode(root->rchild); //总体优化后的思路还是双亲加左加右
    else
       return  countdfznode(root->lchild) + countdfznode(root->rchild);
}


//算法4:   计算最小结点
void minnode(BiTree root,int &min){
    if(root==NULL)
      return ;
    if(root->data<min)
      min=root->data;
    minnode(root->lchild,min);//本质上是先序遍历
    minnode(root->rchild,min);
    return ;
}


//算法5:   计算k层上结点的个数
//层次遍历必须使用辅助队列
// 队列节点
typedef struct qnode {
    BiTree data;
    struct qnode *next;
} QNode;
// 队列结构
typedef struct {
    QNode *front;
    QNode *rear;
} LinkQueue;
// 初始化队列
void initQueue(LinkQueue &q) {
     q.front = q.rear =(QNode*)malloc(sizeof(QNode));
     q.front->next = NULL;
}
// 入队
void enQueue(LinkQueue &q, BiTree data){ //将树的一个结点看成队列的一个元素,见队列的存储结构的定义
     QNode *newNode = new QNode;
     newNode->data = data;
     newNode->next = NULL;
     q.rear->next = newNode;
     q.rear = newNode;
}
// 出队
BiTree deQueue(LinkQueue &q) {
     if (q.front == q.rear) {
        return NULL;
     }
     QNode *p = q.front->next;
     BiTree data = p->data;
     q.front->next = p->next;
     if (q.rear == p) {
        q.rear = q.front;
     }
     delete p;
     return data;
}
bool isEmpty(LinkQueue q) {
     return q.front == q.rear;
}


//算法6:   层次遍历
void levelOrder(BiTree root) {
    LinkQueue q;
    initQueue(q);
    enQueue(q, root);
    while (!isEmpty(q)) {
              BiNode *node =deQueue(q);
              cout<<node->data<<" ";
              if (node->lchild!=NULL) {
                 enQueue(q, node->lchild);
              }
              if (node->rchild!=NULL) {
                 enQueue(q, node->rchild);
              }
              
          }
   
}


//算法7:  第k层树有多少结点
int nodekcount(BiTree root,int k,int h) {//k是求第k层的节点
    int num1=0,num2,num=0;
    if(root!=NULL){
      //h的初始值为1
      if(h==k){
         return 1+nodekcount(root->lchild,k,h+1)+nodekcount(root->rchild,k,h+1);
      }
      else
         return nodekcount(root->lchild,k,h+1)+nodekcount(root->rchild,k,h+1);
      //num1=levelkcount(root->lchild,k,h+1);
      //num2=levelkcount(root->rchild,k,h+1);
      //num+=num1+num2;//假设计算的是最后一层的结点数,上一层的2先执行r到4此时h==k,所以num=1、
      //原理类似,会一直到最后一层然后将求和不断递归相加
      //return num;
      }
    return 0;
    }

int nodeklesf(BiTree root,int k){
    return nodekcount(root,k,1);//从深度为1开始
}


//算法8:  第k层树有多少叶子结点
int levelkcount(BiTree root,int k,int h) {//k是求第k层的节点
    if(root!=NULL){
      //h的初始值为1
      if(h==k){
         if(root->lchild==NULL&&root->rchild==NULL)
         return 1+levelkcount(root->lchild,k,h+1)+levelkcount(root->rchild,k,h+1);
      }
      else
         return levelkcount(root->lchild,k,h+1)+levelkcount(root->rchild,k,h+1);
      //num1=levelkcount(root->lchild,k,h+1);
      //num2=levelkcount(root->rchild,k,h+1);
      //num+=num1+num2;//假设计算的是最后一层的结点数,上一层的2先执行r到4此时h==k,所以num=1、
      //原理类似,会一直到最后一层然后将求和不断递归相加
      //return num;
      }
    return 0;
    }

int levelklesf(BiTree root,int k){
    return levelkcount(root,k,1);//从深度为1开始
}


//算法9:   求树的深度
void hightree(BiTree root,int &high,int h) {//
    if(root==NULL)
      return ;
    if(h>high)
      high=h;
    if(root->lchild!=NULL)
    hightree(root->lchild,high,h+1);
    if(root->rchild!=NULL)
    hightree(root->rchild,high,h+1);
    return ;
}


//算法10:   判断x结点与y结点是否互为兄弟
bool isbrother(BiTree root, int x, int y) {
     if (root == NULL) {
        return false;
     }
     if (root->lchild != NULL && root->rchild != NULL) {
    //如果仅仅用if(root->lchild->data==x&&root->rchild->data==y||root->lchild->data==y&&root->rchild->data==x)
    //来判断到没到底,是不是对的,因为到底后,lchild本身就是空了,你还要访问人家的data,就会产生异常!!!
        if ((root->lchild->data == x && root->rchild->data == y) || (root->lchild->data == y && root->rchild->data == x)) {
            return true;
        }
     }
     bool left = isbrother(root->lchild, x, y);
     bool right = isbrother(root->rchild, x, y);
     return left || right;
}


//算法11:   求结点x的子孙节点
void coutsun(BiTree root,int x) {
     if(root==NULL)
       return ;
     if(root->data!=x)
       cout<<root->data<<" ";
     coutsun(root->lchild,x);
     coutsun(root->rchild,x);
     return ;
}
void findsun(BiTree root,int x) {
     if(root==NULL)
       return ;
     if(root->data==x) {
       coutsun(root,x);//为什么要单启用一个函数,因为还用现在的函数可能会一直遍历,不属于该子树的也遍历了
       return ;
     }
     findsun(root->lchild,x);
     findsun(root->rchild,x);
     return ;
}


//算法12:   将二叉树的左右子树进行交换
BiTree swap(BiTree root) {
     BiNode *t,*t1,*t2;
     if(root==NULL) {
       t=NULL;
     }
     else {
       t=(BiNode*)malloc(sizeof(BiNode));
       t->data=root->data;
       t1=swap(root->lchild);
       t2=swap(root->rchild);
       t->lchild=t2;
       t->rchild=t1;//一样是递归到最底端,每个子树从下往上进行交换
    }
    return t;
}


//算法13:   判断左右子树是否同构
bool symm(BiTree root1,BiTree root2) {
    if(root1==NULL&&root2==NULL)
      return true;
    else if((root1==NULL&&root2!=NULL)||(root1!=NULL&&root2==NULL))
      return false;
    else
      return symm(root1->lchild,root2->lchild)&symm(root1->rchild,root2->rchild);
}
bool symmtree(BiTree root) {
    if(root==NULL)
      return true;
    else
      return (root->lchild,root->rchild);
}


//算法14:   判断二叉树是否为完全二叉树
//借助层次遍历的思想
//1、没有左孩子就一定没有右孩子
//2、缺左孩子或者右孩子,则后继一定无孩子
bool perfecttree(BiTree root) {
    LinkQueue q;
    initQueue(q);
    bool cm=true;//cm为真表示为完全二叉树
    bool bj=true;//bj为真,表示目前所有结点均有左孩子还有右孩子
    enQueue(q, root);
    while (!isEmpty(q)) {
          BiNode *node =deQueue(q);
          if(node->lchild==NULL) {
            bj=false;
            if(node->rchild!=NULL){
              cm=false;
              return cm;
            }
          }
          else {//有左孩子
            if(!bj)//前面已经出现,要不缺右子树要不缺左右子树都没有的,后继还有孩子
              cm=false;
              enQueue(q, node->lchild);
            if(node->rchild==NULL)
              bj=false;
            else
              enQueue(q, node->rchild);
          }
    }
    return cm;
}


//算法15:   将二叉链表转化为顺序存储
int sum=1;
void ctree(BiTree root,int a[],int i) {
    if(root!=NULL) {
      a[i]=root->data;
      ctree(root->lchild,a,2*i);
      ctree(root->rchild,a,2*i+1);
    }
    else
      a[i]=-1;//如果该结点为空的话,执行完该次函数就要返回递归了
    sum++;
}
// 定义链栈结点
typedef struct StackNode {
    BiTree data;
    struct StackNode* next;
} StackNode, * LinkStack;

// 初始化链栈
void initStack(LinkStack &S) {
    S =(StackNode *)malloc(sizeof(StackNode));
    S->next=NULL;
    S->data=NULL;
}


//算法16:    判断链栈是否为空
bool isEmpty(LinkStack S) {
     if(S->next==NULL)
       return true;
    else
       return false;
}

// 入栈
void push(LinkStack &S, BiTree data) {
    StackNode* newNode =(StackNode *)malloc(sizeof(StackNode));
    newNode->data = data;
    newNode->next = S->next;//带头指针的链表
    S->next=newNode;
}

// 出栈
BiTree pop(LinkStack &S) {
    if (isEmpty(S)) {
        cout << "Stack Underflow" << endl;
        return NULL;
    } else {
        BiTree data;
        StackNode* temp = S->next;
        S->next = temp->next;
        data = temp->data;
        delete temp;
        return data;
    }
}


//算法17:   非递归实现先序遍历
void preOrderTraversal(BiTree root) {
    if (root == NULL) return;

    LinkStack nodeStack;
    initStack(nodeStack);
    push(nodeStack, root);

    while (!isEmpty(nodeStack)) {
        BiTree node = pop(nodeStack);
        cout << node->data << " ";

        // 先将右子树压入栈中,保证左子树先于右子树访问
        if (node->rchild != NULL) {
            push(nodeStack, node->rchild);
        }

        // 再将左子树压入栈中
        if (node->lchild != NULL) {
            push(nodeStack, node->lchild);
        }
    }
}


//算法18:    后序遍历
void postOrderTraversal(BiTree root) {
    if (root == NULL) return;

    LinkStack stack1, stack2;
    initStack(stack1);
    initStack(stack2);

    push(stack1, root);

    while (!isEmpty(stack1)) {
        BiTree node = pop(stack1);
        push(stack2, node);

        if (node->lchild != NULL) {
            push(stack1, node->lchild);
        }
        if (node->rchild != NULL) {
            push(stack1, node->rchild);
        }
    }

    while (!isEmpty(stack2)) {
        BiTree node = pop(stack2);
        cout << node->data << " ";
    }
}


//算法19:   先序序列第k个位置结点的值
int p=0;
void forworddatak(BiTree root,int k) {
    if(root==NULL)
      return ;
    p++;
    if(p==k) {
      cout<<root->data;
      return ;
    }
    forworddatak(root->lchild,k);
    forworddatak(root->rchild,k);
    return ;
}


//算法20:   中序遍历递归方法
void midorder(BiTree root) {
    if(root==NULL)
      return ;
    midorder(root->lchild);
      cout<<root->data<<" ";
    midorder(root->rchild);
    return ;
}//!!!!后序遍历亦是如此,只要更改操作的位置就可以,是在两个递归的中间还是上面还是下面


//算法21:    查找最近公共祖先
BiTree lowestCommonAncestor(BiTree root, BiTree p, BiTree q) {
    if (root == NULL || root == p || root == q) return root;
    BiTree left = lowestCommonAncestor(root->lchild, p, q);
    BiTree right = lowestCommonAncestor(root->rchild, p, q);
    //典型的后序遍历,将操作的位置放在两个递归的下面
    if (left != NULL && right != NULL) return root;
    if (left != NULL) return left;
    if (right != NULL) return right;
    return NULL;
}

// 初始化二叉树
void initBitree(BiTree &root) {
    root = (BiTree)malloc(sizeof(BiNode));
    root->data = 1;
    root->lchild = (BiTree)malloc(sizeof(BiNode));
    root->lchild->data = 2;
    root->lchild->lchild = (BiTree)malloc(sizeof(BiNode));
    root->lchild->lchild->data = 4;
    root->lchild->lchild->lchild = NULL;
    root->lchild->lchild->rchild = NULL;
    root->lchild->rchild = (BiTree)malloc(sizeof(BiNode));
    root->lchild->rchild->data = 5;
    root->rchild = (BiTree)malloc(sizeof(BiNode));
    root->rchild->data = 3;
    root->rchild->lchild = (BiTree)malloc(sizeof(BiNode));
    root->rchild->rchild = (BiTree)malloc(sizeof(BiNode));
    root->rchild->lchild->data = 6;
    root->rchild->rchild->data = 7;
    root->rchild->lchild->lchild = NULL;
    root->rchild->lchild->rchild = NULL;
    root->rchild->rchild->lchild = NULL;
    root->rchild->rchild->rchild = NULL;
}
//孩子兄弟表示法,不一定是二叉树,度为3,为4都有可能
typedef struct CSNode{
    ElemType data;
    struct CSNode *firstchild ,*nextsibling;//该节点的第一个孩子与该节点的下一个兄弟
}CSNode,*CSTree;

//双亲表示法
#define MAX_TREE_SIZE 100
typedef struct {
    ElemType data;
    int parent;
}PTNode;
typedef struct {
    PTNode nodes[MAX_TREE_SIZE];
    int n;
}PTree;
// 初始化孩子兄弟表示法的树
void initCSTree(CSTree &root_1, ElemType data) {
    root_1 = new CSNode;
    root_1->data = data;
    root_1->firstchild = NULL;
    root_1->nextsibling = NULL;
}

// 创建示例的树
void createExampleTree(CSTree &root_1) {
    CSTree a, b, c, d, e, f;
    initCSTree(a, 'a');
    initCSTree(b, 'b');
    initCSTree(c, 'c');
    initCSTree(d, 'd');
    initCSTree(e, 'e');
    initCSTree(f, 'f');

    a->firstchild = b;
    b->nextsibling = c;
    b->firstchild = d;
    d->nextsibling = e;
    e->nextsibling = f;
    root_1 = a;
}


//算法22:   孩子兄弟表示法统计叶子数量
int countLeaves(CSTree root_1) {
    if (root_1 == NULL) return 0;
    if (root_1->firstchild == NULL) return 1;
    int count = 0;
    CSTree p = root_1->firstchild;//依据为如果该节点的第一个左孩子域为空,那么就为叶子结点
    while (p != NULL) {
        count += countLeaves(p);
        p = p->nextsibling;
    }
    return count;
}


//算法23:   孩子兄弟表示法计算树的度
int maxDegree(CSTree root) {
    if (root == NULL) return 0;
    int maxChildDegree = 0;
    int currentChildDegree = 0;//当前节点的度
    CSTree p = root->firstchild;
    while (p != NULL) {
        currentChildDegree++;
        int tempDegree = maxDegree(p);
        maxChildDegree = max(maxChildDegree, tempDegree);
        p = p->nextsibling;
    }
    return max(maxChildDegree, currentChildDegree);
}


//算法24:   孩子兄弟表示法计算树的深度
int depth(CSTree root) {
    if (root == NULL) return 0;
    int maxChildDepth = 0;
    CSTree p = root->firstchild;
    while (p != NULL) {
        int currentChildDepth = depth(p);//currentChildDepth只有进入到下一层才会+
        maxChildDepth = max(maxChildDepth, currentChildDepth);
        p = p->nextsibling;
    }
    return maxChildDepth + 1;
}

// 初始化双亲表示法树
void initPTree(PTree &tree) {
    tree.n = 0;
}

// 插入双亲表示法树节点
void insertPTreeNode(PTree &tree, ElemType data, int parent) {
    tree.nodes[tree.n].data = data;
    tree.nodes[tree.n].parent = parent;
    tree.n++;
}

// 创建示例的双亲表示法树
void createExamplePTree(PTree &tree) {
    initPTree(tree);
    insertPTreeNode(tree, 'a', -1);
    insertPTreeNode(tree, 'b', 0);
    insertPTreeNode(tree, 'c', 0);
    insertPTreeNode(tree, 'd', 1);
    insertPTreeNode(tree, 'e', 1);
    insertPTreeNode(tree, 'f', 1);
}
//双亲表示法计算树的深度
int depthPTree(PTree tree, int index) {
    int maxDepth = 0;
    for (int i = 0; i < tree.n; i++) {
        if (tree.nodes[i].parent == index) {
            int currentDepth = depthPTree(tree, i);
            maxDepth = max(maxDepth, currentDepth);
        }
    }
    return maxDepth + 1;
}

// 测试示例
int main() {
    BiTree root;
    initBitree(root);
    cout<<"********关于二叉树的各种算法结果如下********"<<endl;
    cout<<"叶子结点总数为: "<<countLeafNodes(root)<<endl;
    cout<<"节点总数为: "<<countNodes(root)<<endl;
    cout<<"单分支节点总数为: "<<countdfznode(root)<<endl;
    int min=root->data;minnode(root,min);cout<<"最小节点为: "<<min<<endl;
    cout<<"第2层结点总数为:"<<nodeklesf(root,2)<<endl;
    cout<<"第3层结点包含的叶子结点总数为:"<<levelklesf(root,3)<<endl;
    cout<<"层次遍历结果为:";levelOrder(root);cout <<endl;
    int high=0;hightree(root,high,1);cout<<"树的深度为:"<<high<<endl;
    cout<<"6与7是兄弟节点的判断为:"<<isbrother(root,6,7)<<endl;
    cout<<"结点3的子孙节点为:";findsun(root,3);cout<<endl;
    cout<<"二叉树进行左右子树交换后的层次遍历结果为:";BiTree root1=swap(root);levelOrder(root1);cout<<endl;
    cout<<"二叉树左右子树是否同构的判断为:"<<symmtree(root)<<endl;
    cout<<"二叉树是否为完全二叉树的判断为:"<<perfecttree(root)<<endl;
    int a[100];cout<<"转化为顺序存储后为:";ctree(root,a,1);for(int j=1;j<sum;j++) cout<<a[j]<<" ";cout<<endl;
    cout<<"使用栈对二叉树非递归先序遍历结果为:";preOrderTraversal(root);cout<<endl;
    cout<<"使用栈对二叉树非递归后序遍历结果为:";postOrderTraversal(root);cout<<endl;
    cout<<"先序序列第4个位置的结点的值为:";forworddatak(root,4);cout<<endl;
    cout<<"递归中序遍历结果为:";midorder(root);cout<<endl;
    cout<<"查找4和6的最近公共祖先为:";BiNode *ancestor=lowestCommonAncestor(root, root->lchild->lchild, root->rchild->lchild);cout<<ancestor->data<<endl;
    CSTree root_1;
    createExampleTree(root_1);
    cout<<"孩子-兄弟表示法统计叶子结点总数为:"<<countLeaves(root_1)<<endl;
    cout<<"孩子兄弟表示法计算树的度为:"<<maxDegree(root_1)<<endl;
    cout<<"孩子兄弟表示法计算树的深度为:"<<depth(root_1)<<endl;
    PTree tree;
    createExamplePTree(tree);
    cout<<"双亲表示法计算树的深度为:"<<depthPTree(tree,0)<<endl;
    return 0;
}

 运行结果如下所示, 以便于对照学习.

*********关于二叉树的各种算法结果如下********
叶子结点总数为: 4
节点总数为: 7
单分支节点总数为: 0
最小节点为: 1
第2层结点总数为:2
第3层结点包含的叶子结点总数为:4
层次遍历结果为:1 2 3 4 5 6 7
树的深度为:3
6与7是兄弟节点的判断为:1
结点3的子孙节点为:6 7
二叉树进行左右子树交换后的层次遍历结果为:1 3 2 7 6 5 4
二叉树左右子树是否同构的判断为:1
二叉树是否为完全二叉树的判断为:1
转化为顺序存储后为:1 2 3 4 5 6 7 -1 -1 -1 -1 -1 -1 -1 -1
使用栈对二叉树非递归先序遍历结果为:1 2 4 5 3 6 7
使用栈对二叉树非递归后序遍历结果为:4 5 2 6 7 3 1
先序序列第4个位置的结点的值为:5
递归中序遍历结果为:4 2 5 1 6 3 7
查找4和6的最近公共祖先为:1
孩子-兄弟表示法统计叶子结点总数为:4
孩子兄弟表示法计算树的度为:3
孩子兄弟表示法计算树的深度为:3
双亲表示法计算树的深度为:3

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别出现在黎明的梦里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值