从PAT甲级1064题说开去

来源:互联网 发布:php 常量定义 使用 编辑:程序博客网 时间:2024/06/16 05:05

原题链接:https://www.patest.cn/contests/pat-a-practise/1064

这题的意思是,给定一系列树,要用它构造一棵完全二叉树,这棵完全二叉树满足二叉查找树的性质。
在二叉树的遍历中提到了完全二叉树的概念,它是一棵只有最后一层不满,其他所有层都填满的树,最后一层从左向右依次填满。在二叉树的恢复中提到了二叉查找树的性质,它的左子树上的节点均小于它,右子树的节点都大于等于它,左子树右子树都是二叉查找树。

这样这题的思路就很清晰了:既然这棵树是完全二叉树,那这棵树的形状是固定下来的,再把排序好的数字往里填就可以了。

我利用了二叉树在每层上节点个数的性质,迭代地找到每一棵(子)树的根节点,建立完二叉树以后再层序遍历。

#include<iostream>#include<algorithm>#include<queue>using namespace std;int listof2[11] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 };struct tree_node{    int data = 0;    tree_node* left_child = NULL;    tree_node* right_child = NULL;};void find_insert_node(tree_node* &this_node, int node_num, int *data_formal){    if (node_num == 0) return;    if (node_num == 1)    {        this_node = new tree_node;        this_node->data = *data_formal;        return;    }    if (node_num == 2)    {        this_node = new tree_node;        this_node->data = *(data_formal + 1);        this_node->left_child = new tree_node;        this_node->left_child->data = *data_formal;        return;    }    if (node_num == 3)    {        this_node = new tree_node;        this_node->data = *(data_formal + 1);        this_node->left_child = new tree_node;        this_node->left_child->data = *data_formal;        this_node->right_child = new tree_node;        this_node->right_child->data = *(data_formal + 2);        return;    }    int level;    for (level = 0; level < 11; ++level)    {        if (listof2[level]-1 >= node_num) break;    }    if (listof2[level] * 3 / 4 - 1 >= node_num)    {        this_node = new tree_node;        this_node->data = *(data_formal + node_num - listof2[level] / 4);        find_insert_node(this_node->left_child, node_num - listof2[level] / 4, data_formal);        find_insert_node(this_node->right_child, listof2[level] / 4 - 1, data_formal + node_num - listof2[level] / 4 + 1);    }    else    {        this_node = new tree_node;        this_node->data = *(data_formal + listof2[level] / 2 - 1);        find_insert_node(this_node->left_child, listof2[level] / 2 - 1, data_formal);        find_insert_node(this_node->right_child, node_num - listof2[level] / 2, data_formal + listof2[level] / 2);    }}queue<int> data_save;queue<tree_node*> temp_node;void level_order(){    if (temp_node.empty()) return;    tree_node* this_node = temp_node.front();    data_save.push(this_node->data);    temp_node.pop();    if (this_node->left_child) temp_node.push(this_node->left_child);    if (this_node->right_child) temp_node.push(this_node->right_child);    level_order();}int main(){    int num = 10; cin >> num;    int data_formal[1000] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };    for (int i = 0; i < num; ++i) cin >> data_formal[i];    sort(data_formal, data_formal + num);    tree_node* root_node = NULL;    find_insert_node(root_node, num, data_formal);    temp_node.push(root_node);    level_order();    for (int i = 0; i < num; ++i)    {        cout << data_save.front();        data_save.pop();        if (i != num - 1) cout << " ";    }    getchar(); getchar();    return 0;}

这个代码可以AC,但是稍有不慎就会出错,很麻烦。
这个大神的解法很美,分析以后发现写出这样的代码需要深厚的数学和编程功底。

    #include <cstdio>      #include <cstdlib>      const int maxx = 1005;      int node[maxx];      int tree[maxx];      int pos,n;      int cmp(const void *a,const void *b){          int *pa = (int *)a;          int *pb = (int *)b;          return *pa-*pb;      }  //用于排序    void build(int root){          if(root>n)return;          int lson = root<<1,rson = (root<<1)+1;          build(lson);          tree[root] = node[pos++];          build(rson);      }      void print(int *a,int n){          int i;          for(i=0;i<n;++i){              printf("%d ",a[i]);          }          printf("\n");      }  //调试代码    int main()      {          int i;          scanf("%d",&n);          for(i=0;i<n;++i){              scanf("%d",&node[i]);          }          qsort(node,n,sizeof(int),cmp);      //  print(node,n);          pos = 0;          build(1);          printf("%d",tree[1]);          for(i=2;i<=n;++i){              printf(" %d",tree[i]);          }          printf("\n");          return 0;      }  

仔细分析代码,事实上他的代码将构建二叉树和层序排列二叉树在短短6行内完成了

    void build(int root){  //第一次调用时:root=1,pos指向已经升序排列的数组的第一个元素        if(root>n)return;          int lson = root<<1,rson = (root<<1)+1;  //用左移乘以2        build(lson);          tree[root] = node[pos++];          build(rson);      }  

这篇文章提到了完全二叉树的五个基本性质,其中之一是:
如果有一颗有n个节点的完全二叉树的节点按层次序编号,对任一层的节点i(1<=i<=n)有
1)如果i=1,则节点是二叉树的根,无双亲,如果i>1,则其双亲节点为[i/2],向下取整
2)如果2i>n那么节点i没有左孩子,否则其左孩子为2i
3)如果2i+1>n那么节点没有右孩子,否则右孩子为2i+1

通过这个性质,我们可以定位当前处理的节点在数组中应该储存的位置。
按照中序二叉树的遍历顺序,它会从小到大依次遍历二叉查找树的节点。
知道了这两个信息,我们就可以往数组里一个一个填。

即使看懂了他的代码,我大概也无法短时间内写出这么美的代码吧。

0 0
原创粉丝点击