JDK中的Map类型采用键值对的方式保存数据,且键(key)不能重复。在HashMap的实现中实际采用了Hash分类加数组排序的方式。在C++中我没有采用这样的算法。而是通过首先对Key值进行二叉树排序,再查找对应的Value。而对整个树型结构排序则使用最基本的中序遍历。这些都是数据结构的知识,不太了解的可以查看我之前的博客(查找树ADT)。下面言归正传。

假设场景是我需要输入一系列的人名+年龄,然后按照人名字典排序显示所有数据。以下是测试方法:

  1. int main() {
  2. using namespace std;
  3. Item<string, int>* it1 = new Item<string, int>("Done", );
  4. it1->put("Tom", );
  5. it1->put("Kate", );
  6. it1->put("Lory", );
  7. it1->put("Xiaom", );
  8. it1->put("Kate", ); // 重复记录
  9.  
  10. cout << it1->size() << endl; // 結果:5
  11.  
  12. Item<string, int>* it2 = it1; // 调用拷贝构造
  13. Item<string, int>::Entry* ep = it2->sort(); // 获取数组指针
  14.  
  15. for (int i = ; i < it2->size(); i++) { // 遍历指针
  16. cout << ep[i].str_k << "=" << ep[i].str_v << endl;
  17. }
  18. return ;
  19. }

(1)第8行添加了一条重复数据,应该覆盖第5行的数据。

(2)第12行调用拷贝函数,测试指针赋值是否正确。

(3)第15行遍历数组,查询数据。

下面是模板类的设计:

  1. #ifndef ITEM_H_
  2. #define ITEM_H_
  3. #include <iostream>
  4. template<typename K, typename V>
  5. class Item {
  6. public:
  7. struct Entry { // 构造一个内部结构用来保存排序对象
  8. K str_k;
  9. V str_v;
  10. };
  11. private:
  12. static int len; // 每次新增一个元素+1,作为size()的返回值
  13. static int index; //返回数组的下标
  14. K _key; // 键
  15. V _value; // 值
  16. Item* _left; // 左子树
  17. Item* _right; // 右子树
  18. void _sort(Item& item, Entry entry[]); // 内部排序方法
  19. public:
  20. Item(const K& key, const V& value) :
  21. _key(key), _value(value), _left(), _right() {
  22. len++;
  23. }
  24. virtual ~Item() { // 析构左子树指针和右子树指针
  25. if (_left != )
  26. delete _left;
  27. if (_right != )
  28. delete _right;
  29. }
  30. Item(const Item& o) : // 拷贝构造
  31. _key(o._key), _value(o._value), _left(o._left), _right(o._right) {
  32. }
  33. Item& operator=(const Item& it) { // 默认赋值函数
  34. _key = it._key;
  35. _value = it._value;
  36. if (_left != )
  37. delete _left;
  38. if (_right != )
  39. delete _right;
  40. _left = it._left;
  41. _right = it._right;
  42. return *this;
  43. }
  44. void put(const K& key, const V& value);
  45.  
  46. V get(const K& key) const;
  47.  
  48. int size() const;
  49.  
  50. Entry* sort() { // 采用中序遍历方法排序
  51. Entry* entry = new Entry[len];
  52. _sort(*this, entry);
  53. return entry;
  54. }
  55. };
  56.  
  57. template<typename K, typename V>
  58. int Item<K, V>::len = ;
  59.  
  60. template<typename K, typename V>
  61. int Item<K, V>::index = ;
  62.  
  63. template<typename K, typename V>
  64. void Item<K, V>::put(const K& key, const V& value) {
  65. if (key == _key) {
  66. _key = key;
  67. _value = value;
  68. } else if (key > _key) {
  69. if (_right == ) {
  70. Item* r = new Item(key, value);
  71. _right = r;
  72. } else {
  73. _right->put(key, value);
  74. }
  75. } else if (key < _key) {
  76. if (_left == ) {
  77. Item* l = new Item(key, value);
  78. _left = l;
  79. } else {
  80. _left->put(key, value);
  81. }
  82. }
  83. }
  84.  
  85. template<typename K, typename V>
  86. V Item<K, V>::get(const K& key) const {
  87. if (key == _key) {
  88. return _value;
  89. } else if (key < _key) {
  90. if (_left == ) {
  91. return ;
  92. } else {
  93. _left->get(key);
  94. }
  95. } else if (key > _key) {
  96. if (_right == ) {
  97. return ;
  98. } else {
  99. _right->get(key);
  100. }
  101. }
  102. }
  103. template<typename K, typename V>
  104. int Item<K, V>::size() const {
  105. return len;
  106. }
  107.  
  108. template<typename K, typename V>
  109. void Item<K, V>::_sort(Item& item, Entry entry[]) {
  110. if (item._left == ) {
  111. entry[index].str_k = item._key;
  112. entry[index].str_v = item._value;
  113. index++;
  114. } else {
  115. _sort(*item._left, entry);
  116. entry[index].str_k = item._key;
  117. entry[index].str_v = item._value;
  118. index++;
  119. }
  120. if (item._right == ) {
  121. return;
  122. } else {
  123. _sort(*item._right, entry);
  124. }
  125. }
  126.  
  127. #endif /* ITEM_H_ */

部分注释我已经下载的代码中,这里做几点说明:

(1)二叉树排序的原则是先形成树根,然后新进入的数据与树根比较。如果小于树根则形成新的二叉树结构并放在左侧形成左子树。反之形成右子树。如果左子树(右子树)已经存在则采取递归的方式插入。

(2)二叉树遍历的原则是先处理左子树,再处理树根,最后处理右子树。同样也需要采用递归查询。

(3)C++不同于Java,最好是自己实现拷贝构造和重载赋值函数。通常情况下,拷贝构造和重载赋值函数效果相同,但在本例中大家可以看到。重载赋值函数需要首先对自己的左右子树的指针析构再赋值。

(4)由于C++编译方式的不同,类中的静态常量是不可以直接赋值的。需要在声明之后再定义数据,并且定义的顺序也很重要。

C++ 模拟Map的更多相关文章

  1. js模拟Map对象,实现key---value

    js模拟Map对象,实现key---value 根据java中map的属性,实现key----value保存 function Map() { var struct = function (key, ...

  2. ES6之前模拟Map数据结构的写法

    在ES6之前JavaScript 里面本身没有map对象,但是用JavaScript的Array.Object来模拟实现Map的数据结构. 现在已经有Map对象了,这里记录一下之前的写法 Array方 ...

  3. POJ 3087 Shuffle'm Up (模拟+map)

    题目链接:http://poj.org/problem?id=3087 题目大意:已知两堆牌s1和s2的初始状态, 其牌数均为c,按给定规则能将他们相互交叉组合成一堆牌s12,再将s12的最底下的c块 ...

  4. POJ 3087 Shuffle'm Up【模拟/map/string】

    Shuffle'm Up Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14471 Accepted: 6633 Descrip ...

  5. [CSP-S模拟测试]:z(模拟+map+小根堆)

    题目背景 $\frac{1}{4}$遇到了一道水题,$eooooo$完全不会做,于是去请教小$D$.结果小$D$已经去了阿塞拜疆,于是,$\frac{1}{4}$只好来问你,这道题是这样的: 题目描述 ...

  6. HDU - 3347 Calculate the expression — 模拟 + map存变量

    传送门 题意:从输入开始,1.输入样例数:2.然后输入一组样例中的行数n:3.前n-1行为定义变量(之间使用空格隔开),只需要map存进去就可以了(这里有覆盖的情况,故使用mp["s&quo ...

  7. js 模拟java 中 的map

    //js模拟map Map = { obj : {}, put : function(key , value){ this.obj[key] = value; }, get : function(ke ...

  8. webservice返回值为Map类型的处理方法

    在写一个webservice的时候,方法的返回值是一个复杂类型,处理方法是写一个结果类(Javabean)作为返回值.想着webservice方法返回值为Map的没写过,然后就试着写了一个简单的Dem ...

  9. ES6中的Set和Map集合

    前面的话 在ES6标准制定以前,由于可选的集合类型有限,数组使用的又是数值型索引,因而经常被用于创建队列和栈.如果需要使用非数值型索引,就会用非数组对象创建所需的数据结构,而这就是Set集合与Map集 ...

随机推荐

  1. Delphi字符串简码

    从网上找的三个函数自己修改了下,在delphi7运行正常,unicode的版本不能用好像 输入:1abc天天 输出:1ABCTT unit UnitJM; interface uses SysUtil ...

  2. Ubuntu apt-get "Hash Sum mismatch" 问题解决方法

    参考:ubuntu: apt-get update的时候遇到"Hash Sum mismatch"错误 在安装Mininet的时候,apt-get update的时候遇到了这个问题 ...

  3. 解决svn working copy locked问题

    标题:working copy locked 提示:your working copy appears to be locked. run cleanup to amend the situation ...

  4. Autofac

    程序集准备 Assembly: Autofac/Autofac.Integration.Mvc/System.Web.Mvc/System.Web.Helpers/System.Web.WebPage ...

  5. 开发者所需要知道的 iOS 10 SDK 新特性

    转自:https://onevcat.com/2016/06/ios-10-sdk/ 写的很好啊.哈哈哈 总览 距离 iPhone 横空出世已经过去了 9 个年头,iOS 的版本号也跨入了两位数.在我 ...

  6. (java oracle)以bean和array为参数的存储过程及dao部分代码

    一.数据库部分 1.创建bean对象 CREATE OR REPLACE TYPE "QUARTZJOBBEAN" as object ( -- Author : Duwc -- ...

  7. Swift: 在Swift中桥接OC文件(自己创建的类文件、第三方库文件)

    一.介绍 随着Swift的逐渐成熟,使用swift开发或者混合开发已经成为了一个趋势,本身苹果公司也十分推荐使用Swift这门新语言.目前Swift已经更新到了3.0,估计没有多久4.0就要出来了.那 ...

  8. Android 2D Graphics学习 Region和Canvas裁剪

    1.首先介绍Region类 Region,中文意思即区域的意思,它表示的是canvas图层上的某一块封闭的区域. /**构造方法*/ public Region()  //创建一个空的区域 publi ...

  9. IE or Chrome can not use localhost, firefox can works.

    因为服务器开启'localhost:9999",使用IE无法登陆,firefox下正常. 遂查看cookie,果然没有写入. stackoverflow.com: "ie enab ...

  10. RabbitMq中的交换机

          Rabbitmq的核心概念(如下图所示):有虚拟主机.交换机.队列.绑定:                    交换机可以理解成具有路由表的路由程序,仅此而已.每个消息都有一个称为路由键 ...