单链表的C++实现(模板技术、数据结构)

前言:

学习数据结构最好熟悉的几个英语单词:

  • sequence:序列,顺序。在代码中通常简写成seq

  • link:链

  • node:结点

  • push:英语中常理解为推,这里我们理解成压入,就是栈中压栈的那个压。

  • pop:直译为弹出,栈中为出栈的意思,在链表中代表摘链。

  • rear:队尾,代码中常简写为r。

  • STL:standard template library(C++标准模板库)

由于类模板成员函数创建时机实在程序运行阶段,所以在代码的分文件编写过程中需要特殊处理,一种解决方法为在主函数cpp文件直接包含类模板的实现文件(.cpp)文件;第二种是将类模板的声明和实现全都放在头文件(.h)里,头文件后缀改成.hpp文件。

在visual studio中的效果如下:

单链表类声明:

LinkList.hpp:

#pragma once
#include<iostream>
using namespace std;

//构建结点类
template<class T>
class LinkNode
{
public:
    LinkNode() {}
    LinkNode(T d) :data(d), next(0) {}

    T data;                //数据域
    LinkNode<T>* next;    //指针域
};

template<class T>
class LinkList
{
public:
    LinkList();
    LinkList(LinkList& list); // 拷贝构造函数,实现深拷贝

    int get_length();    //返回链表的长度
    void printList();    //从首元结点输出链表
    void push_back(T arr[], int n);        //尾插法建立链表
    void push_back(T d);                //尾插法
    void push_front(T arr[], int n);    //头插法建立链表
    void insert(T d, int pos);            //插入元素
    T pop(int pos);                        //按位置删除元素
    LinkNode<T>* get(int i);            //获取第i位结点的地址
    int locate(T d);                    //按值查找元素在链表的位置
    void clear();                        //将链表置空
    void bubbleSort();                    //冒泡排序
    ~LinkList();                        //析构函数
private:
    int length;            //表长
    LinkNode<T>* front;    //头指针
    LinkNode<T>* rear;    //尾指针
};

template<class T>
LinkList<T>::LinkList(LinkList& list)
{
    LinkNode<T>* p = list.get(1);
    while (p)
    {
        rear->next = new LinkNode<T>(p->data);
        p = p->next;
        rear = rear->next;
    }
}// 拷贝构造函数,实现深拷贝

template<class T>
void LinkList<T>::bubbleSort()
{
    for (int i = 1; i <= this->length - 1; ++i) //外层循环length-1次
    {
        LinkNode<T>* p = front->next;
        for (int j = 1; j <= length - i; ++j) //内层循环length-i次
        {
            if (p->data > p->next->data)
            {
                T temp = p->data;
                p->data = p->next->data;
                p->next->data = temp;
            }
            p = p->next;
        }
        if (i == 1) rear = p->next;    //第一次找到的最大值设置为队尾
    }
}//冒泡排序

template<class T>
LinkList<T>::LinkList()
{
    this->front = new LinkNode<T>;
    rear = front;
    length = 0;
}// 无参构造函数

template<class T>
int LinkList<T>::get_length()
{
    return this->length;
}

template<class T>
void LinkList<T>::printList()
{
    if (length == 0)
    {
        cout << "链表为空..." << endl;
        return;
    }

    LinkNode<T>* p = front->next;
    while (p->next)
    {
        cout << p->data << " -> ";
        p = p->next;
    }
    cout << p->data << endl;
}

template<class T>
void LinkList<T>::push_back(T arr[], int n)
{
    LinkNode<T>* p = front;
    for (int i = 0; i < n; i++)
    {
        LinkNode<T>* s = new LinkNode<T>;
        s->data = arr[i];
        p->next = s;
        p = s;
        this->length++;
    }
    p->next = NULL;        //尾结点指针域必须手动置为空,不然遍历时会异常
    rear = p;            //更新尾指针
}

template<class T>
void LinkList<T>::push_back(T d)
{
    LinkNode<T>* s = new LinkNode<T>(d);
    rear->next = s;
    rear = s;
    s->next = NULL;
    length++;    //更新链表长度!
}//尾插法

template<class T>
void LinkList<T>::push_front(T arr[], int n)
{
    for (int i = 0; i < n; i++)
    {
        LinkNode<T>* p = new LinkNode<T>;
        p->data = arr[i];
        p->next = front->next;
        front->next = p;
        this->length++;
    }
    //尾结点的指针域为最开始的front->next(NULL)
}//头插法建立链表

template<class T>
LinkNode<T>* LinkList<T>::get(int i)
{
    if (i <= 0 || i > length)
        throw "访问的元素位置非法!!";

    LinkNode<T>* p = front;
    int j = 0;
    while (p && j != i)
    {
        p = p->next;
        j++;
    }
    return p;
}//获取第i位结点的地址

template<class T>
int LinkList<T>::locate(T d)
{
    int cnt = 0;    //计数器
    LinkNode<T>* p = front->next;
    while (p)
    {
        cnt++;
        if (p->data == d)
            return cnt;
        p = p->next;
    }

    return -1;    //若未找到,返回错误标识-1
}//按值查找元素在链表的位置

template<class T>
void LinkList<T>::insert(T d, int pos)
{
    if (pos<1 || pos>length + 1)
        throw "插入的位置非法!!";
    LinkNode<T>* p = front;
    if (pos != 1)
        p = get(pos - 1);

    LinkNode<T>* s = new LinkNode<T>(d);
    s->next = p->next;
    p->next = s;
    length++;        //更新链表长度
}//插入元素

template<class T>
T LinkList<T>::pop(int pos)
{
    if (pos < 1 || pos > length)
        throw "删除的位置不合法!!";

    LinkNode<T>* p = front;
    if (pos != 1)
        p = get(pos - 1);

    LinkNode<T>* q = p->next;
    T d = q->data;
    p->next = q->next;
    delete q;
    length--;    //更新链表长度
    return d;
}//按位置删除元素

template<class T>
void LinkList<T>::clear()
{
    LinkNode<T>* p = front->next;
    front->next = NULL;//链表置空,防止front->next成为野指针
    while (p)
    {
        rear = p->next;
        delete p;
        p = rear;
        length--;    //更新链表长度
    }
    rear = front;
}//将链表置空

template<class T>
LinkList<T>::~LinkList()
{
    LinkNode<T>* p = front;
    while (front)
    {
        front = front->next;
        if(p)delete p;
        p = front;
    }
    rear = NULL;    //尾指针置空,否则会出现野指针
    //cout << "结点内存已释放~" << endl;
}

主函数要使用LinkList类只需要首句写#include "LinkList.hpp",不需要重复写iostream和namespace

编写main函数测试代码功能,sketch.cpp:

#include"LinkList.hpp"

int main()
{

    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    LinkList<int> lst;

    cout << "尾插法建立链表:";
    lst.push_back(arr, 5);
    lst.printList();
    cout << endl;

    cout << "链表置空后:" << endl;
    lst.clear();
    lst.printList();
    cout << endl;

    cout << "头插法建立链表:";
    lst.push_front(arr, 6);
    lst.printList();
    cout << endl;

    cout << "插入操作测试:" << endl;
    lst.insert(0, 4);
    lst.printList();

    try //插入的异常处理机制测试
    {
        lst.insert(0, -1);
    }
    catch (const char* err)
    {
        cout << err << endl;
    }

    cout << endl;

    cout << "删除操作测试:" << endl;
    lst.pop(4);
    lst.printList();

    try //删除的异常处理机制测试
    {
        lst.pop(-1);
    }
    catch (const char* err)
    {
        cout << err << endl;
    }
    cout << endl;
    
    cout << "查找操作测试:" << endl;
    cout << "元素4的位置为:" << lst.locate(4) << endl;

    if (lst.locate(0) == -1) cout << "链表中没有指定的元素" << endl << endl;

    cout << "获取链表长度操作测试:" << lst.get_length() << endl;

    return 0;
}

程序运行结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值