数据结构-考研难点代码突破(外部排序 - C++实现败者树(三指针树结构实现))

1. 思想介绍

因为我是先看了败者树的思想,然后实现的代码,所以我和网上的主流败者树实现方式不同,我并没有想到使用类似堆的方式,通过数组保存父子节点。

我直接将其实现成了树状结构。我这个算法有很多可以优化的地方,但因为考研对这部分代码的要求并不高,所以这里就不改了。

败者树节点定义:

// 败者树节点结构,这里使用树状结构实现败者树
struct TreeNode
{
    int _fault_pos = -1;        // 失败节点的归并段在数组的位置
    int _win_val = INT_MIN; // 这层节点的胜利者的值
    int _win_pos = -1;//这层胜利节点在数组的位置
    int _del_val = 0;//这个节点参与比较的关键值
    TreeNode *_parent = nullptr;
    TreeNode *_left = nullptr;
    TreeNode *_right = nullptr;
};

这个leave定义了败者树的叶子节点。

struct leave
{
    friend class defeatTree;

private:
    TreeNode *_parent = nullptr;

public:
    std::vector<int> _vet; // 存放数据的数组
    int _begin = 0;        // 存放败者树需要处理vet那个元素

    leave(const std::vector<int> &vet) : _vet(vet) {}

    leave() = default;
};

思想:

构造败者树时传入一个二维数组,每一个数组就是一个归并段。

因为败者树是一颗完全二叉树,这颗树的叶子节点的个数就是归并段的个数。

所以我们直到了叶子节点的个数,来先构造一颗树,最后把叶子节点的值修改为归并段的值,这棵树的非叶子节点通过向上调整算法进行调整即可。

leave结构体中的_begin字段表示处理到这个归并段哪一个元素,当筛选出最小值后,将最小值弹出,其对应的归并段的_begin+1处理这一段的下一个元素,同时向上调整败者树。

败者树的作用,在进行外部排序时,访问硬盘操作占用的时间比较大。

使用归并的操作,访问磁盘的次数不会超过树高,所以要压缩树高

树高计算为logk(M)

K为归并路数,M是归并段,提高K或者减少M都可以减少高度。

但是提高k不一定能减少高度,因为提高k,内部归并比较次数(k-1)也会上升,为了去除这个问题,引出了败者树。

败者树每次选出归并段的最大值或最小值只需要树高度次即可

2. C++代码实现

#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
#include<assert.h>
//#include<unordered_map>

// 败者树节点结构,这里使用树状结构实现败者树
struct TreeNode
{
    int _fault_pos = -1;        // 失败节点的归并段在数组的位置
    int _win_val = INT_MIN; // 这层节点的胜利者的值
    int _win_pos = -1;//这层胜利节点在数组的位置
    int _del_val = 0;//这个节点参与比较的关键值
    TreeNode *_parent = nullptr;
    TreeNode *_left = nullptr;
    TreeNode *_right = nullptr;
};

struct leave
{
    friend class defeatTree;

private:
    TreeNode *_parent = nullptr;

public:
    std::vector<int> _vet; // 存放数据的数组
    int _begin = 0;        // 存放败者树需要处理vet那个元素

    leave(const std::vector<int> &vet) : _vet(vet) {}

    leave() = default;
};

class defeatTree
{
private:
     //std::unordered_map<leave, int> _index; // leave节点在buff中的映射
    TreeNode* _root = nullptr;
    std::vector<TreeNode*>leaveNodes;

    void _findLeaves(std::vector<TreeNode*>&buff,TreeNode*node) {
        if (node == nullptr) {
            return;
        }
        if (node->_left == nullptr || node->_right == nullptr) {
            buff.push_back(node);
        }
        _findLeaves(buff, node->_left);
        _findLeaves(buff, node->_right);
    }

    //将buff数组的值添加到叶子节点上
    void _update(std::vector<TreeNode*>&leves,std::vector<leave>&buff) {
        assert(leves.size() == buff.size());
        for (int i = 0; i < buff.size(); i++) {
            leves[i]->_del_val = buff[i]._vet[buff[i]._begin];
            leves[i]->_win_val = buff[i]._vet[buff[i]._begin];
            leves[i]->_fault_pos = i;
            leves[i]->_win_pos = i;
        }
    }

    void _adjust(TreeNode*node) {
        while (node->_parent != nullptr) {
            TreeNode* peer = node->_parent->_left == node ? node->_parent->_right : node->_parent->_left;
            TreeNode* parent = node->_parent;

            if (peer->_del_val != INT_MIN) {
                if (node->_win_val < peer->_win_val) {
                    //node获胜
                    parent->_win_val = node->_del_val;
                    parent->_fault_pos = peer->_win_pos;
                    parent->_del_val = node->_del_val;
                    parent->_win_pos = node->_win_pos;
                }
                else {
                    //peer获胜
                    parent->_win_pos = peer->_del_val;
                    parent->_fault_pos = node->_win_pos;
                    parent->_del_val = peer->_del_val;
                    parent->_win_pos = peer->_win_pos;
                }
                std::cout << "DEBUG: " << node->_del_val << ":" << peer->_del_val << std::endl;
            }
            else {
                //对方peer节点未初始化,parent值设定和node相同
                parent->_win_pos = node->_del_val;
                parent->_fault_pos = node->_win_pos;
                parent->_del_val = node->_del_val;
                parent->_win_pos = node->_win_pos;
            }
            node = node->_parent;
        }
    }
public:
    defeatTree(std::vector<leave>& buff)
    {
        构建leave和其buff下标的映射
        //for (int i = 0; i < buff.size(); i++) {
        //    _index[buff[i]] = i;
        //}

        // 根据leave叶子节点个数构造完全二叉树
        int leave = 0;
        TreeNode* root = new TreeNode();
        _root = root;
        std::queue<TreeNode*> q;
        int level = 1;
        bool changeLevel = false;
        q.push(root);
        while (leave != buff.size())
        {
            if (changeLevel == true)
            {
                leave -= 1;
                changeLevel = false;
            }
            // 层序创建树,统计数的叶子节点
            for (int i = 0; i < pow(2, level - 1); i++)
            {
                TreeNode* node = q.front();
                q.pop();
                node->_left = new TreeNode();
                node->_right = new TreeNode();
                node->_left->_parent = node;
                q.push(node->_left);
                leave += 1;
                if (leave == buff.size()) {
                    break;
                }
                node->_right->_parent = node;
                q.push(node->_right);
                leave += 1;
                if (leave == buff.size()) {
                    break;
                }
            }
            if (leave == buff.size()) {
                break;
            }
            // 层数增加,重新计算此时叶子节点
            level += 1;
            leave = pow(2, level-1);
            changeLevel = true;
        }

        //找到叶子节点,向上构造败者树
        _findLeaves(leaveNodes,root);
        
        //给叶子节点添加值
        _update(leaveNodes, buff);

        //对每一个叶子节点进行向上调整
        for (int i = 0; i < leaveNodes.size(); i++) {
            _adjust(leaveNodes[i]);
        }
        disPlay(buff);
    }

    void disPlay(std::vector<leave>& buff) {
        std::cout << "最小值:" << _root->_win_val << std::endl;
        TreeNode* node = _root;
        std::cout << "击败的值为: ";
        while (node->_left != nullptr) {
            std::cout << buff[node->_fault_pos]._vet[buff[node->_fault_pos]._begin] <<
                " ";
            node = node->_left;
        }
    }
};

测试代码:

#include "defeatTree.h"

using namespace std;

int main()
{
    vector<leave> leaves;
    leave lev({ 10,15,31 }); //归并段1
    leave lev2({ 9,20 });    //归并段2
    leave lev3({ 1, 34, 37 });
    leaves.push_back(lev);
    leaves.push_back(lev2);
    leaves.push_back(lev3);
    defeatTree tree(leaves);
    return 0;
}

在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NUC_Dodamce

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

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

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

打赏作者

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

抵扣说明:

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

余额充值