数据结构 第29课 循环链表的实现------------------狄泰软件学院

文章目录

引用文章: 1

引用文章: 2

CircleList.h

#ifndef CIRCLELIST_H
#define CIRCLELIST_H

/*



*/




#include "LinkList.h"

namespace DTLib
{
    
    

template < typename T >
class CircleList : public LinkList<T>
{
    
    
protected:
    typedef typename LinkList<T>::Node Node;
        // typedef 简化一个类型,给一个类型新的名字 Node
        // 父类的Node和泛指类型和泛指类型牵扯上了关系,此处不可用,故而需要重新定义
        // LinkList<T>::Node 编译器不知道这是类型还是静态成员变量,需要关键字typename出场

    // 不能对0取余, 需要特殊处理
    int mod(int i) const
    {
    
    
        return (this->m_length == 0) ? 0 : (i % this->m_length);
    }


    // last获取最后一个节点的指针
    // position定位最后一个位置,再取next可以获取最后一个节点的指针
    Node* last() const
    {
    
    
        return this->position(this->m_length - 1)->next;
    }


    void last_to_first() const
    {
    
    
        last()->next = this->m_header.next;
    }

public:

    bool insert(const T &e)
    {
    
    
        return insert(this->m_length, e);
    }



    // 在位置为0处插入节点:   移动头节点指针和尾节点指针指向新节点,新节点的指针指向原来的首节点
    bool insert(int i, const T &e)
    {
    
    
        bool ret = true;

        i = i % (this->m_length + 1);   // 想要调用父类的insert来实现子类的insert,这就是为什么要取余的原因
        ret = LinkList<T>::insert(i, e);  //要用父类的insert函数实现子类的insert函数

        if(ret && (i == 0))
        {
    
    
            last_to_first();
        }
        return ret;
    }



    // 删除位置为0的节点: 移动头节点指针和尾节点指针指向要删除节点的下一个节点(1号节点), 销毁节点,重新编号
    bool remove(int i)
    {
    
    
        bool ret = true;
        i = mod(i);

        if(i == 0)
        {
    
    
            Node* toDel = this->m_header.next;  // 定义局部指针指向即将删除的节点(首节点)

            if( toDel != NULL )
            {
    
    
                this->m_header.next = toDel->next; // 异常安全
                this->m_length--;

                if( this->m_length > 0 )  // 在还有元素的情况下才能移动指针
                {
    
    
                    last_to_first();
                    if( this->m_current == toDel )  // 如果删除的时候刚好是this->m_current节点,移动一下指针
                    {
    
    
                        this->m_current = toDel->next;
                    }
                }
                else
                {
    
    
                    this->m_header.next = NULL;
                    this->m_current = NULL;
                }

                this->destroy(toDel);
            }
            else
            {
    
    
                ret = false;
            }
        }
        else
        {
    
    
            ret = LinkList<T>::remove(i);  // 删除非首节点
        }

        return ret;
    }

    bool set(int i, const T& e)
    {
    
    
        return LinkList<T>::set(mod(i), e);  // 注意: 由于是循环列表,i有可能非常大的一个值,要取余
    }

    T get(int i) const
    {
    
    
        return LinkList<T>::get(mod(i));
    }


    T get(int i, T& e) const
    {
    
    
        return LinkList<T>::get(mod(i), e);
    }

    // 用指针遍历每一个数据元素
    int find(const T &e) const
    {
    
    
       int ret = -1;

       Node* slider = this->m_header.next;

       for(int i=0; i<this->m_length; i++)
       {
    
    
           if(slider->value == e)
           {
    
    
               ret = i;
               break;
           }
           slider = slider->next;
       }
       return ret;



       /* 这样做可以吗?  先变成单链表,再变成循环列表,如此精妙?

       last()->next = NULL;
       ret = LinkList<T>::find(e);
       last_to_first();
       *
       * 这样做是不对的:
       * 分析:
       * 改变了循环列表的状态,在LinkList.h中分析

            int find(const T& e) const
            {
                int ret = -1;
                int i=0;
                Node* node = m_header.next;

                while(node != NULL)
                {
                    if(node->value == e)  // 是泛指类型所对应的值之间的比较,T可能是个类类型,要比较,肯定要重载操作符
                    {                     // 如果在相等操作符重载中抛出了异常,会怎样?
                                          // 会异常返回 到 ret = LinkList<T>::find(e);
                                          // 这里没有 try...catch(), 异常会接着返回,
                                          // last_to_first() 就没有机会被调用了,
                                          // find函数出了异常,循环链表就变成单链表了,改变了链表的状态和特性
                                          // (为了很好的移植性,不允许使用try...catch())
                                          //
                        ret = i;
                        break;
                    }
                    else
                    {
                        node = node->next;
                        i++;
                    }
                }
                return ret;
            }

       *
       *


        */

    }

    void clear()
    {
    
    
        while( this->m_length > 1 )
        {
    
    
            remove(1);  // 节点个数大于1,那就删掉下标为1的节点(效率高)
                        // 如果每次都是 remove(0),每次都要调用last_to_first();内部还有很多调用
                        // 会导致大量的移动
        }


        if( this->m_length == 1)
        {
    
    
            Node* toDel = this->m_header.next;

            this->m_header.next = NULL;
            this->m_length = 0;
            this->m_current = NULL;

            this->destroy(toDel);
        }


        /* 这样做可以吗?

        if( this->m_length > 0 )
        {
            last()->next = NULL;
            LinkList<T>::clear();    // 道理一样的,clear()如果发生了异常,当前的链表就不再是循环链表了
        }

        */


    }


    bool move(int i, int step)  //  重新实现遍历操作,把父类的遍历相关操作变成虚函数,需要实现就就两个
    {
    
    
        return LinkList<T>::move(mod(i), step);
    }

    bool end()
    {
    
    
        return (this->m_length == 0) || (this->m_current == NULL);
    }


    ~CircleList()
    {
    
    
         clear();
    }

};

}


#endif // CIRCLELIST_H

main.cpp


#include <iostream>
#include "CircleList.h"
#include "Exception.h"

using namespace std;
using namespace DTLib;

void josephus(int n, int s, int m) //n有多少个人玩这个游戏,s从第几个人开始报数,m报到多少
{
    
    
    CircleList<int> cl;

    for(int i = 1; i <= n; i++)
    {
    
    
        cl.insert(i);
    }

    cl.move(s-1, m-1);  //n-1是因为要从下标为0开始,m-1是因为1到3之间隔了两个,移动两次

    while(cl.length() > 0) // 重复自杀的过程
    {
    
    
        cl.next();

        cout << cl.current() << endl;

        cl.remove(cl.find(cl.current())); //删除自杀这个人的下标
    }
}

int main()
{
    
    
    josephus(41, 1, 3);   // 最后出来俩位置,,不会死的位置

    return 0;
}


猜你喜欢

转载自blog.csdn.net/dashuu/article/details/115359642
今日推荐