本文的链表实现使用的是c++的类模板,构造节点类模板Node<T>和链表类模板LinkedList<T>。原因是实用类模板可以适用于各种基本数据类型。以下是类模板及其接口(存放在"LinkedList.h"头文件中):
#ifndef LINKEDLIST_H_
#define LINKEDLIST_H_
#include<iostream>
using namespace std;
//Node类模板的友元类模板声明
template <typename T>
class LinkedList; //LinkedList作为Node类的友元类用于访问Node类的私有属性。在Node类前面加上LinkedList的声明,让编译器不报错
//注意:class LinkedList{}; 这里的{}不能要。只是声明,不需要{}。
//Node类模板
template <typename T>
class Node
{
public:
//node *head = new node();
Node *d_next; //指针段:指向下一个节点的指针
T d_data; //数据段:本节点的数据
public:
Node();//默认构造
Node(const T &elem);//有参构造
~Node();//析构函数
//对于Node数据的类型,cout无法直接通过输出node对象来输出数据d_data,所以需要重载<<运算符实现。同时要访问Node节点的私有属性,要声明是友元函数
template <typename R>
friend std::ostream& operator<<(std::ostream &, Node<R> &); //第二个参数不能用const修饰引用,会报错
};
template <typename T>
class LinkedList
{
private:
//头指针
Node<T>* d_head; //指针指向的类型为Node<T>
//节点数
int d_size;
public:
//默认构造(空链表)
LinkedList();
//析构
~LinkedList();
//尾端插入节点
void push_back(const T &val);
//打印链表
template <typename R>
friend std::ostream & operator<<(std::ostream &out, LinkedList<R> &list);
//获取链表长度
int size()
{
return d_size;
}
//头部插入节点
void push_head(const T &val);
//在指定位置插入节点
void insert(const T &val,int pos);
//删除所有数据
void DeleteAll();
//查找某元素并返回其节点地址
Node<T> * Find(const T &val);
//按元素删除节点
void DeleteElem(const T &val);
//尾部删除
void DeleteLast();
//第一个节点删除
void DeleteFirst();
//链表反转
void Reserve();
};
#endif
下面是类模板接口的实现(存放在linkedList.cpp文件中):
#include<iostream>
#include "LinkedList.h"
//Node无参构造
template <class T>
Node<T>::Node() : d_data(), d_next(nullptr) {} //参数列表法实现默认构造,数据赋值为空,指向下一个节点的指针赋空指针
//Node有参构造
template <class T>
Node<T>::Node(const T &elem)
{
this->d_data = elem;
this->d_next = nullptr;
}
//Node析构函数
template <class T>
Node<T>::~Node(){}
//Node类模板中重载运算度算
template <typename R>
std::ostream& operator<<(std::ostream &out, Node<R> &nod) //重载运算度算是一个友元的全局函数,不需要加作用域。第二个参数不能加const
{
out << nod.elem();
return out;
}
//LinkedList类模板的默认构造
template <typename T>
LinkedList<T>::LinkedList() //默认构造长度为0的链表
{
this->d_head = new Node<T>();//开辟头节点。头结点啥也不存,也不一定必须有,不计入链表长度,但我这里假设有头结点。
this->d_head->d_data = NULL;//该节点的数据初值为NULL
this->d_head->d_next = nullptr;//末指针设为空
this->d_size = 0;
}
//LinkedList类模板析构函数
template <typename T>
LinkedList<T>::~LinkedList()
{
if (this->size()==0)
{
delete this->d_head;
}
else
{
Node<T> *tmp=nullptr;
Node<T> *cur = this->d_head->d_next;//指向第一个节点
while (cur->d_next != nullptr)//不是尾节点时
{
tmp = cur;
cur = cur->d_next;//光标节点指向下一个节点
delete tmp;//释放当前节点
}
delete cur;//释放尾节点
delete this->d_head;//释放头结点
}
}
template <typename T>
void LinkedList<T>::push_back(const T &val)//尾插
{
Node<T> * tmp = new Node<T>(); //Node的有参构造
tmp->d_data = val;
tmp->d_next = nullptr;
Node<T> *cur = this->d_head;//设置光标指针
if (cur->d_next == nullptr)//如果只有头结点
{
cur->d_next = tmp;//头结点直接连接插入数据所创建的新节点
}
else
{
while (cur->d_next != nullptr)//如果有其他节点
{
cur = cur->d_next;//更新光标开始遍历所有非尾节点
}
cur->d_next = tmp;//尾节点连接上插入数据的节点
}
d_size++; //节点个数加1
}
template <typename T>
void LinkedList<T>::push_head(const T &val)//头插
{
Node<T> * tmp = new Node<T>(); //Node的有参构造
tmp->d_data = val;
tmp->d_next = nullptr;
Node<T> *cur = this->d_head;
if (cur->d_next == nullptr)//如果只有头结点
{
cur->d_next = tmp;
}
else
{
tmp->d_next = this->d_head->d_next;//插入节点链接原来的第一个节点
this->d_head->d_next = tmp;//头结点链接插入的节点
}
d_size++; //节点个数加1
}
template <typename T>
void LinkedList<T>::insert(const T &val, int pos) //pos位置插入val
{
Node<T> *tmp = new Node<T>();
tmp->d_data = val;
tmp->d_next = nullptr;
if (pos == 0||this->d_size==0)//约定链表下标从0开始算
{
this->push_head(val);
this->d_size++;
}
else if (pos == this->d_size)//尾插
{
this->push_back(val);
this->d_size++;
}
else if (pos > 0 && pos < this->d_size)
{
Node<T> *cur = this->d_head;
for (int i = 0; i < pos; i++)
{
cur = cur->d_next;
}
tmp->d_next = cur->d_next;
cur->d_next = tmp;
this->d_size++;
}
else
{
cout << "输入的位置有误!!!" << endl;
}
}
template <typename T>
void LinkedList<T>::DeleteAll()//清空除了头结点外所有的节点
{
if (this->d_size==0) //如果
{
cout << "链表已是空链表" << endl;
}
else
{
Node<T>* cur = this->d_head->d_next;
Node<T>* tmp = nullptr;
while (cur->d_next != nullptr)//释放尾节点前的节点
{
tmp = cur;
cur = cur->d_next;
delete tmp;
}
delete cur;//释放尾节点
this->d_head->d_next = nullptr;//空链表,头结点的指针为nullptr
this->d_size = 0;//长度置0
}
}
template <typename R>
std::ostream & operator<<(std::ostream &out, LinkedList<R> &list)//友元函数,重载<<运算符用于输出链表
{
if (list.d_size==0)
{
out << "链表为空"<<endl;
}
else
{
Node<R> * cur = list.d_head->d_next;//光标处于第一个节点
while (cur->d_next != nullptr)
{
out << cur->d_data << "-->";//输出光标所在节点的数据
cur = cur->d_next;//光标更新,指向下一节点
}
out<<cur->d_data << "-->";//输出最后节点的数据
}
out << "&"<<endl;//输出结尾指向的空,用&表示
return out;
}
template <typename T>
Node<T>* LinkedList<T>::Find(const T &val)
{
if (this->d_size == 0)
{
cout << "空链表!!" << endl;
return nullptr;
}
else
{
Node<T>* cur = this->d_head->d_next;//光标处于第一个节点
while (cur !=nullptr)//还没遍历所有节点(若最后一个节点也没找到,则cur更新后给nullptr,退出循环)
//while (cur !=nullptr)可以遍历所有节点
//while (cur->d_next != nullptr)会在最后一个节点处退出循环
{
if (cur->d_data == val)
{
cout << "找到了" << val << endl;
return cur;
}
cur = cur->d_next;
}
cout << "未找到" << val << endl;
return nullptr;//未找到
}
}
template <typename T>
void LinkedList<T>::DeleteElem(const T &val)//按值删除节点
{
Node<T>* tmp = this->Find(val);
if (tmp == nullptr)
{
cout << "不存在" << val << ",无法删除!" << endl;
}
else
{
Node<T>* cur = this->d_head;
while (cur->d_next != tmp)//寻找元素所在的节点
{
cur = cur->d_next;
}
cur->d_next = tmp->d_next;
delete tmp;
tmp = nullptr;
this->d_size--;
}
}
template <typename T>
void LinkedList<T>::DeleteLast()//尾删
{
if (this->d_head->d_next == nullptr)
{
cout << "空链表,无法删除最后一个元素" << endl;
}
Node<T>* cur = this->d_head->d_next;
Node<T>* tmp = nullptr;
while (cur->d_next != nullptr)//不是最后一个节点
{
tmp = cur;
cur = cur->d_next;
}
//到最后一个节点,tmp指向第二个节点
delete cur;
tmp->d_next = nullptr; //倒数第二个节点变为尾节点,其d_next为nullptr
this->d_size--;
}
template <typename T>
void LinkedList<T>::DeleteFirst()
{
if (this->d_size==0)
{
cout << "空链表,无法删除第一个元素" << endl;
}
else
{
Node<T>* tmp = this->d_head->d_next;//光标处于第一个节点
this->d_head->d_next = tmp->d_next;
delete tmp;
tmp = nullptr;
this->d_size--;
}
}
template <typename T>
void LinkedList<T>::Reserve()
{
if (this->d_size == 0)
{
cout << "链表为空,不需要反转" << endl;
}
else if (this->d_size == 1)
{
cout << "链表长度为1,不需要反转" << endl;
}
else
{
Node<T>* pre;
Node<T>* cur;
Node<T>* latter;
pre = nullptr;//pre在链表末尾的nullptr位置
cur = this->d_head->d_next;//cur指向第一个节点
latter = cur->d_next;//latter指向第二个节点
while (latter->d_next != nullptr)
{
cur->d_next = pre;
pre = cur;
cur = latter;
latter = latter->d_next;
}
latter->d_next = cur;
cur->d_next = pre;
this->d_head->d_next = latter;
}
}
void test02()
{
LinkedList<int> List1; //默认构造
cout << List1.size() << endl;
cout << List1 << endl;
//测试push_back
int num = 2;
List1.push_back(num);
List1.push_back(3);
cout << List1 << endl;
List1.push_head(0);
List1.push_head(-1);
cout << List1 << endl;
List1.insert(-2,4);
cout << List1 << endl;
List1.DeleteAll();
cout << List1 << endl;
List1.DeleteAll();
List1.Find(2);
List1.push_head(0);
List1.push_head(1);
List1.push_head(2);
List1.push_head(4);
List1.push_head(10);
cout << List1 << endl;
List1.Find(2);
List1.Find(100);
List1.Find(0);
cout << List1 << endl;
List1.DeleteElem(3);
List1.DeleteElem(0);
cout << List1 << endl;
List1.DeleteLast();
cout << List1 << endl;
List1.DeleteLast();
cout << List1 << endl;
List1.DeleteFirst();
cout << List1 << endl;
List1.DeleteFirst();
cout << List1 << endl;
List1.DeleteFirst();
cout << List1 << endl;
List1.push_back(7);
List1.push_back(8);
List1.push_back(9);
List1.push_back(10);
cout << List1 << endl;
List1.Reserve();//链表反转
cout << List1 << endl;
}
using namespace std;
int main()
{
test02();
return 0;
}
对于链表反转,思路参考下图
具体代码:
template <typename T>
void LinkedList<T>::Reserve()
{
if (this->d_size == 0)
{
cout << "链表为空,不需要反转" << endl;
}
else if (this->d_size == 1)
{
cout << "链表长度为1,不需要反转" << endl;
}
else
{
Node<T>* pre;
Node<T>* cur;
Node<T>* latter;
pre = nullptr;//pre在链表末尾的nullptr位置
cur = this->d_head->d_next;//cur指向第一个节点
latter = cur->d_next;//latter指向第二个节点
while (latter->d_next != nullptr)
{
cur->d_next = pre;
pre = cur;
cur = latter;
latter = latter->d_next;
}
latter->d_next = cur;
cur->d_next = pre;
this->d_head->d_next = latter;
}
}
程序运行结果: