错误:Debug Assertion Failed!
今天花了好久才解决了本篇文章中的一个错误,代码bug当然是不可避免的,但是我们可以尽可能的优化他,减少他。
错误分析:主要原因是求解约瑟夫环的问题时,使用了我上一篇文章的循环单链表的模板类,但是我在实际求解中并没有用模板类中的Remove(删除)这个成员函数(不用是因为我定义的删除函数本身的局限性造成的),直接是
preNode = currNode;
currNode =currNode->link;
也就是直接把前后两个节点建立连接,再释放要删除的节点,那么问题来了,这个时候私有成员last(即始终指向循环单链表尾节点的指针)并没有得到更新,也就是说last指向的节点在早已经被delete(释放)了,在主函数main返回的时候,系统调用析构函数的时候会对last指向的节点再一次释放,图中错误就应运而生了!(这是我一个多小时的分析结果,如有不当,希望大牛指正!)。下面贴出错误处的代码:
好了,现在贴出整个约瑟夫问题的处理(包括循环单链表的模板类和主函数),约瑟夫环是循环单链表的一个重要应用,也是一个很实用的问题,希望大家都能够掌握,如有不当,万望指正、批评!
//模板类
//循环链表模板class
#include
using namespace std;
template<class T>
struct CircleLinkNode {
T data;
CircleLinkNode *link;
//构造函数
CircleLinkNode(CircleLinkNode * next = NULL) : link(next) {}
CircleLinkNode(T d, CircleLinkNode * next = NULL) : data(d), link(next) {}
};
template<class T>
class CircleList {
public:
CircleList(const T & x); //构造函数
CircleList(CircleList & L); //复制构造函数
~CircleList(); //析构函数
int Length() const; //计算循环链表的长度
bool IsEmpty()
{
return first->link == first ? true : false; //判断是否是空表
}
CircleLinkNode * getHead() const; //返回附加头节点的地址
void setHead(CircleLinkNode * p); //设置附加头节点的地址
CircleLinkNode * Search(T x); //搜索含数据x的节点
CircleLinkNode * Locate(int i); //搜索第i个元素的地址
T getData(int i); //取出第i个元素的值
void setData(int i, T & x); //用修改第i个元素的值
bool Insert(int i, T & x); //在第i个元素后插入x
bool Remove(int i, T & x); //删除第i个元素,用x保存删除的数据
void createList(T endFlag); //创建单链表
void outputList(int stopSequence);
protected:
CircleLinkNode *first, *last; //头指针、尾指针
};
//函数定义
template<class T>
CircleList::CircleList(const T & x) {
//构造函数: 开辟头节点并赋值
first = new CircleLinkNode(x);
first->link = first;
last = first;
}
template<class T>
CircleList::~CircleList() {
//释放相应的资源
//if (last != first) {
// delete last;
//}
delete first;
}
template<class T>
CircleLinkNode * CircleList::getHead() const {
//得到函数的头指针
return first;
}
template<class T>
int CircleList::Length() const {
//计算链表的长度
CircleLinkNode *calculLen = NULL;
int Len = 0;
calculLen = first->link;
while (calculLen != first) { //当再一次循环到first时,返回计数长度
Len++;
calculLen = calculLen->link;
}
return Len;
}
template<class T>
CircleList::CircleList(CircleList &L) {
//复制构造函数
int Len = Length(); //得到当前链表的长度
CircleLinkNode *create = NULL, *destData, *srcData;
create = new CircleLinkNode(0);
if (!create) {
cerr << "内存分配错误" << endl;
exit(1);
}
last = create; //标记尾节点
for (int i = 0; i < Len - 1; i ++) {
create->link = first->link;
first->link = create;
create = new CircleLinkNode(0);
if (!create) {
cerr << "内存分配错误" << endl;
exit(1);
}
}
//将L中的data域逐一copy到当前链表中
copyData = first->link;
srcData = L.first->link;
while (srcData == L.first) {
destData->data = srcData->data;
destData = destData->link;
srcData = srcData->link;
}
//构建循环链表
last->link = first;
}
template<class T>
CircleLinkNode * CircleList::Search(T x) {
//查找数据域为x的节点并返回其地址
CircleLinkNode * current = NULL;
current = first->link;
while (current != first) {
if (current->data == x) {
break;
}
current = current->link;
}
return current;
}
template<class T>
CircleLinkNode * CircleList::Locate(int i) {
//定位函数, 返回第i个节点的地址,可供插入与删除函数用
if (i < 0 || i > Length()) {
cout << "数据不合法" << endl;
cin >> i;
return NULL;
}
CircleLinkNode * current = NULL;
current = first;
for (int j = 0; j < i; j ++) {
current = current->link;
}
return current;
}
template<class T>
void CircleList::createList(T endFlag) {
//逆序创建单链表
T inputData = 0;
CircleLinkNode *create = NULL;
cin >> inputData;
create = new CircleLinkNode(inputData); //在循环外侧创建尾节点
if (!create) {
cerr << "内存分配错误" << endl;
exit(1);
}
last = create; //标记尾节点
while (inputData != endFlag) { //是否满足结束条件
create->link = first->link; //建立链接
first->link = create;
cin >> inputData;
create = new CircleLinkNode(inputData); //依次建立其他节点
if (!create) {
cerr << "内存分配错误" << endl;
exit(1);
}
}
//单链表建立完成
last->link = first; //构建循环链表
}
template<class T>
T CircleList::getData(int i) {
CircleLinkNode * current;
current = Locate(i); //定位函数调用
return current->data;
}
template<class T>
void CircleList::setData(int i, T & x) {
//设置第i个节点的数据域为x
CircleLinkNode * current = NULL;
current = Locate(i);
current->data = x; //完成修改
}
template<class T>
bool CircleList::Insert(int i, T & x) {
//在第i个节点后插入新的节点,并使其数据域赋值为x
CircleLinkNode * flagPtr;
CircleLinkNode * newNode = new CircleLinkNode(x);
if (!newNode) { //内存分配错误
return false;
}
if (0 == i) { //插入在首位置
newNode->link = first->link;
first->link = newNode;
}
else {
flagPtr = Locate(i);
newNode->link = flagPtr->link;
flagPtr->link = newNode;
}
//更新last指向的节点
last = Locate(Length());
return true;
}
template<class T>
bool CircleList::Remove(int i, T & x) {
//删除第i个节点并将删除的数据存储在x当中
CircleLinkNode *preNode = NULL, *delNode = NULL;
preNode = Locate(i - 1);
delNode = Locate(i);
preNode->link = delNode->link;
delete delNode;
return true;
}
template<class T>
void CircleList::outputList(int stopSequence) {
if (first == first->link) {
cout << "此为空表" << endl;
return;
}
//输出循环链表,直到指定序号停止输出(方便测试循环特性)
CircleLinkNode * current = NULL;
current = first->link;
for (int i = 0; i < stopSequence; i ++) {
cout << current->data << " ";
current = current->link;
if (current == first) {
current = current->link; //跳过first的输出
}
}
cout << endl;
}
主函数及约瑟夫处理函数测试
//循环链表实例,求解约瑟夫问题
#include
#include "CircleList.cpp"
using namespace std;
template<class T>
void Josephus(CircleList & js, int n, int m);
int main()
{
CircleList<int> circle(0);
int nodesAmount = 0, delSequence = 0, data = 0, sotre;
cout << "输入游戏人数和报数间隔 :" << endl;
cin >> nodesAmount >> delSequence;
for (int i = 0; i < nodesAmount; i ++) { //形成约瑟夫环
data = i + 1;
circle.Insert(i, data);
}
Josephus(circle, nodesAmount, delSequence); //解决约瑟夫问题
system("pause");
return 0;
}
template<class T>
void Josephus(CircleList & js, int n, int m) {
//用来求解约瑟夫问题
//参数 : 循环链表、节点数、节点的删除间隔
CircleLinkNode *preNode = NULL, *currNode = NULL; //定义两个标志节点
preNode = js.getHead();
currNode = preNode->link;
for (int i = 0; i < n - 1; i ++) { //循环n - 1次,最后只剩下一个节点
for (int j = 0; j < m - 1; j ++) { //每次进行一个报数循环
if (currNode == js.getHead()) {
preNode = currNode;
currNode = currNode->link;
}
//开始删除报数后的节点
preNode = currNode;
currNode = currNode->link;
if(currNode == js.getHead()) {
preNode = currNode;
currNode = currNode->link;
}
}
cout << "删除的数据是 : " << currNode->data << endl;
preNode->link = currNode->link;
delete currNode;
currNode = preNode->link; //这步毋漏
}
}
正常运行结果显示:
数据结构需要大量的时间实践与研究,希望我们能够多花点时间练习,珍惜当前的时间,做任何一件事都应该有目标!