數據結構與算法--單向有序鏈表(C++實現)

前言

線性表是基本的數據結構。一般有數組和鏈表兩種常用實現形式。本文用C++實現有序鏈表。是C++、數據結構及算法學習的一個小練習。供大家參考。

鏈表的操作

鏈表的操作主要有:插入節點(insertNode)、刪除節點(deleteNode)、查找(search)、遍歷等。插入節點就是按鍵值(key)的順序插入數據節點。刪除節點就是將鏈表中等於鍵值(key)的節點刪除,並保持鏈表的排序。查找就是根據鍵值(key)查找到第一個滿足鍵值的節點。遍歷就是順序訪問所有或指定鍵值的節點。

數據結構和算法描述

節點的結構

節點(Node)分爲兩部分,數據和鏈接部分。數據部分存儲數據,可以根據實際情況分爲若干字段。本文數據部分分兩個字段:key和data。key字段用於排序和查找節點。本文允許key重複,因此查找和刪除節點要遍歷所有相同key值的節點。data則存儲節點的其它信息。鏈接部分就是一個指針,指向下一個節點。

算法描述

爲表述方便,約定:dataPt爲指向鏈表表頭的指針,pt爲指向待插入節點的指針,curPt爲指向當前節點指針,prePt爲指向當前節點的前導指針。

插入節點

(1) 如果鏈表是空,直接指向新節點:dataPt=ptdataPt=pt 返回;否則繼續(2)。
(2) 當前指針curPtcurPt指向鏈表首:curPt=dataPtcurPt=dataPt。若(pt>key<curPt>key)(pt->key < curPt->key),則(插入鏈表首部):pt>next=curPt;dataPt=pt;{pt->next = curPt; dataPt=pt;} 否則執行(3)。
(3) 移動curPtcurPtprePtprePtprePt=curPt;curPt=curPt>next;{ prePt=curPt; curPt=curPt->next; } 直到(curPt>key>pt>keynullptr!=curPt>next)(curPt->key > pt->key || nullptr!=curPt->next)。如果不是鏈表末尾,則在當前位置插入節點:pt>next=curPt;prePt>next=pt{ pt->next=curPt; prePt->next=pt }; 否則執行(4)(在鏈表尾部插入節點)。
(4) 在鏈表尾部插入節點: curPt>next=ptcurPt->next=pt

刪除節點

刪除鏈表中所有鍵值等於給定參數kk的節點:
(1) 如果鏈表是空,直接返回;否則繼續(2)。
(2) 如果表頭的(key==k)(key==k),連續刪除表頭直到(key!=k)(key!=k),或者到達表尾;返回。
(3) 如果(1)、(2)沒有執行,說明需要刪除的節點在鏈表中間或者不在鏈表中。prePtprePt指向表頭,curPtcurPt指向其後繼,連續移動prePtprePtcurPtcurPt,直到(curPt>key==k)(curPt->key==k),或者curPtcurPt到達表尾。如果curPtcurPt到達表尾,表明需要刪除的節點不在鏈表中;否則連續刪除curPtcurPt節點,直到(curPt>key!=k)(curPt->key!=k),或者curPtcurPt到達表尾。
(4)返回。

查找

查找鏈表中鍵值等於給定參數kk的第一個節點,返回指向這個節點的指針,或者查找失敗返回空指針。
(1)curPtcurPt指向表頭。
(2)當curPtcurPt不是空,如果 (curPt>key==k)(curPt->key == k), 找到目標,跳出循環;否則curPtcurPt指向下一節點。循環執行(2)直到curPtcurPt爲空。
(3)返回curPtcurPt

遍歷

(1)curPtcurPt指向表頭。
(2)當curPtcurPt不是空時,輸出節點;curPtcurPt指向下一節點。循環執行(2)直到curPtcurPt爲空。
(3)返回。

類的定義

鏈表定義成一個類,其上定義了構造函數、析構函數、插入節點(insertNode)、刪除節點(deleteNode)、查找(search)、遍歷(重載“<<”操作符)、輸出指定鍵值節點(printData)、判斷空鏈表函數(isEmpty())、讀取數據readData(filename) —— 從數據文件中讀入實驗數據並生成鏈表等操作,以及指向鏈表頭節點的指針dataPt。C++代碼如下:

class DataChain
{
    friend std::ostream &operator<<(std::ostream &output, const DataChain &daCh);
    public:
        DataChain();
        DataChain(const DataChain &da); //copy construct
        DataChain &operator=(const DataChain &rhs); //assignment
        virtual ~DataChain();
        bool isEmpty() { return nullptr==dataPt; }
        void readData(std::string fn);
        void printData(int k);
        void insertNode(Node *pt);
        void deleteNode(int k);
        Node *searchData(int k);
    protected:
    private:
        Node *dataPt;
};

程序清單

主程序則對鏈表的操作做了相應試驗。全部程序清單如下:

#include <iostream>
#include <fstream>
using namespace std;
struct Node
{
    int key;
    std::string data;
    Node *next;
    Node(int k, std::string da, Node *npt=nullptr) : key(k), data(da), next(npt) {};
};
class DataChain
{
    friend std::ostream &operator<<(std::ostream &output, const DataChain &daCh);
    public:
        DataChain();
        DataChain(const DataChain &da); //copy construct
        DataChain &operator=(const DataChain &rhs); //assignment
        virtual ~DataChain();
        bool isEmpty() { return nullptr==dataPt; }
        void readData(std::string fn);
        void printData(int k);
        void insertNode(Node *pt);
        void deleteNode(int k);
        Node *searchData(int k);
    protected:
    private:
        Node *dataPt;
};
DataChain::DataChain()
{
    //ctor
    dataPt=nullptr;
}
DataChain::DataChain(const DataChain &da) //copy construct
{
    dataPt=da.dataPt;
}
DataChain& DataChain::operator=(const DataChain &rhs) //assignment
{
     if (this == &rhs) return *this; // handle self assignment
    //assignment operator
    dataPt=rhs.dataPt;
    return *this;
}
DataChain::~DataChain()
{
    //dtor
    Node *curPt, *nextPt;
    curPt=dataPt;
    while (curPt!=nullptr) {
        nextPt=curPt->next;
        delete curPt;
        curPt=nextPt;
    }
    dataPt=nullptr;
}
void DataChain::readData(std::string fn)
{
    int k;
    std::string str;
    std::ifstream dataFile(fn);
    if (!dataFile)
    {
        std::cout << "404, file not found.";
        exit (1);
    }
    while (dataFile >> k >> str)
    {
        insertNode(new Node(k, str, nullptr)); // insert and sorted by key
    }
    dataFile.close();
    return;
}
void DataChain::printData(int k)
{
   Node *pt;
   pt=searchData(k);
   while ( nullptr!=pt && pt->key == k ) {
        std::cout<<"("<<pt->key<<", "<<pt->data<<")";
        pt=pt->next;
   }
   return;
}
std::ostream &operator<<(std::ostream &output, const DataChain &daCh)
{
   Node *pt=daCh.dataPt;

   if (nullptr==pt) output<<" <Null> ";
   else {
        while (nullptr!=pt) {
            output<<" ("<<pt->key<<", "<<pt->data<<") ";
            pt=pt->next;
        }
   }
   return output;
}
void DataChain::insertNode(Node *pt)
{
    Node *prePt=nullptr, *curPt=nullptr;

    if (isEmpty()) { //empty
        dataPt=pt;
    }
    else {
        curPt=dataPt;
        if ( curPt->key > pt->key ) { //first
            pt->next = curPt;
            dataPt=pt;
        }
        else {
            while (curPt->key <= pt->key && nullptr!=curPt->next) { //move
                prePt=curPt;
                curPt=curPt->next; //shiftNext(curPt);
            }
            if (curPt->key > pt->key) { //middle
                pt->next=curPt;
                prePt->next=pt;
            }
            else { //tail
                curPt->next=pt;
            }
        }
    }
    return;
}
void DataChain::deleteNode(int k)
{
    Node *prePt=dataPt, *curPt;

    if (nullptr!=prePt) { curPt=prePt->next; }
    else return; //empty

    while (nullptr!=prePt && prePt->key == k) { //delete head
        delete prePt;
        prePt=curPt;
        if (nullptr!=curPt) curPt=curPt->next;
        dataPt=prePt;
    }
    while (nullptr!=curPt) {
        if (curPt->key == k) {
            prePt->next=curPt->next;
            delete curPt;
            curPt=prePt->next;
        }
        else {
            prePt=prePt->next;
            if (nullptr!=curPt) curPt=curPt->next;
        }
    }
    return;
}
Node* DataChain::searchData(int k)
{
    Node *curPt=dataPt;
    while (nullptr!=curPt) {
        if (curPt->key == k) { break; }
        else { curPt=curPt->next; //shiftNext(pt);
        }
    }
    return curPt;
}
int main()
{
    DataChain dc;
    int k=0;
    cout << "Read data ... \n" << endl;
    dc.readData("datafile.txt");
    cout<<dc<<"\n";
    //Search and list data
    for (k=0; k!=30; k++) {
        cout << "\nSearch "<<k<<" ... ";
        dc.printData(k);
    }
    //delete nodes
    for (k=0; k!=30; k++) {
        cout << "\n\nSearch "<<k<<" ... ";
        if (nullptr!=dc.searchData(k)) {
            dc.printData(k);
            cout << "\nDelete "<<k<<" ... " << endl;
            dc.deleteNode(k);
            cout<<dc<<endl;
        }
        else cout << "Not found."<<endl;
    }
    return 0;
}

供試驗用的數據文件,datafile.txt。內容如下:

24 Xyz
2 Bcd
3 Cde
1 Abc
4 Def
10 Exa
22 Vwx
7 Ghi
8 Hij
9 Ijk
1 A
10 Jkl
5 Efg
24 Xz
11 Klm
12 Lmn
26 Z
13 Mno
25 Sam
1 Ab
14 Nop
6 Fgh
15 Opq
16 Pqr
1 bc
18 Rst
19 Stu
20 Tuv
24 Xy
21 Txt
17 Qrs
21 Uvw
23 Wxy
25 Yz
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章