求二叉树的带权路径长度(深搜或广搜)

考研昨天结束了,专业课我考得不好,是408,算法题13分。今年是求二叉树的带权路径长度,我就写了算法思想,代码空白,因为时间来不及了,慌了,是心态的问题吧,做到最后有种天要塌下来的感觉,回来后很沮丧,因为我觉得自己是可以写出程序的,不服啊! 

下面说说这道题目。树的带权路径长度(Weighted Path Length)定义:树中所有叶子的带权路径长度之和。比如下面这棵树,WPL就是3*2+7*1 = 13。

                         2
                        / \
                       /   \
                      4     7
                       \  
                        \
                         3

1、深搜(前序遍历)的算法可以用递归,程序简洁,先判断当前的根是不是叶子,若是则加上该叶子的带权路径长度,叶子的深度可以作为参数传递得到,求深度是个必须解决的问题。然后对左子树、右子树递归进行。因为是递归,所以我用了全局变量来记录部分和,不知道有没有不用全局变量的方法。

2、广搜(层次遍历)中的难题是求每层的深度,我参考了王道书上的算法,用了一个 last 变量来记录每层的最右结点,我觉得这个方法很巧,有点难想。

两个算法的实现如下(测试数据建的是上面那棵树,假设树根的深度为0)

/********************************************************************
created:    2014/01/06
created:    14:42
file base:  main
file ext:   cpp
author:     Justme0 (http://blog.csdn.net/Justme0)

purpose:    求二叉树的带权路径长度
*********************************************************************/

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <queue>
#include <cassert>
using namespace std;

/*
** 二叉链表的结点定义
*/
struct Node {
    Node *left;
    Node *right;
    int weight;

    Node() : left(NULL), right(NULL), weight(0) {}

    bool has_left() const {
        return NULL != left;
    }

    bool has_right() const {
        return NULL != right;
    }

    bool is_leaf() const {
        return !has_left() && !has_right();
    }
};

#pragma region 深搜

/*
** 前序遍历求树的带权路径长度(weighted path length)
*/
int wpl_dfs = 0;    // wpl_dfs 为加权的部分和
void DFS(Node *root, int depth) {
    if (root == NULL) { // root 为空树
        return ;
    }

    if (root->is_leaf()) {
        wpl_dfs += root->weight * depth;
    } else {
        DFS(root->left, depth + 1);
        DFS(root->right, depth + 1);
    }
}

int get_WPL_dfs(Node *root) {
    DFS(root, 0);
    return wpl_dfs;
}

#pragma endregion 深搜


#pragma region 广搜

/*
** 层次遍历求树的带权路径长度(weighted path length)
*/
int get_WPL_bfs(Node *root) {
    if (root == NULL) { // root 是空树
        return 0;
    }

    int wpl_bfs = 0;
    int depth = 0;
    Node *last = root;  // last 为每层最右结点的地址,用于确定高度
    queue<Node *> Q;
    Q.push(root);
    do {
        Node *p = Q.front();
        Q.pop();

        if (p->is_leaf()) {
            wpl_bfs += p->weight * depth;
        } else {
            if (p->has_left()) {
                Q.push(p->left);
            }
            if (p->has_right()) {
                Q.push(p->right);
            }
        }

        if (p == last && !Q.empty()) {  // 此时处理到该层的最右结点
            last = Q.back();    // 队尾恰好是下一层的最右结点的地址
            ++depth;
        }
    } while (!Q.empty());

    return wpl_bfs;     
}

#pragma endregion 广搜

/*
** 输入前序序列动态生成树
*/
void creat_tree(Node *&root) {
    int info;
    cin >> info;
    if (info == -1) {
        root = NULL;
    } else {
        root = new Node;
        root->weight = info;
        creat_tree(root->left);
        creat_tree(root->right);
    }
}

/*
** 后序遍历释放树
*/
void free_tree(Node *&root) {
    if (root == NULL) {
        return ;
    }

    free_tree(root->left);
    free_tree(root->right);
    delete root;
    root = NULL;
}

int main(int argc, char **argv) {
    freopen("cin.txt", "r", stdin);

    Node *tree = NULL;
    creat_tree(tree);

    int dfs_wpl = get_WPL_dfs(tree);
    int bfs_wpl = get_WPL_bfs(tree);
    assert(dfs_wpl == bfs_wpl);
    cout << "深搜:WPL=" << dfs_wpl << endl;    // 11
    cout << "广搜:WPL=" << bfs_wpl << endl;    // 11

    free_tree(tree);

    return 0;
}

/*cin.txt
1 2 -1 4 -1 -1 3 -1 -1
*/

我考试时写的算法思想是广搜,然后一想到程序中得用队列,还得考虑深度,我就蒙了,只写了个函数头,希望能施舍一点分。。。我觉得这题是个典型的题目,不偏,出得挺好的。

20140110 
1、下面这种方法基于的原理是 WPL 为所有分支结点的权的和(这里的分支结点的权定义为它的左儿子和右儿子的权的和,同 Huffman 树)。

/*
** 后序遍历将分支结点的左儿子和右儿子的权相加作为该分支结点的权(覆盖原来无用的权)
** 再将所有的分支结点的权相加即 WPL
*/
int wpl_postorder = 0;
int get_WPL_postorder(Node *root) {
    if (root == NULL) {
        return 0;
    }

    get_WPL_postorder(root->left);
    get_WPL_postorder(root->right);
    if (!root->is_leaf()) {
        root->weight = 0;
        if (root->has_left()) {
            root->weight += root->left->weight;
        }
        if (root->has_right()) {
            root->weight += root->right->weight;
        }
        wpl_postorder += root->weight;
    }
    return wpl_postorder;
}
2、下面的方法有点偷懒,在结点定义时加了一个深度字段,用以解决求深度的问题。这种方法可以任何一种方式遍历树,当用前序时原理同开始的深搜算法,只不过那里的深搜将深度作为参数求出,这个干脆为它增加一个字段。

/*
** 在结点中加深度字段
*/
int wpl_depth = 0;

void visit(Node *root) {
    if (NULL == root) {
        return ;
    }

    if (root->is_leaf()) {
        wpl_depth += root->weight * root->depth;
    } else {
        if (root->has_left()) {
            root->left->depth = root->depth + 1;
        }
        if (root->has_right()) {
            root->right->depth = root->depth + 1;
        }
        visit(root->left);
        visit(root->right);
    }
}

int get_WPL_depth(Node *root) {
    root->depth = 0;
    visit(root);
    return wpl_depth;
}


  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
二叉树带权路径长度(WPL)是指二叉树中所有叶结点的带权路径长度之和。\[1\]带权路径长度是从的根结点到任意结点的路径长度(经过的边数)与该结点上权值的乘积。\[2\]要设计一个算法来解给定二叉树T的WPL。 一个可行的思路是使用递归来遍历二叉树的所有根结点,并将每个根结点的带权路径长度与一个静态局部变量相加,最终得到二叉树的WPL。\[3\]具体实现时,可以定义一个递归函数,该函数的参数为当前结点和该结点的深度。通过判断当前结点是否有左右子结点,来递归遍历所有的根结点。在遍历过程中,将每个根结点的带权路径长度与静态局部变量相加,最终得到二叉树的WPL。 以下是一个示例的二叉树结点数据类型的定义: typedef struct BitNode { int weight; BitNode *left, *right; } BitNode, *BitTree; 通过实现上述算法,可以计算出给定二叉树带权路径长度(WPL)。 #### 引用[.reference_title] - *1* [【数据结构】二叉树带权路径长度WPL是二叉树中所有叶结点的带权路径长度之和。给定一颗二叉树T,采用二叉...](https://blog.csdn.net/weixin_51280571/article/details/126234359)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [二叉树带权路径长度(WPL)是二叉树中所有叶结点的带权路径长度之和。给定一棵二叉树T,采用二叉链表存储,...](https://blog.csdn.net/weixin_48239221/article/details/120337506)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值