九度OJ 1113 二叉树(完全二叉树)

原题地址:

http://ac.jobdu.com/problem.php?pid=1113
(由于原始版面的排版实在有些不好看,所以就不贴过来啦。CSDN旧版的编辑器适应不了,这次尝试用markdown写)

解题思路

题意:给定一棵共有节点数为n的完全二叉树,对它每一层的节点从左到右编号,第一层为1,第二层为2、3,第三层为4、5、6、7……然后输入一个编号m,求以m为根的子树下共有多少节点。
完全二叉树

  • 通过观察该树状图和完全二叉树的性质,容易得到一下结论:如果序号为m的节点存在子树,那么它左子树根节点的序号为2m,右子树根节点的序号为2m+1
  • 以此类推,该节点下面第k层最左节点(若存在)的序号为2k*m,最右节点的序号由该层是否满决定,如果该层是满的,那么最右节点的序号为2k*m+2k,否则和n的大小有关。
  • 因此,根据这个规律就不必构造一棵完全二叉树,直接对序号空间做处理。

注意点

第一次提交时出现Time Limit Exceed超时错误,原因是while循环中用了以下的for循环:

for (i = m*iter; i<=n && i <= m*iter+(iter-1); ++i) //某一层遍历
    cnt++;

即在该层满的情况下依然遍历,效率极低,必须对这种情况剪枝。(当时太愚蠢了,只需要一个if就能解决,何必要用for呢?)

AC代码:

#include <iostream>
using namespace std;

int main() //完全二叉树问题可以用数组、树状图的规律解决,不必构造节点
{
    int m,n;
    while (cin >> m >> n)
    {
        if (!m && !n) break;
        int iter = 1, cnt = 0; //iter记录m子树的第iter层
        while (m*iter <= n) //存在该层最左的节点
        {
            int i;
            int fullNum = m*iter+(iter-1);
            if (n >= fullNum) //该层全部都存在,直接累加,否则超时
            {
                cnt += iter;
            }
            else //该层不全
            {
                cnt += n-m*iter+1;
            }
            iter <<= 1;
        }
        cout << cnt << endl;
    }
    return 0;
}

内存占用: 1520Kb 耗时:0ms
算法复杂度:O(logn)

posted @ 2017-03-26 20:49  Lecholin  阅读(564)  评论(0编辑  收藏  举报