leetcode#1104 二叉树寻路

目录

题目描述:

“     在一棵无限的二叉树上,每个节点都有两个子节点,树中的节点 逐行 依次按 “之” 字形进行标记。如下图所示:
    在奇数行(即,第一行、第三行、第五行……)中,按从左到右的顺序进行标记;

    而偶数行(即,第二行、第四行、第六行……)中,按从右到左的顺序进行标记。

    给你树上某一个节点的标号 label,请你返回从根节点到该标号为 label 节点的路径,该路径是由途经的节点标号所组成的。 “

示例:

示例 1:
​ 输入:label = 14
​ 输出:[1,3,4,14]
​示例 2:

​ 输入:label = 26
​ 输出:[1,2,6,10,26]

提示:

​ 1 <= label <= 10^6
来源:力扣(LeetCode)
​链接:https://leetcode-cn.com/problems/path-in-zigzag-labelled-binary-tree



题解性能:





思路:

看到题目的一刻,想到建立完全树,只不过这个建树的顺序比较奇特。

1)种树(未用)

    起初中规中矩,希望可以通过建两颗树来解决问题,于是想到两种种树的方法:

第一种: 生成的时候,赋值按照规矩来,也就是按照数字顺序建类似一条链表的线性结构,然后利用数学规律找到某个endNum 数字的父节点-》直至root

第二种: 直接前序建完全树,直到节点数目达到endNum停止(最终因为难以使父亲不同的节点横线连接未采用)

​ 但还是写了前序建立完全树代码:

int locCol(int label)  //按照完全树的定位某个在第几行
{
   
    int cols=1;
    int sum=1;
    int base=2;
    while(sum < label)
    {
   
        cols ++;
        sum += base;
        base *= 2;
    }

    return cols;
}

void preOrderCreate(treeNode &tr,int endNum) //按照顺序前向建树
{
   
    data ++;
    if(data == endNum)
    {
   
        cout<<"end"<<endl;
        tr = new Node;
        tr->val = data;
        tr->left=NULL;
        tr->right=NULL;
        tr->next=NULL;
        return; //终点限制
    }
    else if(depth == locCol(endNum))
    {
   
        cout<<"end col"<<endl;
        tr = new Node;
        tr->val = data;
        tr->left=NULL;
        tr->right=NULL;
        return;  //层数限制
    }
    else
    {
   

        tr = new Node;
        tr->val = data;

        depth++;
        preOrderCreate(tr->left,endNum);
 
        if(data == endNum) return;
 
        preOrderCreate(tr->right,endNum);
        depth--;
        //回到父节点,此时的data是父节点的data
        if(tr->left!=NULL && tr->right!=NULL)
        {
   
            if(locCol(data)%2==0)  //所以儿子们在奇数行
            {
   
                tr->left->next = tr->right;
            }
            else //所以儿子们在偶数行
            {
   
                tr->right->next = tr->left;
            }
        }
    }
}

(2种建树示意图)


2) 找规律用数学方法

​  ​  舍弃建树,尝试找规律,最后研究出一个神奇的公式
f a t h e r = ( s o n / 2 ) + ( a r e a [ t e m p C o l − 1 ] − 2 ∗ r o u n d ( f l o a t ( s o n − p r e A r e a [ t e m p C o l − 1 ] ) / 2 ) + 1 ) father = (son / 2) + (area[tempCol-1] - 2 * round(float(son - preArea[tempCol-1])/2) + 1) father=(son/2)+(area[tempCol1]2round(float(sonpreArea[tempCol1])/2)+1)
​ ​  ​  其中,tempCol 是当前子节点 son 所在的行(行标从1开始)

​ ​  ​  area 数组存储每个行有多少个节点,故 area[tempCol-1] 代表当前子节点前一行有多少个节点

​ ​​    preAre 数组存储前x行一共多少个节点,故 preArea[tempCol-1] 代表当前子节点之前的所有行节点数之和

(不同符号含义示意图)

怎么得出这个公式的呢?只能说是 小学计算 + 灵感

​  ​  是完全树的性质给的灵感,对于一颗数字顺序存储的完全二叉树,父子编号有这样的性质:

​  ​  父节点 = 子节点 / 2(int型除法,直接保留整数部分)

​  ​  因此,从这个入手,加上本题中每行奇怪的顺序,加上一个偏移量就可以从子得到父,然后经一番推导就得到以上公式。

​  ​  想通了这一点,树不用建了,甚至一个顺序存储的数组也不用建了——上代码(主要关注函数pathInZigZagTree):



题解代码:

class Solution {
   
public:
    int locCol(int label)  //按照完全树的定位某个在第几行
    {
   
    int cols=1;
    int sum=1;
    int base=2;
    while(sum < label)
    {
   
        cols ++;
        sum += base;
        base *= 2;
    }

    return cols;
    }

    vector<int> pathInZigZagTree(int endNum) {
   
    int sum=1;
    int base=1;
    int col=1;
    int cols=locCol(endNum);
    int *area = new int[cols+2]; //每个段的树的量
    int *pre_area = new int[cols+1];  //前面的每一段的总数
    int footprint = endNum;
    vector<int> path;
    area[0] = -1;
    pre_area[0] = -1;

    while(col <= cols)
    {
   
        area[col] = base; //每一段的树
        pre_area[col] = sum; //前col段的总数
        col ++;
        base *= 2;
        sum += base;
    }



    for(int i=1;i<=cols;i++)
    {
   
        path.insert(path.begin(),footprint);
        int temp_col = locCol(footprint); //当前行
        int pre = pre_area[temp_col-1]; //之前和
        int temp = area[temp_col];  //当前行数目
        int cal = (footprint / 2) + (area[temp_col-1] - 2 * round(float(footprint - pre)/2) + 1);  //呕心力学
        footprint = cal;

    }

    return path;
    }
};

int stringToInteger(string input) {
   
    return stoi(input);
}

string integerVectorToString(vector<int> list, int length = -1) {
   
    if (length == -1) {
   
        length = list.size();
    }

    if (length == 0) {
   
        return "[]";
    }

    string result;
    for(int index = 0; index < length; index++) {
   
        int number = list[index];
        result += to_string(number) + ", ";
    }
    return "[" + result.substr(0, result.length() - 2) + "]";
}

int main() {
   
    string line;
    while (getline(cin, line)) {
   
        int label = stringToInteger(line);
        vector<int> ret = Solution().pathInZigZagTree(label);
        string out = integerVectorToString(ret);
        cout << out << endl;
    }
    return 0;
}
全部评论

相关推荐

04-25 19:29
已编辑
宁波大学 运营
被普调的六边形战士很高大:你我美牛孩
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务