前言:
学习数据结构最好熟悉的几个英语单词:
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;
}
程序运行结果: