線性表之循環列表

相對於單鏈表,循環列表所不同的就是增加了一個指向尾節點的尾指針,且尾指針指向頭節點,這樣就使得這個鏈表可以一直不斷地循環了。實現方法上,與單鏈表雷同,所需要做的改變:數據成員處增加一個指向尾節點的尾指針,在成員函數處注意移動尾指針,在循環處需要把原先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))
  • 可以從任一節點出發遍歷整個鏈表(注意:可從任一節點出發不代表可以實現隨機儲存
  • 容易造成死循環(缺點),需要格外注意循環的條件

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

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