單鏈表的C++實現(采用模板類)


采用模板類實現的好處是,不用拘泥於特定的數據類型。就像活字印刷術,制定好模板,就可以批量印刷,比手抄要強多少倍!

此處不具體介紹泛型編程,還是着重敘述鏈表的定義和相關操作。 

 

 鏈表結構定義

定義單鏈表的結構可以有4方式。如代碼所示。

本文采用的是第4種結構類型

/* ************************************************************************
1、復合類:在Node類中定義友元的方式,使List類可以訪問結點的私有成員
************************************************************************
*/
class LinkNode
{
    friend  class LinkList;
private:
     int data;
    LinkNode *next;
};

class LinkList
{
public:
     // 單鏈表具體操作
private:
    LinkNode *head;
}; 

/* ************************************************************************
2、嵌套類:在List內部定義Node類,但是Node的數據成員放在public部分,使List
和Node均可以直接訪問Node的成員
************************************************************************
*/
class LinkList
{
public:
     // 單鏈表具體操作
private:
     class LinkNode
    {
     public:
         int data;
        LinkNode *next;
    };
    LinkNode *head;
}; 

/* ************************************************************************
3、繼承:在Node類中把成員定義為protected,然后讓List繼承Node類,這樣就可以
訪問Node類的成員了。
************************************************************************
*/
class LinkNode
{
protected:
     int data;
    LinkNode *next;
};

class LinkList :  public LinkNode
{
public:
     // 單鏈表具體操作
private:
    LinkNode *head;
}; 

/* ************************************************************************
4、直接用struct定義Node類,因為struct的成員默認為公有數據成員,所以可直接
訪問(struct也可以指定保護類型)。
************************************************************************
*/
struct LinkNode
{
     int data;
    LinkNode *next;
};

class LinkList
{
public:
     // 單鏈表具體操作
private:
    LinkNode *head;
}; 

 

單鏈表的模板類定義

使用模板類需要注意的一點是template<class T>必須定義在同一個文件,否則編譯器會無法識別。

如果在.h中聲明類函數,但是在.cpp中定義函數具體實現, 會出錯。所以,推薦的方式是直接在.h中定義。

/*  單鏈表的結點定義  */
template< class T>
struct LinkNode
{
    T data;
    LinkNode<T> *next;
    LinkNode(LinkNode<T> *ptr = NULL){next = ptr;}
    LinkNode( const T &item, LinkNode<T> *ptr = NULL)    
     // 函數參數表中的形參允許有默認值,但是帶默認值的參數需要放后面
    {
        next = ptr;
        data = item;
    }
};

/*  帶頭結點的單鏈表定義  */
template< class T>
class LinkList
{
public:
     // 無參數的構造函數
    LinkList(){head =  new LinkNode<T>;}
     // 帶參數的構造函數
    LinkList( const T &item){head =  new LinkNode<T>(item);}
     // 拷貝構造函數
    LinkList(LinkList<T> &List);
     // 析構函數
    ~LinkList(){Clear();}
     // 重載函數:賦值
    LinkList<T>&  operator=(LinkList<T> &List);
     // 鏈表清空
     void Clear();
     // 獲取鏈表長度
     int Length()  const;
     // 獲取鏈表頭結點
    LinkNode<T>* GetHead()  const;
     // 設置鏈表頭結點
     void SetHead(LinkNode<T> *p);
     // 查找數據的位置,返回第一個找到的滿足該數值的結點指針
    LinkNode<T>* Find(T &item);
     // 定位指定的位置,返回該位置上的結點指針
    LinkNode<T>* Locate( int pos);
     // 在指定位置pos插入值為item的結點,失敗返回false
     bool Insert(T &item,  int pos);
     // 刪除指定位置pos上的結點,item就是該結點的值,失敗返回false
     bool Remove( int pos, T &item);
     // 獲取指定位置pos的結點的值,失敗返回false
     bool GetData( int pos, T &item);
     // 設置指定位置pos的結點的值,失敗返回false
     bool SetData( int pos, T &item);
     // 判斷鏈表是否為空
     bool IsEmpty()  const;
     // 打印鏈表
     void Print()  const;
     // 鏈表排序
     void Sort();
     // 鏈表逆置
     void Reverse();
private:
    LinkNode<T> *head;
};

 

定位位置  

/* 返回鏈表中第pos個元素的地址,如果pos<0或pos超出鏈表最大個數返回NULL */
template<class T>
LinkNode<T>* LinkList<T>::Locate(int pos)
{
    int i = 0;
    LinkNode<T> *p = head;

    if (pos < 0)
        return NULL;

    while (NULL != p && i < pos)
    {
        p = p->next;
        i++;
    }
    
    return p;
}

 

插入結點

單鏈表插入結點的處理如圖 

 

 圖:單鏈表插入操作

要在p結點后插入一個新結點node,(1)要讓node的next指針指向p的next結點;(2)再讓p的next指向node結點(即斷開圖中的黑色實線,改成紅色虛線指向node)

接下來:node->next = p->next; p->next = node; 

template<class T>
bool LinkList<T>::Insert(T &item, int pos)
{
    LinkNode<T> *p = Locate(pos);
    if (NULL == p)
        return false;

    LinkNode<T> *node = new LinkNode<T>(item);
    if (NULL == node)
    {
        cerr << "分配內存失敗!" << endl;
        exit(1);
    }
    node->next = p->next;
    p->next = node;
    return true;
}

 

刪除結點

刪除結點的處理如圖:

 

圖:單鏈表刪除 

刪除pos位置的結點,如果這個位置不存在結點,則返回false;

如果找到對應結點,則通過實參item輸出要刪除的結點的數值, 然后刪除結點並返回true。

template<class T>
bool LinkList<T>::Remove(int pos, T &item)
{
    LinkNode<T> *p = Locate(pos);
    if (NULL == p || NULL == p->next)
        return false;

    LinkNode<T> *del = p->next;
    p->next = del->next;
    item = del->data;
    delete del;
    return true;
}

 

清空鏈表 

遍歷整個鏈表,每次head結點的next指針指向的結點,直到next指針為空。

最后保留head結點。 

template<class T>
void LinkList<T>::Clear()
{
    LinkNode<T> *p = NULL;

    //遍歷鏈表,每次都刪除頭結點的next結點,最后保留頭結點
    while (NULL != head->next)
    {
        p = head->next;
        head->next = p->next;   //每次都刪除頭結點的next結點
        delete p;
    }
}

 

求鏈表長度和打印鏈表

着兩個功能的實現非常相近,都是遍歷鏈表結點,不贅述。 

template<class T>
void LinkList<T>::Print() const
{
    int count = 0;
    LinkNode<T> *p = head;
    while (NULL != p->next)
    {
        p = p->next;
        std::cout << p->data << " ";
        if (++count % 10 == 0)  //每隔十個元素,換行打印
            cout << std::endl;
    }
}

template<class T>
int LinkList<T>::Length() const
{
    int count = 0;
    LinkNode<T> *p = head->next;
    while (NULL != p)
    {
        p = p->next;
        ++count;
    }
    return count;
} 

 

單鏈表倒置

單鏈表的倒置處理如圖: 

圖:單鏈表倒置 

(1)初始狀態:prev = head->next; curr = prev->next;

(2)讓鏈表的第一個結點的next指針指向空

(3)開始進入循環處理,讓next指向curr結點的下一個結點;再讓curr結點的next指針指向prev。即:next = curr->next; curr->next = prev; 

(4)讓prev、curr結點都繼續向后移位。即:prev = curr; curr = next;

(5)重復(3)、(4)動作,直到curr指向空。這時循環結束,讓haed指針指向prev,此時的prev是倒置后的第一個結點。即:head->next = prev;

template<class T>
void LinkList<T>::Reverse()
{
    LinkNode<T> *pre = head->next;
    LinkNode<T> *curr = pre->next;
    LinkNode<T> *next = NULL;

    head->next->next = NULL;
    while (curr)
    {
        next = curr->next;
        curr->next = pre;
        pre = curr;
        curr = next;
    }

    head->next = pre;
}

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM