线性表之循环列表

相对於单链表,循环列表所不同的就是增加了一个指向尾节点的尾指针,且尾指针指向头节点,这样就使得这个链表可以一直不断地循环了。实现方法上,与单链表雷同,所需要做的改变:数据成员处增加一个指向尾节点的尾指针,在成员函数处注意移动尾指针,在循环处需要把原先p==NULL用来做判断条件的循环条件改变,用数据成员m_Length,来做判断(这一点有了数据成员m_Length后使得即使尾指针的next部分也不为NULL的情况下,用另一条件来做判断也很方便)。
以下就是实现代码:
节点部分的结构依然没变

#pragma once
template<typename T>
class Node
{
public:
    T data;
    Node *next;
};

类模板的实现部分

#pragma once

#include<iostream>
#include"Node.h";
using namespace std;

template<typename T>
class LinkList
{
public:
    LinkList(T a[], int n);      //有参构造函数 使用了头插法
    ~LinkList();          //析构函数
    int Length();         //返回单链表的长度
    T Get(int i);           //按位查找,查找第i个节点的元素
    int Locate(T x);        //按值查找,查找链表中第一个值为x的元素,并返回序号
    bool Insert(int i, T x);   //插入元素,在第i个位置插入值x
    bool Delete(int i);       //删除节点,删除第i个节点
    bool InsertHead(T x);    //头插法插入节点
    bool InsertTail(T x);    //尾插法插入节点
    void ListTraverse();      //遍历节点
    void printrear() { cout << endl << "尾指针为:" << rear->data << endl; }
private:
    Node<T> *first;         //头指针
    Node<T> *rear;        //尾指针
    int  m_Length;       //实际使用过程当中,添加多一个数据成员Length会更好
};


template<typename T>
LinkList<T>::LinkList(T a[], int n)     //有参构造函数,  使用头插法(注意点:头插法是将元素放在头结点的后面)
{
    first = new Node<T>;        //空链表的初始化,同无参构造函数
    first->next =first ;
    for (int i = 0; i<n; i++)      
    {
        Node<T> *s = new Node<T>;
        s->data = a[i];
        if (first->next==first)
        {
            first->next = s;
            rear = s;
            rear->next = first;
        }
        else
        {
            s->next = first->next;
            first->next = s;
        }
    }
    m_Length = n;
}
//template<typename T>
//LinkList::LinkList(T a[], int n)         //同样是有参构造函数,但是使用的是尾插法
//{
//      
//  first = new Node<T>;
//  first = rear;
//  Node<T> *r = first;      //将头指针赋值给变量r
//  for (int i = 0; i < n; i++)
//  {
//      Node<T> *s = new Node<T>;   
//      s->data = a[i];    //创建新节点,赋值
//      s->next = rear->next;   //尾指针赋值
//      rear->next = s;         //上链,将尾指针
//      rear = s;
//  }
//}

template<typename T>
bool LinkList<T>::InsertHead(T x)      //头插发插入数据
{
    Node<T> *s = new Node<T>;         //建立新节点
    if (s == NULL)         //判断新节点是否申请成功
    {
        return false;
    }
    s->data = x;         //赋值
    if (first->next==first)
    {
        first->next = s;
        rear = s;
        rear->next = first;
    }
    else
    {
        Node<T> *Temp = first->next;           //建立指向头指针的临时变量
        s->next = Temp;      //上链  
        first->next = s;
    }
    m_Length++;       //链表的长度加一
    return true;       //插入成功,返回true
}

template<typename T>
bool LinkList<T>::InsertTail(T x)      //使用尾插法插入数据,使数据插入到最后一个
{
    Node<T> *s = new Node<T>;   //建立新节点
    if (s == NULL)      //判断新节点是否申请成功,若申请失败,则退出函数,不插入数据
    {
        return false;
    }
    s->data = x;
    s->next = rear->next;
    rear->next = s;
    rear = s;
    m_Length++;
    return true;      //返回true,插入成功
}

template<typename T>
LinkList<T>::~LinkList()         //析构函数
{
    int i = 0;
    while (i<m_Length)
    {
        Node<T> *q = first;        //遍历删除头指针指向的节点,将头指针暂存
        first = first->next;      //将头指针后移
        delete q;        //从链表中脱离出来的指针删除,释放内存
        i++;
    }
    m_Length = 0;
}
template<typename T>
int LinkList<T>::Length()          /*返回链表的长度的算法,实现思想:设定循环函数,将节点的指针从头指针开始依次后移,
                                   后移一次便将计数器自加1,直至到尾节点的指针为空,此时结束循环,返回计数器*/
{
    /*int num=0;
    Node<T> *p = first->next;
    while (p!= NULL)
    {
    p = p->next;
    num++;
    }*/
    return m_Length;   //添加数据成员length后,使得返回链表的长度函数更简单,代码更少  
                       /*return num;*/
}
template<typename T>
T LinkList<T>::Get(int i)    //按位查找,返回第i个节点的元素
{
    Node<T> *p = first->next;
    int count = 1;
    if (i>m_Length)
    {
        cout << "无此节点(节点位置超出当前链表长度)" << endl;
    }
    else
    {
        while (count < i)
        {
            p = p->next;
            count++;
        }
        return p->data;
    }
}
template<typename T>
int LinkList<T>::Locate(T x)          //按值查找,返回d第一个匹配值的序号
{
    Node<T> *p = first;
    int count = 0,i=0;
    while (i<=m_Length)
    {
        if (p->data == x)
        {
            return count;
        }
        p = p->next;
        count++;
        i++;
    }
    return -1;     //返回-1表示没找到
}
template<typename T>
bool LinkList<T>::Insert(int i, T x)      //往链表中插入元素,i为要插入的位置,x为要插入的值
{
    Node<T> *p = first;
    int count = 0;
    int num = i - 1,j=0;
    if (j>=m_Length)       //要插入的位置大于当前链表的长度,将会把数据插入到链表尾中   return false代表插入到尾部
    {
        Node<T> *s = new Node<T>;
        s->data = x;
        s->next = rear->next;
        rear->next = s;
        rear = s;
        m_Length++;  
        return false;
    }
    else                  
    {
        while (count <num)
        {
            p = p->next;
            count++;
        }
        Node<T> *s = new Node<T>;
        s->data = x;
        s->next = p->next;
        p->next = s;
        m_Length++;
        return true;      //return true代表插入到了目标位置
    }

}
template<typename T>
void LinkList<T>::ListTraverse()            //遍历函数
{
    Node<T> *p = first->next;
    int i = 0;
    while (i<m_Length)
    {
        cout << p->data << ",";
        p = p->next;              //遍历的指针的后移,注意不能写成p++,因为这是节点
        i++;
    }
}
template<typename T>
bool LinkList<T>::Delete(int i)
{
    Node<T> *p = first;
    int count = 0;
    if (i>m_Length)
    {
        return false;      //删除目标的节点大于当前链表的长度(节点不存在),直接返回
    }
    else
    {
        while (count < i - 1)
        {
            p = p->next;
            count++;
        }
        Node<T> *q;
        q = p->next;
        p->next = q->next;
        delete q;
        m_Length--;
        return true;
    }
}

实例调用:

#include<iostream>
#include"CircuList.h"
using namespace std;

int main()
{
    int a[5] = { 1,2,3,4,5 };
    LinkList<int>  MyList(a, 5);
    cout <<"链表长度为:"<< MyList.Length() << endl;         //链表长度函数调用成功
    cout << "第5个节点的元素为:" << MyList.Get(5) << endl;     //输入有效长度的数据可成功调用,但是,若输入不合法的数据将出错,throw的语句未完成
    if (MyList.Locate(5) != 0)
    {
        cout << "元素5所在的位置为:" << MyList.Locate(5) << endl;
    }    //输入合法的数据,测试成功
    else if (MyList.Locate(5) == 0) {
        cout << "输入数据不合法,输入的节点位置超过链表超度" << endl;
    }
    if (MyList.Insert(3, 2))
    {
        cout << "插入元素成功!" << endl;
    }//插入元素测试成功
    else
    {
        cout << "插入元素失败!" << endl;
    }
    MyList.ListTraverse();
    if (MyList.Delete(3))
    {
        cout << "删除节点成功" << endl;
    }
    else
    {
        cout << "删除节点失败" << endl;
    }
    MyList.ListTraverse();     //单链表的遍历成功
    cout << endl;
    MyList.InsertHead(7);       //调用头插法
    cout << "调用头插法成功!";
    MyList.ListTraverse();
    MyList.InsertTail(4);     //调用尾插法
    cout << endl;
    cout << "调用尾插法成功!";
    MyList.ListTraverse();
    return 0;
}

循环单链表的实例调用结果
可见,该结果与上一单链表的结果相同(输入样本数据相同)

单链表特点:

  • 查找开始节点和尾节点都变得特别方便(时间复杂度下降了,为o(1))
  • 可以从任一节点出发遍历整个链表(注意:可从任一节点出发不代表可以实现随机储存
  • 容易造成死循环(缺点),需要格外注意循环的条件

以上!如果日后遇到其他需要注意的(新奇的,好玩的),会更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章