数据结构与算法C++描述(4)---链表

上文“数据结构C++描述—线性表的基本操作”中所述的线性表是基于公式化描述来存储数据的,即它的存储位置之间满足一定的数学关系。而利用链表描述线性表时,某个元素的描述包含两部分:链接域(指向下一个或上一个节点)和数据域(存储当前元素的值)。因此,链表利用链接指针将各个数据连接起来。
链表包括:
1. 单链表:只有指向下一个元素的指针,最后一个节点链接域为NULL(或0)如下图所示(图片来源于文末参考文献)
图1 单链表
2. 循环链表:在单链表头指针前面新增一个节点作为头结点(*first),将最后一个节点的链接指针改为指向头结点 *first,就构成了循环链表。(如下图所示,图片来源于参考文献)
图2  循环链表
3. 双向链表:对于双向链表,链接域中有两个链接指针*left(指向前一个节点,如果有)和 *right(指向下一个节点,如果有)。(如下图所示,图片来源于参考文献)
这里写图片描述

下文重点讲述单链表的操作。


1、单链表的操作主要包括:

这里写图片描述

2、链表各种操作的C++实现

链表抽象为Chain链表操作类和ChainNode节点类。链表的创建和删除利用Chain()类的构造函数Chain()和析构函数~Chain()实现。Chain()类有一个私有成员变量,头节点指针*first,在创建链表时,初始化为0;ChainNode类具有两个私有成员变量,分别存储节点的数据(data)和指向下一个节点的指针 (link)。由于Chain()类需要访问ChainNode类的私有成员,因此,将Chain类声明为ChainNode类的友类。两个类的具体声明如下:

template <class T> class Chain;
//节点类---ChainNode
template <class T>
class ChainNode
{
    friend Chain<T>;    //由于Chain()类需要访问ChainNode类的私有成员,因此,声明为Chain类的友类
private:
    T data;
    ChainNode<T> *link;
};

//链表操作类---Chain
template <class T>
class Chain{

public:
    Chain(){first=0;};                      //创建链表
    ~Chain();                               //删除链表

    bool IsEmpty() const{return first==0;}  //判断链表是否为空,即首节点是否等于末节点
    int Length() const;                     //链表长度,即链表中节点个数
    bool Find(int k,T &x) const;            //寻找第k个位置的元素,若存在,返回true,并赋给x;若不存在,返回false
    int Search(const T &x) const;           //返回链表中元素x的位置,若不存在,返回0
    Chain<T>& Delete(int k,T &x);           //删除第k个位置的元素,并将它赋给x
    Chain<T>& Insert(int k,const T &x);     //在第k个位置后插入x
    void Output(ostream &out) const;        //输出链表---运算符重载

    Chain<T>& Erase();                      //删除表中所有节点
    void Zero();                            //将first指针清零
    Chain<T>& Append(T x);                  //在链表末尾追加一个数据

private:
    ChainNode<T> *first;                    //指向第一个节点的指针
};

2.1 删除链表—Chain类的析构函数~Chain()

//析构函数
template <class T>
Chain<T>::~Chain()
{
    ChainNode<T> *next;      //下一个节点
    while(first)
    {
        next=first->link;   //指向下一个节点
        delete first;       //删除当前节点
        first=next;         //循环
    }

}    

2.3 返回链表长度

//返回链表长度
template <class T>
int Chain<T>::Length() const
{
    ChainNode<T> *current=first;
    int len=0;
    while(current)
    {
        len++;
        current=current->link;
    }
    return len;
}

2.3 寻找第k个位置的元素

//寻找第k个位置的元素,若存在,返回true,并赋给x;若不存在,返回false
template <class T>
bool Chain<T>::Find(int k,T &x) const
{
    if(k<0)
        throw OutOfRange();
    ChainNode<T> *current=first;           //初始化current
    for(int i=1;i<k && current;i++)        //移动到第k个位置
        current=current->link; 
    if(current) 
    {
        x=current->data;                  //获取当前数据
        return true;
    }
    else
        return false;
}

2.4 返回链表中元素x的位置

//返回链表中元素x的位置,若不存在,返回0
template <class T>
int Chain<T>::Search(const T &x) const
{
    ChainNode<T> *current;         
    int i=1;                                        //链表元素位置坐标
    //遍历链表
    for(current=first;current;current=current->link)
    {
        if(current->data==x)
            return i;
        i++;
    }
    return 0;
}

2.5 删除第k个位置的元素

//删除第k个位置的元素,并将它赋给x
template <class T>
Chain<T>& Chain<T>::Delete(int k,T &x)
{
    if(k<=0)   throw OutOfRange();
    ChainNode<T> *current=first;
    if(k==1)                            //删除第一个元素
        first=first->link;              //将第2个元素作为链表首端
    else                                //删除非首端元素,包括中间元素和末尾元素
    {
        for(int i=1;i<k-1 && current;i++)
            current=current->link;             //移动到第k-1个位置,出去末尾
        ChainNode<T> *previous=current;        //保存current前一个节点
        current=current->link;                 //第k个节点
        x=current->data;                       //将第k个位置上的元素赋给x
        if(current)                            //若current不是末节点
        {
            ChainNode<T> *After=current->link; //保存current后一个节点
            delete current;                    //删除current节点
            previous->link=After;              //连接current前后的两个节点
        }
        else                                   //若是末节点
            delete current;
    }
    return *this;
}

2.6 在第k个位置后插入元素

//在第k个位置后插入元素
template<class T>
Chain<T>& Chain<T>::Insert(int k,const T &x) 
{
    if(k<0)                                    //k设置错误
        throw OutOfRange();
    ChainNode<T> *current=first;
    for(int i=1;i<k && current;i++)           //移动到第k个位置
        current=current->link;
    if(k>0 && !current) throw OutOfRange();    //不存在第k个元素
    ChainNode<T> *y=new ChainNode<T>;          //新建节点
    y->data=x;                                 //将x赋给新建节点的data
    //在正确位置插入新节点
    if(k)                                      //k不为0
    {
        y->link=current->link;                 //指向原来的第k+1个节点
        current->link=y;                       //原来第k个节点指向新建节点
    }
    else                                       //k=0,在链表首端插入
    {
        y->link=first;                         //指向首端
        first=y;                               
    }
    return *this;
}

2.7 删除链表中所有节点

template<class T>
Chain<T>& Chain<T>::Erase()
{
    ChainNode<T> *next;
    while(first)
    {
        next=first->link;
        delete first;
        first=next;     
    }
    return *this;
}

2.8 将first指针清零

template <class T>
void Chain<T>::Zero()
{
    first=0;
}

2.9 在链表末尾追加一个数据

//在链表末尾追加一个数据
template <class T>
Chain<T>& Chain<T>::Append(T x)
{
    int n=Length();
    Insert(n,x); 
    return *this;
}

2.10 输出链表—运算符重载

template <class T>
void Chain<T>::Output(ostream &out) const
{
    ChainNode<T> *current;
    for(current=first;current;current=current->link)
        out<<current->data<<"  ";
}
//重载“<<”
template <class T>
ostream & operator<<(ostream &out,const Chain<T> &x)
{
    x.Output(out);
    return out;
}

3 测试数据

    Chain<int> c;
        cout<<"新建链表:      "<<endl;
        c.Length();
        cout<<"链表是否为空:  "<<c.IsEmpty()<<endl;
        cout<<"链表的长度:    "<<c.Length()<<endl;
        cout<<"链表为:   "<<c<<endl<<endl;

        //测试Insert()函数
        c.Insert(0,1).Insert(1,2).Insert(2,3).Insert(3,4);
        cout<<"测试Insert()函数---Insert(0,1).Insert(1,2):"<<endl;
        cout<<"链表是否为空:  "<<c.IsEmpty()<<endl;
        cout<<"链表的长度:    "<<c.Length()<<endl;
        cout<<"链表为:   "<<c<<endl<<endl;

        //测试Find()函数
        int x;
        c.Find(2,x);
        cout<<"测试Find()函数---c.Find(2,x):"<<endl;
        cout<<"x为:   "<<x<<endl<<endl;

        //测试Search()
        cout<<"测试Search():"<<endl;
        cout<<"元素0的位置为:  "<<c.Search(0)<<endl;
        cout<<"元素2的位置为:  "<<c.Search(2)<<endl<<endl;

        //测试Delete()
        cout<<"测试Delete():"<<endl;
        cout<<"原链表:   "<<c<<endl;
        //c.Delete(1,x);
        //cout<<"c.Delete(1,x):  "<<x<<endl<<endl;
        c.Delete(1,x);
        cout<<"c.Delete(1,x):    "<<c<<endl<<endl;

        //测试Erase()函数
        //c.Erase();
        //cout<<"Erase()后:   "<<c<<endl<<endl;

        //测试append()函数
        c.Append(5);
        cout<<"Append()后:   "<<c<<endl<<endl;

参考文献:
[1] 数据结构算法与应用:C++描述(Data Structures, Algorithms and Applications in C++ 的中文版)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值