数据结构与算法--单向有序链表(C++实现)

前言

线性表是基本的数据结构。一般有数组和链表两种常用实现形式。本文用C++实现有序链表。是C++、数据结构及算法学习的一个小练习。供大家参考。

链表的操作

链表的操作主要有:插入节点(insertNode)、删除节点(deleteNode)、查找(search)、遍历等。插入节点就是按键值(key)的顺序插入数据节点。删除节点就是将链表中等于键值(key)的节点删除,并保持链表的排序。查找就是根据键值(key)查找到第一个满足键值的节点。遍历就是顺序访问所有或指定键值的节点。

数据结构和算法描述

节点的结构

节点(Node)分为两部分,数据和链接部分。数据部分存储数据,可以根据实际情况分为若干字段。本文数据部分分两个字段:key和data。key字段用于排序和查找节点。本文允许key重复,因此查找和删除节点要遍历所有相同key值的节点。data则存储节点的其它信息。链接部分就是一个指针,指向下一个节点。

算法描述

为表述方便,约定:dataPt为指向链表表头的指针,pt为指向待插入节点的指针,curPt为指向当前节点指针,prePt为指向当前节点的前导指针。

插入节点

(1) 如果链表是空,直接指向新节点: d a t a P t = p t dataPt=pt 返回;否则继续(2)。
(2) 当前指针 c u r P t curPt 指向链表首: c u r P t = d a t a P t curPt=dataPt 。若 ( p t > k e y < c u r P t > k e y ) (pt->key < curPt->key) ,则(插入链表首部): p t > n e x t = c u r P t ; d a t a P t = p t ; {pt->next = curPt; dataPt=pt;} 否则执行(3)。
(3) 移动 c u r P t curPt p r e P t prePt p r e P t = c u r P t ; c u r P t = c u r P t > n e x t ; { prePt=curPt; curPt=curPt->next; } 直到 ( c u r P t > k e y > p t > k e y n u l l p t r ! = c u r P t > n e x t ) (curPt->key > pt->key || nullptr!=curPt->next) 。如果不是链表末尾,则在当前位置插入节点: p t > n e x t = c u r P t ; p r e P t > n e x t = p t { pt->next=curPt; prePt->next=pt } ; 否则执行(4)(在链表尾部插入节点)。
(4) 在链表尾部插入节点: c u r P t > n e x t = p t curPt->next=pt

删除节点

删除链表中所有键值等于给定参数 k k 的节点:
(1) 如果链表是空,直接返回;否则继续(2)。
(2) 如果表头的 ( k e y = = k ) (key==k) ,连续删除表头直到 ( k e y ! = k ) (key!=k) ,或者到达表尾;返回。
(3) 如果(1)、(2)没有执行,说明需要删除的节点在链表中间或者不在链表中。 p r e P t prePt 指向表头, c u r P t curPt 指向其后继,连续移动 p r e P t prePt c u r P t curPt ,直到 ( c u r P t > k e y = = k ) (curPt->key==k) ,或者 c u r P t curPt 到达表尾。如果 c u r P t curPt 到达表尾,表明需要删除的节点不在链表中;否则连续删除 c u r P t curPt 节点,直到 ( c u r P t > k e y ! = k ) (curPt->key!=k) ,或者 c u r P t curPt 到达表尾。
(4)返回。

查找

查找链表中键值等于给定参数 k k 的第一个节点,返回指向这个节点的指针,或者查找失败返回空指针。
(1) c u r P t curPt 指向表头。
(2)当 c u r P t curPt 不是空,如果 ( c u r P t > k e y = = k ) (curPt->key == k) , 找到目标,跳出循环;否则 c u r P t curPt 指向下一节点。循环执行(2)直到 c u r P t curPt 为空。
(3)返回 c u r P t curPt

遍历

(1) c u r P t curPt 指向表头。
(2)当 c u r P t curPt 不是空时,输出节点; c u r P t curPt 指向下一节点。循环执行(2)直到 c u r P t curPt 为空。
(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
原创文章 8 获赞 9 访问量 4万+

猜你喜欢

转载自blog.csdn.net/yang_deyuan/article/details/105351001