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

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

//循环链表模板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;                               //这步毋漏
    }
}  

正常运行结果显示:循环链表示例:求解约瑟夫问题_第4张图片
数据结构需要大量的时间实践与研究,希望我们能够多花点时间练习,珍惜当前的时间,做任何一件事都应该有目标!

你可能感兴趣的:(数据结构与算法)