十字链表的AOI算法实现

AOI主要有九宫格、灯塔和十字链表的算法实现。本文阐述十字链表的实现和尝试。

1. 基本原理

根据二维地图,将其分成x轴和y轴两个链表。如果是三维地图,则还需要维护多一个z轴的链表。将对象的坐标值按照大小相应的排列在相应的坐标轴上面。

2. 基本接口

对对象的操作主要有以下三个接口:

  • add:对象进入地图;
  • leave:对象离开地图;
  • move:对象在地图内移动。

2. 算法实现

既然是链表,很自然地想到用线性表来实现。因为存在向前和向后找的情况,所以使用双链表实现。其实实现也是非常简单,就是两个双链表(这里以二维地图举例)。那么我们的节点需要四个指针,分布为x轴的前后指针,y轴的前后指针。


  
  
  1. // 双链表(对象)
  2. class DoubleNode
  3. {
  4. public:
  5. DoubleNode(string key, int x, int y)
  6. {
  7. this->key = key;
  8. this->x = x;
  9. this->y = y;
  10. xPrev = xNext = NULL;
  11. };
  12. DoubleNode * xPrev;
  13. DoubleNode * xNext;
  14. DoubleNode * yPrev;
  15. DoubleNode * yNext;
  16. string key; // 只是一个关键字
  17. int x; // 位置(x坐标)
  18. int y; // 位置(y坐标)
  19. private:
  20. };

下面是地图场景信息和接口。这里的实现比较粗略,是带头尾的的双链表,暂时不考虑空间占用的问题。类Scene有分别有一个头尾指针,初始化的时候会为其赋值,主要用DoubleNode类的指针来存储x轴和y轴的头尾。初始化的时候,将_head的next指针指向尾_tail;将_tail的prev指针指向_head


  
  
  1. // 地图/场景
  2. class Scene
  3. {
  4. public:
  5. Scene()
  6. {
  7. this->_head = new DoubleNode("[head]", 0, 0); // 带头尾的双链表(可优化去掉头尾)
  8. this->_tail = new DoubleNode("[tail]", 0, 0);
  9. _head->xNext = _tail;
  10. _head->yNext = _tail;
  11. _tail->xPrev = _head;
  12. _tail->yPrev = _head;
  13. };
  14. // 对象加入(新增)
  15. DoubleNode * Add(string name, int x, int y);
  16. // 对象离开(删除)
  17. void Leave(DoubleNode * node);
  18. // 对象移动
  19. void Move(DoubleNode * node, int x, int y);
  20. // 获取范围内的AOI (参数为查找范围)
  21. void PrintAOI(DoubleNode * node, int xAreaLen, int yAreaLen);
  22. private:
  23. DoubleNode * _head;
  24. DoubleNode * _tail;
  25. };
2.1. add(进入地图)

DoubleNode对象插入到十字链表中。x轴和y轴分别处理,处理方法基本一致。其实就是双链表的数据插入操作,需要从头开始遍历线性表,对比相应轴上的值的大小,插入到合适的位置。


  
  
  1. void _add(DoubleNode * node)
  2. {
  3. // x轴处理
  4. DoubleNode * cur = _head->xNext;
  5. while(cur != NULL)
  6. {
  7. if((cur->x > node->x) || cur==_tail) // 插入数据
  8. {
  9. node->xNext = cur;
  10. node->xPrev = cur->xPrev;
  11. cur->xPrev->xNext = node;
  12. cur->xPrev = node;
  13. break;
  14. }
  15. cur = cur->xNext;
  16. }
  17. // y轴处理
  18. cur = _head->yNext;
  19. while(cur != NULL)
  20. {
  21. if((cur->y > node->y) || cur==_tail) // 插入数据
  22. {
  23. node->yNext = cur;
  24. node->yPrev = cur->yPrev;
  25. cur->yPrev->yNext = node;
  26. cur->yPrev = node;
  27. break;
  28. }
  29. cur = cur->yNext;
  30. }
  31. }

假设可视范围为x轴2以内,y轴2以内,则运行:

  1. 分别插入以下数据a(1,5)、f(6,6)、c(3,1)、b(2,2)、e(5,3),然后插入d(3,3),按照x轴和y轴打印其双链表结果;
  2. 插入d(3,3)数据,求其可视AOI范围(如图,除了f(6,6),其它对象都在d的可视范围内)。

控制台结果(前8行):

步骤1结果图示:

步骤2结果图示:

2.2. leave(离开地图)和move(移动)

其实都是双链表的基本操作,断掉其相应的指针就好了。按理,是需要

move和leave操作如图,move是将d(3,3)移动到(4,4),然后再打印其AOI范围。

控制台结果:

移动后AOI范围图示:

3. 完整代码实例

  
  
  1. #include "stdafx.h"
  2. #include "stdio.h"
  3. #include <iostream>
  4. #include <string>
  5. using namespace std;
  6. // 双链表(对象)
  7. class DoubleNode
  8. {
  9. public:
  10. DoubleNode(string key, int x, int y)
  11. {
  12. this->key = key;
  13. this->x = x;
  14. this->y = y;
  15. xPrev = xNext = NULL;
  16. };
  17. DoubleNode * xPrev;
  18. DoubleNode * xNext;
  19. DoubleNode * yPrev;
  20. DoubleNode * yNext;
  21. string key;
  22. int x; // 位置(x坐标)
  23. int y; // 位置(y坐标)
  24. private:
  25. };
  26. // 地图/场景
  27. class Scene
  28. {
  29. public:
  30. Scene()
  31. {
  32. this->_head = new DoubleNode("[head]", 0, 0); // 带头尾的双链表(可优化去掉头尾)
  33. this->_tail = new DoubleNode("[tail]", 0, 0);
  34. _head->xNext = _tail;
  35. _head->yNext = _tail;
  36. _tail->xPrev = _head;
  37. _tail->yPrev = _head;
  38. };
  39. // 对象加入(新增)
  40. DoubleNode * Add(string name, int x, int y)
  41. {
  42. DoubleNode * node = new DoubleNode(name, x, y);
  43. _add(node);
  44. return node;
  45. };
  46. // 对象离开(删除)
  47. void Leave(DoubleNode * node)
  48. {
  49. node->xPrev->xNext = node->xNext;
  50. node->xNext->xPrev = node->xPrev;
  51. node->yPrev->yNext = node->yNext;
  52. node->yNext->yPrev = node->yPrev;
  53. node->xPrev = NULL;
  54. node->xNext = NULL;
  55. node->yPrev = NULL;
  56. node->yNext = NULL;
  57. };
  58. // 对象移动
  59. void Move(DoubleNode * node, int x, int y)
  60. {
  61. Leave(node);
  62. node->x = x;
  63. node->y = y;
  64. _add(node);
  65. };
  66. // 获取范围内的AOI (参数为查找范围)
  67. void PrintAOI(DoubleNode * node, int xAreaLen, int yAreaLen)
  68. {
  69. cout << "Cur is: " << node->key << "(" << node ->x << "," << node ->y << ")" << endl;
  70. cout << "Print AOI:" << endl;
  71. // 往后找
  72. DoubleNode * cur = node->xNext;
  73. while(cur!=_tail)
  74. {
  75. if((cur->x - node->x) > xAreaLen)
  76. {
  77. break;
  78. }
  79. else
  80. {
  81. int inteval = 0;
  82. inteval = node->y - cur->y;
  83. if(inteval >= -yAreaLen && inteval <= yAreaLen)
  84. {
  85. cout << "\t" << cur->key << "(" << cur ->x << "," << cur ->y << ")" << endl;
  86. }
  87. }
  88. cur = cur->xNext;
  89. }
  90. // 往前找
  91. cur = node->xPrev;
  92. while(cur!=_head)
  93. {
  94. if((node->x - cur->x) > xAreaLen)
  95. {
  96. break;
  97. }
  98. else
  99. {
  100. int inteval = 0;
  101. inteval = node->y - cur->y;
  102. if(inteval >= -yAreaLen && inteval <= yAreaLen)
  103. {
  104. cout << "\t" << cur->key << "(" << cur ->x << "," << cur ->y << ")" << endl;
  105. }
  106. }
  107. cur = cur->xPrev;
  108. }
  109. };
  110. // 调试代码
  111. void PrintLink() // 打印链表(从头开始)
  112. {
  113. // 打印x轴链表
  114. DoubleNode * cur = _head->xNext;
  115. while (cur != _tail)
  116. {
  117. cout << (cur->key) << "(" << (cur->x) <<"," << (cur->y) << ") -> " ;
  118. cur = cur->xNext;
  119. }
  120. cout << "end" << endl;
  121. // 打印y轴链表
  122. cur = _head->yNext;
  123. while (cur != _tail)
  124. {
  125. cout << (cur->key) << "(" << (cur->x) <<"," << (cur->y) << ") -> " ;
  126. cur = cur->yNext;
  127. }
  128. cout << "end" << endl;
  129. };
  130. private:
  131. DoubleNode * _head;
  132. DoubleNode * _tail;
  133. void _add(DoubleNode * node)
  134. {
  135. // x轴处理
  136. DoubleNode * cur = _head->xNext;
  137. while(cur != NULL)
  138. {
  139. if((cur->x > node->x) || cur==_tail) // 插入数据
  140. {
  141. node->xNext = cur;
  142. node->xPrev = cur->xPrev;
  143. cur->xPrev->xNext = node;
  144. cur->xPrev = node;
  145. break;
  146. }
  147. cur = cur->xNext;
  148. }
  149. // y轴处理
  150. cur = _head->yNext;
  151. while(cur != NULL)
  152. {
  153. if((cur->y > node->y) || cur==_tail) // 插入数据
  154. {
  155. node->yNext = cur;
  156. node->yPrev = cur->yPrev;
  157. cur->yPrev->yNext = node;
  158. cur->yPrev = node;
  159. break;
  160. }
  161. cur = cur->yNext;
  162. }
  163. }
  164. };
  165. // --------------------------------------------
  166. void main()
  167. {
  168. Scene scene = Scene();
  169. // 增加
  170. scene.Add("a", 1, 5);
  171. scene.Add("f", 6, 6);
  172. scene.Add("c", 3, 1);
  173. scene.Add("b", 2, 2);
  174. scene.Add("e", 5, 3);
  175. DoubleNode * node = scene.Add("d", 3, 3);
  176. scene.PrintLink();
  177. scene.PrintAOI(node, 2, 2);
  178. // 移动
  179. cout << endl << "[MOVE]" << endl;
  180. scene.Move(node, 4, 4);
  181. scene.PrintLink();
  182. scene.PrintAOI(node, 2, 2);
  183. // 删除
  184. cout << endl << "[LEAVE]" << endl;
  185. scene.Leave(node);
  186. scene.PrintLink();
  187. }

作者:Ron Ngai
出处:http://rondsny.github.io
关于作者:断码码农一枚。
欢迎转载,但未经作者同意须在文章页面明显位置给出原文连接
如有问题,可以通过rondsny#gmail.com 联系我,非常感谢。

        </div>
            </div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值