前言
線性表是基本的數據結構。一般有數組和鏈表兩種常用實現形式。本文用C++實現有序鏈表。是C++、數據結構及算法學習的一個小練習。供大家參考。
鏈表的操作
鏈表的操作主要有:插入節點(insertNode)、刪除節點(deleteNode)、查找(search)、遍歷等。插入節點就是按鍵值(key)的順序插入數據節點。刪除節點就是將鏈表中等於鍵值(key)的節點刪除,並保持鏈表的排序。查找就是根據鍵值(key)查找到第一個滿足鍵值的節點。遍歷就是順序訪問所有或指定鍵值的節點。
數據結構和算法描述
節點的結構
節點(Node)分爲兩部分,數據和鏈接部分。數據部分存儲數據,可以根據實際情況分爲若干字段。本文數據部分分兩個字段:key和data。key字段用於排序和查找節點。本文允許key重複,因此查找和刪除節點要遍歷所有相同key值的節點。data則存儲節點的其它信息。鏈接部分就是一個指針,指向下一個節點。
算法描述
爲表述方便,約定:dataPt爲指向鏈表表頭的指針,pt爲指向待插入節點的指針,curPt爲指向當前節點指針,prePt爲指向當前節點的前導指針。
插入節點
(1) 如果鏈表是空,直接指向新節點: 返回;否則繼續(2)。
(2) 當前指針指向鏈表首:。若,則(插入鏈表首部): 否則執行(3)。
(3) 移動 和: 直到。如果不是鏈表末尾,則在當前位置插入節點:; 否則執行(4)(在鏈表尾部插入節點)。
(4) 在鏈表尾部插入節點: 。
刪除節點
刪除鏈表中所有鍵值等於給定參數的節點:
(1) 如果鏈表是空,直接返回;否則繼續(2)。
(2) 如果表頭的,連續刪除表頭直到,或者到達表尾;返回。
(3) 如果(1)、(2)沒有執行,說明需要刪除的節點在鏈表中間或者不在鏈表中。指向表頭,指向其後繼,連續移動和,直到,或者到達表尾。如果到達表尾,表明需要刪除的節點不在鏈表中;否則連續刪除節點,直到,或者到達表尾。
(4)返回。
查找
查找鏈表中鍵值等於給定參數的第一個節點,返回指向這個節點的指針,或者查找失敗返回空指針。
(1)指向表頭。
(2)當不是空,如果 , 找到目標,跳出循環;否則指向下一節點。循環執行(2)直到爲空。
(3)返回。
遍歷
(1)指向表頭。
(2)當不是空時,輸出節點;指向下一節點。循環執行(2)直到爲空。
(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