c++实现单链表

一、用面向对象的思想,用c++语言实现单链表类

(1)链表结点的定义

typedef int DataType;
struct ListNode
{
    ListNode(const DataType& x)
        :data(x)
        ,next(NULL)
    {}
    DataType data;
    ListNode* next;
};

(2)链表的成员变量

    Node* _head;//指向链表的头结点
    Node* _tail;//指向链表的尾结点

(3)链表的成员函数

①默认成员函数函数
在这里我只说两点:

1)链表的拷贝构造和赋值运算符重载函数的区别:

赋值运算符重载其实和拷贝构造差不多,【拷贝构造】是直接给一个没有初始化的新对象初始化;而【赋值运算符的重载】是给一个已经初始化的对象重新赋值;—>所以赋值运算符之前要初始化两个对象,必须要调用两次构造函数或者一次构造,一次拷贝构造;而拷贝构造函数之前则是只调用一次构造;(以上所说情况,只对两个对象)

2)赋值运算符重载函数写法中的效率问题:

在写链表的赋值运算符时,由于是一个链表a 给另一个链表b 赋值;所以可以首先从两个链表的第一个结点开始,将链表a中的结点值赋给链表b;当链表b的结点已经到了最后一个,链表a的值还没有赋值完;然后再用链表a剩余的结点值采用new操作符创建结点;链接到链表b的后面;这样做的好处是;利用已经开辟的空间,进行赋值操作;提高效率;
如果一上来,就是将链表b的结点全部释放,然后重新开辟结点连到链表上;这样的话,空间的释放与开辟次数过多;影响程序的运行效率;

②功能性成员函数

void PushBack(const DataType& x)//尾插
void Popback()//尾删
void PushFront(const DataType& x)//头插
void PopFront()//头删
void Insert(Node* pos,const DataType x)//某一个位置插入某个元素
Node* Find(const DataType& x)//查找某一个元素的地址,没有返回空---二分查找
void Remove(const DataType& x)//删除链表中第一次出现的该元素
void RemoveAll(const DataType& x)//删除链表中全部的改元素
void Sort()//链表元素排序--冒泡(将结点值交换)----升序
void Destroy()//释放链表所有结点内存,并将头,尾指针赋为NULL

二、源代码(待测试部分)

//c++模拟实现单链表
#include<iostream>
using namespace std;
typedef int DataType;
struct ListNode
{
    ListNode(const DataType& x)
        :data(x)
        ,next(NULL)
    {}
    DataType data;
    ListNode* next;
};

class LinkList
{
typedef ListNode Node;
friend ostream& operator<<(ostream& os,LinkList& linklist);
public:
    LinkList()//构造函数
        :_head(NULL)
        ,_tail(NULL)
    {}
    LinkList(const LinkList& linklist)//拷贝构造函数
    {
         /*写法一:
         //1.一进来,先判断链表是否为空,若为空,没有数据进来构造新对象,直接返回。
         if (linklist._head==NULL)
         {
             return;
         }
         Node* cur=linklist._head;//取用来构造的链表的头
         //2.由于新对象一进来,还未初始化,成员指针为野指针;
         //先构造第一个结点;然后用结点的地址初始化成员变量;
         _head=new Node(cur->data);
         _tail=_head;
         //3.接下来,采用循环的方式赋值链表;
         if (cur->next)
         {
             cur=cur->next;
             while (cur)
             {
                 Node* tmp=new Node(cur->data);
                 _tail->next=tmp;
                 _tail=tmp; 
                 cur=cur->next;
             }
         }*/
//***********************************************************************************
        //写法二:直接给链表的成员变量初始化为NULL,等到在while循环赋值链表时;特殊处理
        if (linklist._head==NULL)
        {
            return;
        }
         _head=NULL;
         _tail=NULL;
         Node* cur=linklist._head;
         while (cur)
         {
             Node* tmp=new Node(cur->data);
 //为什么在这里不用类名+对象名的方式构造一个结点对象结点,然后取对象的地址,将结点连到链表上呢???
 //答:因为如果用你说的方式,创建的对象结点是一个临时变量;在栈上创建;出了该函数作用域,对象自动销毁;
 //连到链表 上的结点不见了;这样会导致链表出了函数作用域,没有数据可以供其他函数操作;
 //而用new操作符开辟一段空间,在此空间创建一个结点对象并初始化;该对象空间实在堆上创建,出了作用域,
 //结点依然在链表上存在;可供其他函数对结点数据进行管理;当程序结束时,采用delete操作符释放所开辟的结点空间;
             if (_head==NULL)
             {
                 _head=tmp;
                 _tail=tmp;
             }
             _tail->next=tmp;
             _tail=tmp; 
             cur=cur->next;
         }
    }

    //在写链表的赋值运算符时,由于是一个链表a  给另一个链表b 赋值;所以可以首先从两个链表的第一个结点开始,
    //将链表a中的结点值赋给链表b;当链表b的结点已经到了最后一个,链表a的值还没有赋值完;然后再用链表a剩余的
    //结点值采用new操作符创建结点;链表到链表b的后面;

    //这样做的好处是;利用已经开辟的空间,进行赋值操作;提高效率;
    //如果一上来,就是将链表b的结点全部释放,然后重新开辟结点连到链表上;这样的话,空间的释放与开辟次数过多;
    //影响程序的运行效率;
    LinkList& operator=(const LinkList& linklist)//赋值运算符重载
    {
        //赋值运算符重载其实和拷贝构造差不多,拷贝构造是直接给一个没有初始化的新对象
        //初始化;而赋值运算符的重载是给一个已经初始化的对象重新赋值;所以赋值运算符之前要初始化两个
        //对象必须要调用两次构造函数或者一次构造,一次拷贝构造;而拷贝构造函数之前则是只调用一次构造;
        //(以上所说情况,只对两个对象对象)
        if (linklist._head==NULL)
        {
            return *this;
        }
        if (this!=&linklist)
        {
            Node* cur=linklist._head;
            Node* cur1=_head;
            while (cur && cur1)
            {
                cur1->data=cur->data;
                cur1=cur1->next;
                cur=cur->next;
            }
            if (cur1==NULL)
            {
                while (cur)
                {
                    Node* tmp=new Node(cur->data);

                    _tail->next=tmp;
                    _tail=tmp;  
                    cur=cur->next;
                }
            }
        }
        return *this;
    }
    ~LinkList()//析构函数
    {
       Destroy(); 
    }
public:
    void PushBack(const DataType& x)//尾插
    {
        Node* tmp=new Node(x);
        if (_head==NULL)
        {
            _head=tmp;
            _tail=tmp;
        }
        else
        {
           _tail->next=tmp;
           _tail=tmp;
        }
        tmp->next=NULL;
    }
    void Popback()//尾删
    {
        //1.如果链表为空
        if (_head==NULL)
        {
            return ;
        } 
        //2.如果链表只有一个结点
        if (_head->next==NULL)
        {
            delete _head;
            _head=NULL;
            _tail=NULL;
        }
        //3.链表有大于一个结点
        Node* cur=_head;
        while (cur->next->next)//寻找倒数第二个结点
        {
           cur=cur->next;
        }
        delete _tail;
        cur->next=NULL;
        _tail=cur;
    }
    void PushFront(const DataType& x)//头插
    {
        Node* tmp=new Node(x);
        if(_head==NULL)
        {
           _head=tmp;
        }
        Node* cur=_head;
        _head=tmp;
        tmp->next=cur;
    }
    void PopFront()//头删
    {
        if (_head==NULL)
        {
            return ;
        }
        Node* tmp=_head->next;
        delete _head;
        _head=tmp;
    }
    void Insert(Node* pos,const DataType x)//某一个位置插入某个元素
    {  
        Node* cur=_head;
        if (_head==NULL||pos==NULL)//1.链表为空 或者 插入的位置正好为空位置
        {
            return ;
        }
        while (cur)//2.找结点位置
        {
            if (cur==pos)
            {
                break;
            }
            cur=cur->next;
        }
        if (cur==NULL)//2.1pos位置没有在链表中
        {
            return ;
        }
        //2.2pos在链表在链表中,创建结点并插入
        Node* tmp=new Node(x);
        Node* next=cur->next;
        cur->next=tmp;
        tmp->next=next;
        //如果是想让插入的结点在在pos位置之前,那么加上交换
        std::swap(cur->data,tmp->data);
    }
    Node* Find(const DataType& x)//查找某一个元素的地址,没有返回空---二分查找
    {
         if (_head==NULL)
         {
             return NULL;
         }
         Node* cur=_head;
         while(cur)
         {
             if (cur->data==x)
             {
                 return cur;
                 break;
             }
             cur=cur->next;
         }
         return  NULL;
    }
    void Remove(const DataType& x)//删除链表中第一次出现的该元素
    {
        if (_head==NULL)//1.如果链表为空,直接返回
        {
            return ;
        }
         Node* pos=Find(x);//2.先找到链表中改结点第一个的位置
         if (pos==NULL)//2.1如果链表中没有改元素。直接返回
         {
             return ;
         }
         if (pos==_head&&_head->next==NULL)//2.2如果元素是链表的第一个元素,且链表只有一个元素;
         {
             delete _head;
             _head=NULL;
             return ;
         }
         if (pos==_tail)//2.3如果是最后一个元素
         {
             Popback();
             return ;
         }
         //3.除了上述三种特殊情况以外
         Node* next=pos->next;
         std::swap(pos->data,next->data);
         if (next->next)
         {
             Node* next_next=next->next;
             delete next;
             pos->next=next_next;
         }
         else//pos在倒数第二个结点
         {
             delete next;
             pos->next=NULL;
             _tail=pos;
         }
    }
    void RemoveAll(const DataType& x)//删除链表中全部的改元素
    {
        while(Find(x))
        {
            Remove(x);
        }
    }
    void Sort()//链表元素排序--冒泡(将结点值交换)----升序
    {
        //1.如果链表只有一个结点或者没有结点,直接返回
        if (_head==NULL||_head->next==NULL)
        {
            return ;
        }
        //2.链表有多个结点
        Node* tail=NULL;
        while(tail!=_head)
        {
            Node* cur=_head;
            while (cur->next!=tail)
            {
                Node* next=cur->next;
                if (cur->data > next->data)
                {
                    std::swap(cur->data,next->data);
                }
                cur=cur->next;
            }
            tail=cur;
        }
    }
    void Destroy()//释放链表所有结点内存,并将头,尾指针赋为NULL
    {
        Node* cur=_head;
        while(cur)
        {
            Node* tmp=cur;
            cur=cur->next;
            delete tmp;
        }
        _head=NULL;
        _tail=NULL;
    }
private:
    Node* _head;//指向链表的头结点
    Node* _tail;//指向链表的尾结点
};
ostream& operator<<(ostream& os,LinkList& linklist)
{
     ListNode* cur=linklist._head;
     while(cur)
     {
         os<<cur->data<<" ";
         cur=cur->next;
     }
     return os;
}
void test()
{
     LinkList l;

     l.PushBack(2);
     l.PushBack(7);
     l.PushBack(8);
     l.PushBack(9);
     l.PushBack(1);
     l.PushBack(4);
     l.PushBack(5); 
     l.PushBack(3);
     l.PushBack(3);
     l.PushBack(3);
     l.PushBack(3);
     l.PushBack(3);
     l.PushBack(3);
     cout<<l<<endl;
     l.RemoveAll(3);
     cout<<l<<endl;


     //ListNode* pos=l.Find(1);
     //l.Insert(pos,99);
     //cout<<l<<endl;
     //ListNode* pos=l.Find(3);
  //   l.Insert(pos,99);
     //cout<<l<<endl;
     //l.PopFront();
     //l.PopFront();
     //l.PopFront();
     //l.Popback();
     //l.Popback();
     //l.Popback();
     //l.Sort();
     //cout<<l<<endl;

     //cout<<"----"<<endl;

     //LinkList l2;
     //l2.PushBack(6);
     //l2.PushBack(7);
     //l2.PushBack(8);
     //l2.PushBack(9);
     //l2.PushBack(10);
     //l2.PushBack(11);
     //l2.PushBack(12);
     //l2.PushBack(13);
     //cout<<l2<<endl;
  //   l=l2;
     //cout<<l<<endl;
}
int main()
{
    test();
    return 0;
}

接下来,开始写c++的三大特性之 继承和多态方面的知识

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值