循环链表示例:求解约瑟夫问题

短命女 2022-06-07 00:12 94阅读 0赞

错误:Debug Assertion Failed!
这里写图片描述
今天花了好久才解决了本篇文章中的一个错误,代码bug当然是不可避免的,但是我们可以尽可能的优化他,减少他。
错误分析:主要原因是求解约瑟夫环的问题时,使用了我上一篇文章的循环单链表的模板类,但是我在实际求解中并没有用模板类中的Remove(删除)这个成员函数(不用是因为我定义的删除函数本身的局限性造成的),直接是
preNode = currNode;
currNode =currNode->link;
也就是直接把前后两个节点建立连接,再释放要删除的节点,那么问题来了,这个时候私有成员last(即始终指向循环单链表尾节点的指针)并没有得到更新,也就是说last指向的节点在早已经被delete(释放)了,在主函数main返回的时候,系统调用析构函数的时候会对last指向的节点再一次释放,图中错误就应运而生了!(这是我一个多小时的分析结果,如有不当,希望大牛指正!)。下面贴出错误处的代码:
析构函数
约瑟夫处理函数中的错误
好了,现在贴出整个约瑟夫问题的处理(包括循环单链表的模板类和主函数),约瑟夫环是循环单链表的一个重要应用,也是一个很实用的问题,希望大家都能够掌握,如有不当,万望指正、批评!
//模板类

  1. //循环链表模板class
  2. #include <iostream>
  3. using namespace std;
  4. template<class T>
  5. struct CircleLinkNode {
  6. T data;
  7. CircleLinkNode<T> *link;
  8. //构造函数
  9. CircleLinkNode(CircleLinkNode<T> * next = NULL) : link(next) {}
  10. CircleLinkNode(T d, CircleLinkNode<T> * next = NULL) : data(d), link(next) {}
  11. };
  12. template<class T>
  13. class CircleList {
  14. public:
  15. CircleList(const T & x); //构造函数
  16. CircleList(CircleList<T> & L); //复制构造函数
  17. ~CircleList(); //析构函数
  18. int Length() const; //计算循环链表的长度
  19. bool IsEmpty()
  20. {
  21. return first->link == first ? true : false; //判断是否是空表
  22. }
  23. CircleLinkNode<T> * getHead() const; //返回附加头节点的地址
  24. void setHead(CircleLinkNode<T> * p); //设置附加头节点的地址
  25. CircleLinkNode<T> * Search(T x); //搜索含数据x的节点
  26. CircleLinkNode<T> * Locate(int i); //搜索第i个元素的地址
  27. T getData(int i); //取出第i个元素的值
  28. void setData(int i, T & x); //用修改第i个元素的值
  29. bool Insert(int i, T & x); //在第i个元素后插入x
  30. bool Remove(int i, T & x); //删除第i个元素,用x保存删除的数据
  31. void createList(T endFlag); //创建单链表
  32. void outputList(int stopSequence);
  33. protected:
  34. CircleLinkNode<T> *first, *last; //头指针、尾指针
  35. };
  36. //函数定义
  37. template<class T>
  38. CircleList<T>::CircleList(const T & x) {
  39. //构造函数: 开辟头节点并赋值
  40. first = new CircleLinkNode<T>(x);
  41. first->link = first;
  42. last = first;
  43. }
  44. template<class T>
  45. CircleList<T>::~CircleList() {
  46. //释放相应的资源
  47. //if (last != first) {
  48. // delete last;
  49. //}
  50. delete first;
  51. }
  52. template<class T>
  53. CircleLinkNode<T> * CircleList<T>::getHead() const {
  54. //得到函数的头指针
  55. return first;
  56. }
  57. template<class T>
  58. int CircleList<T>::Length() const {
  59. //计算链表的长度
  60. CircleLinkNode<T> *calculLen = NULL;
  61. int Len = 0;
  62. calculLen = first->link;
  63. while (calculLen != first) { //当再一次循环到first时,返回计数长度
  64. Len++;
  65. calculLen = calculLen->link;
  66. }
  67. return Len;
  68. }
  69. template<class T>
  70. CircleList<T>::CircleList(CircleList<T> &L) {
  71. //复制构造函数
  72. int Len = Length(); //得到当前链表的长度
  73. CircleLinkNode<T> *create = NULL, *destData, *srcData;
  74. create = new CircleLinkNode<T>(0);
  75. if (!create) {
  76. cerr << "内存分配错误" << endl;
  77. exit(1);
  78. }
  79. last = create; //标记尾节点
  80. for (int i = 0; i < Len - 1; i ++) {
  81. create->link = first->link;
  82. first->link = create;
  83. create = new CircleLinkNode<T>(0);
  84. if (!create) {
  85. cerr << "内存分配错误" << endl;
  86. exit(1);
  87. }
  88. }
  89. //将L中的data域逐一copy到当前链表中
  90. copyData = first->link;
  91. srcData = L.first->link;
  92. while (srcData == L.first) {
  93. destData->data = srcData->data;
  94. destData = destData->link;
  95. srcData = srcData->link;
  96. }
  97. //构建循环链表
  98. last->link = first;
  99. }
  100. template<class T>
  101. CircleLinkNode<T> * CircleList<T>::Search(T x) {
  102. //查找数据域为x的节点并返回其地址
  103. CircleLinkNode<T> * current = NULL;
  104. current = first->link;
  105. while (current != first) {
  106. if (current->data == x) {
  107. break;
  108. }
  109. current = current->link;
  110. }
  111. return current;
  112. }
  113. template<class T>
  114. CircleLinkNode<T> * CircleList<T>::Locate(int i) {
  115. //定位函数, 返回第i个节点的地址,可供插入与删除函数用
  116. if (i < 0 || i > Length()) {
  117. cout << "数据不合法" << endl;
  118. cin >> i;
  119. return NULL;
  120. }
  121. CircleLinkNode<T> * current = NULL;
  122. current = first;
  123. for (int j = 0; j < i; j ++) {
  124. current = current->link;
  125. }
  126. return current;
  127. }
  128. template<class T>
  129. void CircleList<T>::createList(T endFlag) {
  130. //逆序创建单链表
  131. T inputData = 0;
  132. CircleLinkNode<T> *create = NULL;
  133. cin >> inputData;
  134. create = new CircleLinkNode<T>(inputData); //在循环外侧创建尾节点
  135. if (!create) {
  136. cerr << "内存分配错误" << endl;
  137. exit(1);
  138. }
  139. last = create; //标记尾节点
  140. while (inputData != endFlag) { //是否满足结束条件
  141. create->link = first->link; //建立链接
  142. first->link = create;
  143. cin >> inputData;
  144. create = new CircleLinkNode<T>(inputData); //依次建立其他节点
  145. if (!create) {
  146. cerr << "内存分配错误" << endl;
  147. exit(1);
  148. }
  149. }
  150. //单链表建立完成
  151. last->link = first; //构建循环链表
  152. }
  153. template<class T>
  154. T CircleList<T>::getData(int i) {
  155. CircleLinkNode<T> * current;
  156. current = Locate(i); //定位函数调用
  157. return current->data;
  158. }
  159. template<class T>
  160. void CircleList<T>::setData(int i, T & x) {
  161. //设置第i个节点的数据域为x
  162. CircleLinkNode<T> * current = NULL;
  163. current = Locate(i);
  164. current->data = x; //完成修改
  165. }
  166. template<class T>
  167. bool CircleList<T>::Insert(int i, T & x) {
  168. //在第i个节点后插入新的节点,并使其数据域赋值为x
  169. CircleLinkNode<T> * flagPtr;
  170. CircleLinkNode<T> * newNode = new CircleLinkNode<T>(x);
  171. if (!newNode) { //内存分配错误
  172. return false;
  173. }
  174. if (0 == i) { //插入在首位置
  175. newNode->link = first->link;
  176. first->link = newNode;
  177. }
  178. else {
  179. flagPtr = Locate(i);
  180. newNode->link = flagPtr->link;
  181. flagPtr->link = newNode;
  182. }
  183. //更新last指向的节点
  184. last = Locate(Length());
  185. return true;
  186. }
  187. template<class T>
  188. bool CircleList<T>::Remove(int i, T & x) {
  189. //删除第i个节点并将删除的数据存储在x当中
  190. CircleLinkNode<T> *preNode = NULL, *delNode = NULL;
  191. preNode = Locate(i - 1);
  192. delNode = Locate(i);
  193. preNode->link = delNode->link;
  194. delete delNode;
  195. return true;
  196. }
  197. template<class T>
  198. void CircleList<T>::outputList(int stopSequence) {
  199. if (first == first->link) {
  200. cout << "此为空表" << endl;
  201. return;
  202. }
  203. //输出循环链表,直到指定序号停止输出(方便测试循环特性)
  204. CircleLinkNode<T> * current = NULL;
  205. current = first->link;
  206. for (int i = 0; i < stopSequence; i ++) {
  207. cout << current->data << " ";
  208. current = current->link;
  209. if (current == first) {
  210. current = current->link; //跳过first的输出
  211. }
  212. }
  213. cout << endl;
  214. }

主函数及约瑟夫处理函数测试

  1. //循环链表实例,求解约瑟夫问题
  2. #include <iostream>
  3. #include "CircleList.cpp"
  4. using namespace std;
  5. template<class T>
  6. void Josephus(CircleList<T> & js, int n, int m);
  7. int main()
  8. {
  9. CircleList<int> circle(0);
  10. int nodesAmount = 0, delSequence = 0, data = 0, sotre;
  11. cout << "输入游戏人数和报数间隔 :" << endl;
  12. cin >> nodesAmount >> delSequence;
  13. for (int i = 0; i < nodesAmount; i ++) { //形成约瑟夫环
  14. data = i + 1;
  15. circle.Insert(i, data);
  16. }
  17. Josephus(circle, nodesAmount, delSequence); //解决约瑟夫问题
  18. system("pause");
  19. return 0;
  20. }
  21. template<class T>
  22. void Josephus(CircleList<T> & js, int n, int m) {
  23. //用来求解约瑟夫问题
  24. //参数 : 循环链表、节点数、节点的删除间隔
  25. CircleLinkNode<T> *preNode = NULL, *currNode = NULL; //定义两个标志节点
  26. preNode = js.getHead();
  27. currNode = preNode->link;
  28. for (int i = 0; i < n - 1; i ++) { //循环n - 1次,最后只剩下一个节点
  29. for (int j = 0; j < m - 1; j ++) { //每次进行一个报数循环
  30. if (currNode == js.getHead()) {
  31. preNode = currNode;
  32. currNode = currNode->link;
  33. }
  34. //开始删除报数后的节点
  35. preNode = currNode;
  36. currNode = currNode->link;
  37. if(currNode == js.getHead()) {
  38. preNode = currNode;
  39. currNode = currNode->link;
  40. }
  41. }
  42. cout << "删除的数据是 : " << currNode->data << endl;
  43. preNode->link = currNode->link;
  44. delete currNode;
  45. currNode = preNode->link; //这步毋漏
  46. }
  47. }

正常运行结果显示:这里写图片描述
数据结构需要大量的时间实践与研究,希望我们能够多花点时间练习,珍惜当前的时间,做任何一件事都应该有目标!

发表评论

表情:
评论列表 (有 0 条评论,94人围观)

还没有评论,来说两句吧...

相关阅读