前言
线性表是基本的数据结构。一般有数组和链表两种常用实现形式。本文用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