C++ unordered_map与unordered_set的模拟实现

news2024/11/26 10:04:09

目录

0.前言

1.哈希表(HashTable)设计

1.1设计思想

1.2 HashTable.h

1.3设计思路

2.unordered_map封装

2.1 UnorderedMap.h

2.2代码解释

2.3测试函数

3.unordered_set封装

3.1 UnorderedSet.h

3.2代码解释

3.3测试函数

4.结语


(图像由AI生成) 

0.前言

在C++标准库中,unordered_mapunordered_set是两个常用的关联容器,它们分别用于存储键值对和唯一值。它们的底层实现基于哈希表,能够提供高效的插入、查找和删除操作。本文将详细介绍如何使用C++实现unordered_mapunordered_set,并展示其具体实现代码。

1.哈希表(HashTable)设计

哈希表是实现unordered_mapunordered_set的基础。在本设计中,我们使用拉链法(链地址法)解决哈希冲突。拉链法通过在每个哈希桶中存储一个链表,使得每个桶可以包含多个具有相同哈希值的元素。

1.1设计思想

  1. 哈希函数:我们定义了一个通用的哈希函数模板,并对字符串类型进行了特化处理。哈希函数的作用是将关键码转换为哈希值。
  2. 哈希节点:每个哈希节点存储一个元素,并包含指向下一个节点的指针。
  3. 哈希表结构:哈希表由一个指针数组和链表组成,每个指针指向一个哈希桶。
  4. 迭代器:为了遍历哈希表,我们定义了迭代器,支持哈希表的遍历操作。
  5. 基本操作:插入、查找和删除操作通过哈希函数确定元素的存储位置,并在相应的链表中进行操作。

1.2 HashTable.h

#pragma once
#include <vector>
#include <string>
#include <utility>
using namespace std;

// 哈希函数模板
template<class K>
struct HashFunc {
    size_t operator()(const K& key) {
        return (size_t)key;
    }
};

// 特化字符串哈希函数
template<>
struct HashFunc<string> {
    size_t operator()(const string& key) {
        size_t hash = 0;
        for (auto e : key) {
            hash *= 31;
            hash += e;
        }
        return hash;
    }
};

namespace hash_bucket {
    // 哈希节点结构
    template<class T>
    struct HashNode {
        T _data;
        HashNode<T>* _next;

        // 构造函数
        HashNode(const T& data)
            : _data(data), _next(nullptr) {}
    };

    // 前置声明
    template<class K, class T, class KeyOfT, class Hash>
    class HashTable;

    // 哈希表迭代器
    template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
    struct HTIterator {
        typedef HashNode<T> Node;
        typedef HTIterator<K, T, Ptr, Ref, KeyOfT, Hash> Self;

        Node* _node; // 当前节点
        const HashTable<K, T, KeyOfT, Hash>* _pht; // 哈希表指针

        // 构造函数
        HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht)
            : _node(node), _pht(pht) {}

        // 重载*运算符
        Ref operator*() {
            return _node->_data;
        }

        // 重载->运算符
        Ptr operator->() {
            return &_node->_data;
        }

        // 重载!=运算符
        bool operator!=(const Self& s) {
            return _node != s._node;
        }

        // 重载++运算符(前置)
        Self& operator++() {
            if (_node->_next) {
                _node = _node->_next;
            } else {
                KeyOfT kot;
                Hash hs;
                size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
                ++hashi;
                while (hashi < _pht->_tables.size()) {
                    if (_pht->_tables[hashi]) {
                        break;
                    }
                    ++hashi;
                }
                if (hashi == _pht->_tables.size()) {
                    _node = nullptr; // end()
                } else {
                    _node = _pht->_tables[hashi];
                }
            }
            return *this;
        }
    };

    // 哈希表类
    template<class K, class T, class KeyOfT, class Hash>
    class HashTable {
        // 友元声明
        template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
        friend struct HTIterator;

        typedef HashNode<T> Node;
    public:
        typedef HTIterator<K, T, T*, T&, KeyOfT, Hash> Iterator;
        typedef HTIterator<K, T, const T*, const T&, KeyOfT, Hash> ConstIterator;

        // 开始迭代器
        Iterator Begin() {
            if (_n == 0)
                return End();
            for (size_t i = 0; i < _tables.size(); i++) {
                Node* cur = _tables[i];
                if (cur) {
                    return Iterator(cur, this);
                }
            }
            return End();
        }

        // 结束迭代器
        Iterator End() {
            return Iterator(nullptr, this);
        }

        // 常量开始迭代器
        ConstIterator Begin() const {
            if (_n == 0)
                return End();
            for (size_t i = 0; i < _tables.size(); i++) {
                Node* cur = _tables[i];
                if (cur) {
                    return ConstIterator(cur, this);
                }
            }
            return End();
        }

        // 常量结束迭代器
        ConstIterator End() const {
            return ConstIterator(nullptr, this);
        }

        // 构造函数
        HashTable() {
            _tables.resize(10, nullptr);
        }

        // 析构函数
        ~HashTable() {
            for (size_t i = 0; i < _tables.size(); i++) {
                Node* cur = _tables[i];
                while (cur) {
                    Node* next = cur->_next;
                    delete cur;
                    cur = next;
                }
                _tables[i] = nullptr;
            }
        }

        // 插入元素
        pair<Iterator, bool> Insert(const T& data) {
            KeyOfT kot;
            Iterator it = Find(kot(data));
            if (it != End())
                return make_pair(it, false);

            Hash hs;
            size_t hashi = hs(kot(data)) % _tables.size();

            if (_n == _tables.size()) {
                vector<Node*> newtables(_tables.size() * 2, nullptr);
                for (size_t i = 0; i < _tables.size(); i++) {
                    Node* cur = _tables[i];
                    while (cur) {
                        Node* next = cur->_next;
                        size_t hashi = hs(kot(cur->_data)) % newtables.size();
                        cur->_next = newtables[hashi];
                        newtables[hashi] = cur;
                        cur = next;
                    }
                    _tables[i] = nullptr;
                }
                _tables.swap(newtables);
            }

            Node* newnode = new Node(data);
            newnode->_next = _tables[hashi];
            _tables[hashi] = newnode;
            ++_n;

            return make_pair(Iterator(newnode, this), true);
        }

        // 查找元素
        Iterator Find(const K& key) {
            KeyOfT kot;
            Hash hs;
            size_t hashi = hs(key) % _tables.size();
            Node* cur = _tables[hashi];
            while (cur) {
                if (kot(cur->_data) == key) {
                    return Iterator(cur, this);
                }
                cur = cur->_next;
            }
            return End();
        }

        // 删除元素
        bool Erase(const K& key) {
            KeyOfT kot;
            Hash hs;
            size_t hashi = hs(key) % _tables.size();
            Node* prev = nullptr;
            Node* cur = _tables[hashi];
            while (cur) {
                if (kot(cur->_data) == key) {
                    if (prev == nullptr) {
                        _tables[hashi] = cur->_next;
                    } else {
                        prev->_next = cur->_next;
                    }
                    delete cur;
                    --_n;
                    return true;
                }
                prev = cur;
                cur = cur->_next;
            }
            return false;
        }

    private:
        vector<Node*> _tables; // 哈希表桶数组
        size_t _n = 0; // 表中存储的数据个数
    };
}

1.3设计思路

  1. 哈希函数:通用的哈希函数模板和针对字符串类型的特化版本,确保不同类型的数据都能正确计算哈希值。
  2. 哈希节点:每个哈希节点存储一个数据元素,并且包含指向下一个节点的指针,用于链表结构。
  3. 哈希表类:哈希表类包含一个指针数组,每个指针指向一个哈希桶。哈希表支持插入、查找和删除操作。
  4. 迭代器:哈希表迭代器支持对哈希表的遍历操作,通过重载运算符实现。

2.unordered_map封装

unordered_map是一个基于哈希表实现的键值对容器。我们将使用前面设计的HashTable类来实现unordered_mapunordered_map提供了高效的插入、查找和删除操作,适用于需要快速查找的场景。

2.1 UnorderedMap.h

#pragma once
#include "HashTable.h"

namespace wxk {
    // unordered_map类模板
    template<class K, class V, class Hash = HashFunc<K>>
    class unordered_map {
        // 定义一个提取键的仿函数
        struct MapKeyOfT {
            const K& operator()(const pair<K, V>& kv) {
                return kv.first;
            }
        };

    public:
        // 定义迭代器类型
        typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Iterator iterator;
        typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::ConstIterator const_iterator;

        // 返回指向容器第一个元素的迭代器
        iterator begin() {
            return _ht.Begin();
        }

        // 返回指向容器末尾的迭代器
        iterator end() {
            return _ht.End();
        }

        // 返回指向容器第一个元素的常量迭代器
        const_iterator begin() const {
            return _ht.Begin();
        }

        // 返回指向容器末尾的常量迭代器
        const_iterator end() const {
            return _ht.End();
        }

        // 插入键值对
        pair<iterator, bool> insert(const pair<K, V>& kv) {
            return _ht.Insert(kv);
        }

        // 重载[]操作符,访问或插入元素
        V& operator[](const K& key) {
            pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
            return ret.first->second;
        }

        // 查找元素
        iterator Find(const K& key) {
            return _ht.Find(key);
        }

        // 删除元素
        bool Erase(const K& key) {
            return _ht.Erase(key);
        }

    private:
        // 使用哈希表来存储键值对
        hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
    };

    // 测试unordered_map
     void test_map() {
        unordered_map<int, string> dict;

        // 插入键值对
        dict.insert({1, "one"});
        dict.insert({2, "two"});
        dict.insert({3, "three"});
        dict.insert({4, "four"});
        dict.insert({5, "five"});

        // 使用[]操作符访问和修改元素
        dict[2] = "TWO";
        dict[6] = "six"; // 插入新的键值对

        // 查找元素
        auto it = dict.Find(3);
        if (it != dict.end()) {
            cout << "Found: " << it->first << " -> " << it->second << endl;
        } else {
            cout << "Not Found: 3" << endl;
        }

        // 删除元素
        bool erased = dict.Erase(4);
        cout << "Element with key 4 " << (erased ? "was erased." : "not found.") << endl;

        // 遍历unordered_map
        cout << "Contents of the unordered_map:" << endl;
        for (auto it = dict.begin(); it != dict.end(); ++it) {
            cout << it->first << " -> " << it->second << endl;
        }
        cout << endl;
    }

}

2.2代码解释

  1. MapKeyOfT仿函数:用于提取键值对中的键,operator() 返回键值对的第一个元素,即键。
  2. 类型定义:定义了iteratorconst_iterator,分别用于遍历和访问unordered_map中的元素。
  3. begin() 和 end():返回指向容器第一个元素和末尾的迭代器,支持常量版本。
  4. insert():插入键值对,使用哈希表的Insert方法。
  5. operator[]:重载[]操作符,用于访问或插入元素。如果键不存在,则插入默认值。
  6. Find():查找指定键的元素,返回指向该元素的迭代器。
  7. Erase():删除指定键的元素,返回操作是否成功的布尔值。
  8. 私有成员变量:使用哈希表HashTable存储键值对。

2.3测试函数

  • test_map():测试unordered_map的插入、删除、访问和遍历功能,展示了如何使用该容器进行基本操作。

输出结果:

Found: 3 -> three
Element with key 4 was erased.
Contents of the unordered_map:
1 -> one
2 -> TWO
3 -> three
5 -> five
6 -> six

3.unordered_set封装

unordered_set是一个基于哈希表实现的唯一值容器。我们将使用前面设计的HashTable类来实现unordered_setunordered_set提供了高效的插入、查找和删除操作,适用于需要快速查找唯一元素的场景。

3.1 UnorderedSet.h

#pragma once
#include "HashTable.h"

namespace wxk {
    // unordered_set类模板
    template<class K, class Hash = HashFunc<K>>
    class unordered_set {
        // 定义一个提取键的仿函数
        struct SetKeyOfT {
            const K& operator()(const K& key) {
                return key;
            }
        };

    public:
        // 定义迭代器类型
        typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::Iterator iterator;
        typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::ConstIterator const_iterator;

        // 返回指向容器第一个元素的迭代器
        iterator begin() {
            return _ht.Begin();
        }

        // 返回指向容器末尾的迭代器
        iterator end() {
            return _ht.End();
        }

        // 返回指向容器第一个元素的常量迭代器
        const_iterator begin() const {
            return _ht.Begin();
        }

        // 返回指向容器末尾的常量迭代器
        const_iterator end() const {
            return _ht.End();
        }

        // 插入元素
        pair<iterator, bool> insert(const K& key) {
            return _ht.Insert(key);
        }

        // 查找元素
        iterator Find(const K& key) {
            return _ht.Find(key);
        }

        // 删除元素
        bool Erase(const K& key) {
            return _ht.Erase(key);
        }

    private:
        // 使用哈希表来存储唯一元素
        hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;
    };

    // 测试unordered_set
    void test_set() {
        unordered_set<int> s;

        // 插入元素
        int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 3, 3, 15 };
        for (auto e : a) {
            s.insert(e);
        }

        // 查找元素
        auto it = s.Find(6);
        if (it != s.end()) {
            cout << "Found: " << *it << endl;
        } else {
            cout << "Not Found: 6" << endl;
        }

        // 删除元素
        bool erased = s.Erase(7);
        cout << "Element 7 " << (erased ? "was erased." : "not found.") << endl;

        // 遍历unordered_set
        cout << "Contents of the unordered_set:" << endl;
        for (auto it = s.begin(); it != s.end(); ++it) {
            cout << *it << " ";
        }
        cout << endl;
    }
}

3.2代码解释

  1. SetKeyOfT仿函数:用于提取集合中的元素,operator() 返回元素本身。
  2. 类型定义:定义了iteratorconst_iterator,分别用于遍历和访问unordered_set中的元素。
  3. begin() 和 end():返回指向容器第一个元素和末尾的迭代器,支持常量版本。
  4. insert():插入元素,使用哈希表的Insert方法。
  5. Find():查找指定元素,返回指向该元素的迭代器。
  6. Erase():删除指定元素,返回操作是否成功的布尔值。
  7. 私有成员变量:使用哈希表HashTable存储唯一元素。

3.3测试函数

让我们编写一个新的测试函数,展示更多unordered_set的功能,包括插入、查找、删除和遍历操作。

void test_set() {
    unordered_set<int> s;

    // 插入元素
    int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 3, 3, 15 };
    for (auto e : a) {
        s.insert(e);
    }

    // 查找元素
    auto it = s.Find(6);
    if (it != s.end()) {
        cout << "Found: " << *it << endl;
    } else {
        cout << "Not Found: 6" << endl;
    }

    // 删除元素
    bool erased = s.Erase(7);
    cout << "Element 7 " << (erased ? "was erased." : "not found.") << endl;

    // 遍历unordered_set
    cout << "Contents of the unordered_set:" << endl;
    for (auto it = s.begin(); it != s.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 插入更多元素并检查唯一性
    s.insert(10);
    s.insert(2); // 重复插入2,测试唯一性
    s.insert(8);

    // 遍历unordered_set
    cout << "Contents of the unordered_set after more insertions:" << endl;
    for (auto it = s.begin(); it != s.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
}

输出结果: 

Found: 6
Element 7 was erased.
Contents of the unordered_set:
1 2 3 14 4 15 5 16 6
Contents of the unordered_set after more insertions:
1 2 3 4 5 6 8 10 14 15 16

4.结语

通过本文,我们详细介绍了如何使用C++实现unordered_mapunordered_set,并展示了其底层哈希表的实现。通过这些示例代码,读者可以深入理解哈希表的工作原理及其在C++中的应用。在实际开发中,灵活运用哈希表能够大幅提升程序的性能和效率。希望本文能够帮助读者掌握哈希表及其相关容器的实现与应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1952581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目打包与运行

前端运行时必须有与后端相同的数据库版本&#xff0c;数据库账号密码 右侧maven -> 展开要打包的项目 -> 生命周期 -> 双击package 打包好之后在target目录下 右键打开 在资源目录下输入cmd&#xff0c;执行以下命令即可运行&#xff08;端口号为yml文件…

人流量为王:背后的赚钱密码深度解析

在当今商业世界中&#xff0c;“人流量为王”这一理念被广泛认可和奉行。但你是否认真思考过&#xff0c;这简单的四个字背后&#xff0c;究竟隐藏着怎样复杂而精妙的赚钱逻辑&#xff1f; 一、人流量意味着潜在客户的聚集 想象一下繁华的商业街&#xff0c;熙熙攘攘的人群穿梭…

7月26日JavaSE学习笔记

反射 Java是面向对象的&#xff0c;有对象必须先有类&#xff0c; 有static修饰类的属性和方法&#xff1b;在Java中存储了类的内容&#xff0c;这个内容也应该是一个对象&#xff1b;Java中每一个用到的类都会加载一块内存&#xff0c;这每一块内存都是一个对象&#xff1b;这…

学习周报:文献阅读+HEC RAS案例

目录 摘要 Abstract 文献阅读&#xff1a;通过HEC RAS软件为罗马尼亚布加勒斯特市的Dmbovița河水管理的水力模型 文献摘要 讨论|结论 理论知识 边界条件计算 流量计算方式 曼宁公式 (Mannings Equation) 连续性方程 (Continuity Equation) 能量方程 (Energy Equatio…

EB Tresos 基于S32K3芯片 ICU模块实现gpio外部中断配置[后续更新实现icu模块的其他功能]

环境&#xff1a;eb tresos 27.0.1 port 模块配置&#xff1a; 选择一个具有erq功能的引脚并配置为erq功能。如下我选择的是 PTB0 -EIRQ[8] - SIUL2_EXT_IRQ_8_15_ISR Platform 模块配置 在这个模块中配置中断的开关以及中断句柄 ICU模块配置 具体配置参考博客&#xff1a;…

【python】python大学排名数据抓取+可视化(源码+数据集+可视化+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

深度强化学习 ②(DRL)

参考视频&#xff1a;&#x1f4fa;王树森教授深度强化学习 前言&#xff1a; 最近在学习深度强化学习&#xff0c;学的一知半解&#x1f622;&#x1f622;&#x1f622;&#xff0c;这是我的笔记&#xff0c;欢迎和我一起学习交流~ 这篇博客目前还相对比较乱&#xff0c;后面…

黑马Java零基础视频教程精华部分_5_面向对象综合练习

系列文章目录 文章目录 系列文章目录一、文字版格斗游戏二、文字版格斗游戏进阶版三、对象数组练习1、对象数组1先学习一下键盘录入。注意&#xff1a;两套体系不能混用 对象数组2对象数组3对象数组4对象数组5 一、文字版格斗游戏 GameTes.javat代码如下&#xff1a; package …

[数通网络基础]——广播域与路由器

广播域 广播域概述 广播域是指网络中能接收到同一广播消息的所有设备的集合。 广播域的大小会影响网络的性能和效率。当同一个广播域内广播报文过多时&#xff0c;会对局域网造成干扰&#xff0c;导致网络延迟&#xff0c;网络拥塞&#xff08;上网卡&#xff0c;上网慢&…

hot100-3滑动窗口

3无重复字符得最长字串 438找出字符串中得所有字母异位词 遇到没有限制字母排列方式的&#xff0c;都可以考虑维护一个charCode数组 和第567题相似 567字符串得排列&#xff08;和438一个思路&#xff09;

docker dotnet-dump离线部署

1.下载指定dotnet版本的dotnet-dump 示例地址&#xff1a; https://www.nuget.org/packages/dotnet-dump/3.1.141901#dependencies-body-tab 我本地测试的是netcore 3.1 2. 在本地解压 将文件解压出来。看到any目录,能看到我们要用的dotnet-dump文件 3. 将tools/netcoreapp2.…

AccessLog| 一款开源的日志分析系统

前言 ClkLog作为分析系列产品中的前端数据分析系统&#xff0c;通过采集前端应用数据进行用户行为分析。其社区版从23年9月发布至今已有近一年&#xff0c;商业版也上线快半年&#xff0c;感谢大家一直以来的关注和支持&#xff0c;ClkLog会继续做好产品升级与服务&#xff0c;…

算法-----递归~~搜索~~回溯(宏观认识)

目录 1.什么是递归 1.1二叉树的遍历 1.2快速排序 1.3归并排序 2.为什么会用到递归 3.如何理解递归 4.如何写好一个递归 5.什么是搜索 5.1深度&#xff08;dfs&#xff09;优先遍历&优先搜索 5.2宽度&#xff08;bfs&#xff09;优先遍历&优先搜索 6.回溯 1.什…

微信小游戏之 三消(一)

首先设定一下 单个 方块 cell 类&#xff1a; 类定义和属性 init 方法 用于初始化方块&#xff0c;接收游戏实例、数据、宽度、道具类型和位置。 onWarning 方法 设置警告精灵的帧&#xff0c;并播放闪烁动作&#xff0c;用于显示方块的警告状态。 grow 方法 根据传入的方向…

【科研技巧】如何查找一个人发表的所有文章

使用此网站 点击作者检索 点击作者名字 可以看到全部文章

SpringBoot运行流程源码分析

run方法核心流程 我们在启动SpringBoot的时候调用的是SpringApplication类的静态run方法。其核心流程如下图所示&#xff1a; 在run方法内完成了SpringApplication的声明周期。&#xff0c;这个过程涉及的几个核心类如下&#xff1a; SpringApplicationRunListeners&#xff…

【C++】:红黑树的应用 --- 封装map和set

点击跳转至文章&#xff1a;【C】&#xff1a;红黑树深度剖析 — 手撕红黑树&#xff01; 目录 前言一&#xff0c;红黑树的改造1. 红黑树的主体框架2. 对红黑树节点结构的改造3. 红黑树的迭代器3.1 迭代器类3.2 Begin() 和 End() 四&#xff0c;红黑树相关接口的改造4.1 Find…

Qt基础 | 自定义界面组件 | 提升法 | 为UI设计器设计自定义界面组件的Widget插件 | MSVC2019编译器中文乱码问题

文章目录 一、自定义 Widget 组件1.自定义 Widget 子类2.自定义 Widget 组件的使用 二、自定义 Qt Designer 插件1.创建 Qt Designer Widget 插件项目2.插件项目各文件的功能实现3.插件的编译与安装4.使用自定义插件5.使用 MSVC 编译器输出中文的问题 一、自定义 Widget 组件 当…

【React】详解受控表单绑定

文章目录 一、受控组件的基本概念1. 什么是受控组件&#xff1f;2. 受控组件的优势3. 基本示例导入和初始化定义函数组件处理输入变化处理表单提交渲染表单导出组件 二、受控组件的进阶用法1. 多个输入框的处理使用多个状态变量使用一个对象管理状态 2. 处理选择框&#xff08;…

leetcode-104. 二叉树的最大深度

题目描述 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;root [1,n…