C++泛化单链表
泛型单链表
单链表将每个数据分为节点,每个节点存储数据和指向下一个节点的指针。这样数据就不用在内存中使用连续的存储空间,有更大的灵活性。
这里将单链表分为节点类(Node)和链表类(singleLinkedList),由于链表类需要访问到Node类的数据,因此将Node类的数据声明为public,也可以将链表类声明为节点类的友元类。
抽向数据类型(ADT) :(linearList.h)
/*************************************************************************
> File Name : linearList.h
> Author : Harold
> Mail : 2106562095@qq.com
> Github : www.github.com/Haroldcc
> Created Time : 2020年02月26日 11时02分58秒
************************************************************************/
/***** 线性表的抽象定义 *****/
#ifndef LINEARLIST_H
#define LINEARLIST_H
#include <iostream>
template <typename T>
class linearList
{
public:
virtual ~linearList() {}
virtual bool isEmpty() const = 0; //是否为空
virtual int size() const = 0; // 元素个数
virtual T &getElement(int index) const = 0; // 根据索引获取元素
virtual void setElement(int index, const T &element) = 0; // 根据索引更改元素值
virtual int indexOf(const T &element) const = 0; //指定元素第一次出现的索引
virtual void removeByIndex(int index) = 0; //根据索引删除元素并返回
virtual void removeByElement(const T &element) = 0; // 删除指定元素
virtual void add(const T &element) = 0; // 在尾部插入元素
virtual void add(int index, const T &element) = 0; // 在指定索引处插入元素
virtual void output(std::ostream &out) const = 0; // 插入输出流out
};
#endif
异常类(myException.h)
/*************************************************************************
> File Name : myExceptions.h
> Author : Harold
> Mail : 2106562095@qq.com
> Github : www.github.com/Haroldcc
> Created Time : 2020年02月26日 14时32分01秒
************************************************************************/
/***** 自定义的异常 *****/
#ifndef MYEXCEPTIONS_H
#define MYEXCEPTUONS_H
#include <iostream>
#include <string>
// 非法参数
class illegalParameterValue
{
private:
std::string message;
public:
illegalParameterValue(std::string theMessage = "参数值非法!") : message(theMessage) {}
void outputMessage() { std::cout << message << std::endl; }
};
// 非法输入
class illegalInputData
{
private:
std::string message;
public:
illegalInputData(std::string theMessage = "非法数据输入!")
{
message = theMessage;
}
void outputMessage() { std::cout << message << std::endl; }
};
// 非法索引
class illegalIndex
{
private:
std::string message;
public:
illegalIndex(std::string theMessage = "非法索引!") : message(theMessage) {}
void outputMessage() { std::cout << message << std::endl; }
};
#endif
单链表头文件(singleLinkedList.h)
/*************************************************************************
> File Name : singleLinkedList.h
> Author : Harold
> Mail : 2106562095@qq.com
> Github : www.github.com/Haroldcc
> Created Time : 2020年02月27日 10时12分02秒
************************************************************************/
/***** 单链表 *****/
#ifndef SINGLELINKEDLIST_H
#define SINGLELINKEDLIST_H
#include "linearList.h" // ADT
#include "myExceptions.h" // 异常类
#include <sstream> // for ostringstream
/*** 链表节点类 ***/
template <typename T>
class Node
{
public:
T element;
Node<T> *next;
Node(){};
Node(const T &element) { this->element = element; }
Node(const T &element, Node<T> *next)
{
this->element = element;
this->next = next;
}
};
/*** 单链表类 ***/
template <typename T>
class singleLinkedList : public linearList<T>
{
private:
void checkIndex(int index) const; // 索引异常检测
Node<T> *firstNode; // 链表头节点指针
int listSize; // 链表的元素个数
public:
/* 构造函数 */
singleLinkedList(int initialCapacity = 10);
singleLinkedList(const singleLinkedList<T> &);
~singleLinkedList();
/* ADT */
bool isEmpty() const { return this->listSize == 0; }
int size() const { return this->listSize; }
T &getElement(int index) const;
void setElement(int index, const T &element);
int indexOf(const T &element) const;
void removeByIndex(int index);
void removeByElement(const T &element);
void add(const T &element);
void add(int index, const T &element);
void output(std::ostream &out) const;
};
// 默认构造
template <typename T>
singleLinkedList<T>::singleLinkedList(int initialCapacity)
{
if (initialCapacity < 1)
{
std::ostringstream s;
s << "初始化容量 = " << initialCapacity << "必须 > 0";
throw illegalParameterValue(s.str());
}
this->firstNode = nullptr;
this->listSize = 0;
}
// 拷贝构造
template <typename T>
singleLinkedList<T>::singleLinkedList(const singleLinkedList<T> &list)
{
this->listSize = list.listSize;
// list 为空
if (this->listSize == 0)
{
this->firstNode = nullptr;
return;
}
// list 不为空
Node<T> *sourceNode = list.firstNode; // 要复制链表list的节点
this->firstNode = new Node<T>(sourceNode->element); // 要复制链表list的首元素
sourceNode = sourceNode->next;
Node<T> *targetNode = this->firstNode; // 当前链表 *this 的最后一个节点
while (sourceNode != nullptr)
{ // 复制剩下的节点
targetNode->next = new Node<T>(sourceNode->element);
targetNode = targetNode->next;
sourceNode = sourceNode->next;
}
targetNode->next = nullptr; // 尾部置为空
}
// 析构函数
template <typename T>
singleLinkedList<T>::~singleLinkedList()
{
while (this->firstNode != nullptr)
{
Node<T> *nextNode = this->firstNode->next;
delete this->firstNode;
firstNode = nextNode;
}
}
template <typename T>
void singleLinkedList<T>::checkIndex(int index) const
{
if (index < 0 || index >= this->listSize)
{
std::ostringstream s;
s << "index = " << index << "size = " << this->listSize;
throw illegalIndex(s.str());
}
}
// 根据索引获取对应的节点,如未找到,抛异常
template <typename T>
T &singleLinkedList<T>::getElement(int index) const
{
checkIndex(index);
Node<T> *currentNode = this->firstNode;
for (int i = 0; i < index; i++)
currentNode = currentNode->next;
return currentNode->element;
}
// 根据索引设置元素值
template <typename T>
void singleLinkedList<T>::setElement(int index, const T &element)
{
checkIndex(index);
Node<T> *currentNode = this->firstNode;
for (int i = 0; i < index; i++)
currentNode = currentNode->next;
currentNode->element = element;
}
// 返回给定元素的索引值,若未找到返回 -1
template <typename T>
int singleLinkedList<T>::indexOf(const T &element) const
{
Node<T> *currentNode = this->firstNode;
int index = 0;
while (currentNode != nullptr && currentNode->element != element)
{
currentNode = currentNode->next;
index++;
}
if (currentNode == nullptr)
{
return -1;
}
else
{
return index;
}
}
// 删除指定索引处的元素值,未找到元素抛异常
template <typename T>
void singleLinkedList<T>::removeByIndex(int index)
{
checkIndex(index);
Node<T> *deleteNode;
if (index == 0)
{ // 删除链表中的第一个元素
deleteNode = this->firstNode;
this->firstNode = this->firstNode->next;
}
else
{
Node<T> *p = this->firstNode;
for (int i = 0; i < index - 1; i++)
p = p->next;
deleteNode = p->next;
p->next = p->next->next;
}
this->listSize--;
delete deleteNode;
}
template <typename T>
void singleLinkedList<T>::removeByElement(const T &element)
{
removeByIndex(indexOf(element));
}
template <typename T>
void singleLinkedList<T>::add(const T &element)
{
add(this->listSize, element);
}
// 在指定索引处添加元素
template <typename T>
void singleLinkedList<T>::add(int index, const T &element)
{
if (index < 0 || index > this->listSize)
{
std::ostringstream s;
s << "index = " << index << "size = " << this->listSize;
throw illegalIndex(s.str());
}
if (index == 0)
{ // 头部插入元素
this->firstNode = new Node<T>(element, this->firstNode);
}
else
{
Node<T> *p = this->firstNode;
for (int i = 0; i < index - 1; i++)
{
p = p->next;
}
p->next = new Node<T>(element, p->next);
}
this->listSize++;
}
template <typename T>
void singleLinkedList<T>::output(std::ostream &out) const
{
for (Node<T> *currentNode = this->firstNode;
currentNode != nullptr;
currentNode = currentNode = currentNode->next)
out << currentNode->element << " ";
}
// 重载 << 运算符
template <typename T>
std::ostream &operator<<(std::ostream &out, const singleLinkedList<T> &list)
{
list.output(out);
return out;
}
#endif
测试
#include "linearList.h"
#include "singleLinkedList.h"
#include <iostream>
using namespace std;
int main()
{
// test constructor
linearList<double> *x = new singleLinkedList<double>;
singleLinkedList<int> y, z;
// test size
cout << "Initial size of x, y, and z = "
<< x->size() << ", "
<< y.size() << ", "
<< z.size() << endl;
// test isEmpty
if (x->isEmpty()) cout << "x is isEmpty" << endl;
else cout << "x is not isEmpty" << endl;
if (y.isEmpty()) cout << "y is isEmpty" << endl;
else cout << "y is not isEmpty" << endl;
// test add
y.add(0, 2);
y.add(1, 6);
y.add(0, 1);
y.add(2, 4);
y.add(3, 5);
y.add(2, 3);
cout << "Inserted 6 integers, list y should be 1 2 3 4 5 6" << endl;
cout << "Size of y = " << y.size() << endl;
if (y.isEmpty()) cout << "y is isEmpty" << endl;
else cout << "y is not isEmpty" << endl;
y.output(cout);
cout << endl << "Testing overloaded <<" << endl;
cout << y << endl;
// test indexOf
int index = y.indexOf(4);
if (index < 0) cout << "4 not found" << endl;
else cout << "The index of 4 is " << index << endl;
index = y.indexOf(7);
if (index < 0) cout << "7 not found" << endl;
else cout << "The index of 7 is " << index << endl;
// test getElement
cout << "Element with index 0 is " << y.getElement(0) << endl;
cout << "Element with index 3 is " << y.getElement(3) << endl;
// test removeByIndex
y.removeByIndex(1);
cout << "Element 1 erased" << endl;
cout << "The list is " << y << endl;
y.removeByIndex(2);
cout << "Element 2 erased" << endl;
cout << "The list is " << y << endl;
y.removeByIndex(0);
cout << "Element 0 erased" << endl;
cout << "The list is " << y << endl;
cout << "Size of y = " << y.size() << endl;
if (y.isEmpty()) cout << "y is isEmpty" << endl;
else cout << "y is not isEmpty" << endl;
try {y.add(-3, 0);}
catch (illegalIndex e)
{
cout << "Illegal index exception" << endl;
cout << "Insert index must be between 0 and list size" << endl;
e.outputMessage();
}
// test copy constructor
singleLinkedList<int> w(y);
y.removeByIndex(0);
y.removeByIndex(0);
cout << "w should be old y, new y has first 2 elements removed" << endl;
cout << "w is " << w << endl;
cout << "y is " << y << endl;
// a few more inserts, just for fun
y.add(0,4);
y.add(0,5);
y.add(0,6);
y.add(0,7);
cout << "y is " << y << endl;
return 0;
}
输出
Initial size of x, y, and z = 0, 0, 0
x is isEmpty
y is isEmpty
Inserted 6 integers, list y should be 1 2 3 4 5 6
Size of y = 6
y is not isEmpty
1 2 3 4 5 6
Testing overloaded <<
1 2 3 4 5 6
The index of 4 is 3
7 not found
Element with index 0 is 1
Element with index 3 is 4
Element 1 erased
The list is 1 3 4 5 6
Element 2 erased
The list is 1 3 5 6
Element 0 erased
The list is 3 5 6
Size of y = 3
y is not isEmpty
Illegal index exception
Insert index must be between 0 and list size
index = -3size = 3
w should be old y, new y has first 2 elements removed
w is 3 5 6
y is 6
y is 7 6 5 4 6
C++泛化单链表的更多相关文章
- 时间复杂度分别为 O(n)和 O(1)的删除单链表结点的方法
有一个单链表,提供了头指针和一个结点指针,设计一个函数,在 O(1)时间内删除该结点指针指向的结点. 众所周知,链表无法随机存储,只能从头到尾去遍历整个链表,遇到目标节点之后删除之,这是最常规的思路和 ...
- 单链表的C++实现(采用模板类)
采用模板类实现的好处是,不用拘泥于特定的数据类型.就像活字印刷术,制定好模板,就可以批量印刷,比手抄要强多少倍! 此处不具体介绍泛型编程,还是着重叙述链表的定义和相关操作. 链表结构定义 定义单链表 ...
- Java实现单链表的各种操作
Java实现单链表的各种操作 主要内容:1.单链表的基本操作 2.删除重复数据 3.找到倒数第k个元素 4.实现链表的反转 5.从尾到头输出链表 6.找到中间节点 7.检测链表是否有环 8.在 ...
- [LeetCode] Linked List Cycle II 单链表中的环之二
Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Foll ...
- c++单链表基本功能
head_LinkNode.h /*单链表类的头文件*/#include<assert.h>#include"compare.h"typedef int status; ...
- 单链表、循环链表的JS实现
数据结构系列前言: 数据结构作为程序员的基本知识,需要我们每个人牢牢掌握.近期我也展开了对数据结构的二次学习,来弥补当年挖的坑...... 当时上课的时候也就是跟着听课,没有亲自实现任何一种数据结 ...
- C代码实现非循环单链表
C代码实现非循环单链表, 直接上代码. # include <stdio.h> # include <stdlib.h> # include <malloc.h> ...
- 分离的思想结合单链表实现级联组件:CascadeView
本文介绍自己最近做省市级联的类似的级联功能的实现思路,为了尽可能地做到职责分离跟表现与行为分离,这个功能拆分成了2个组件并用到了单链表来实现关键的级联逻辑,下一段有演示效果的gif图.虽然这是个很常见 ...
- 数据结构:单链表结构字符串(python版)添加了三个新功能
#!/urs/bin/env python # -*- coding:utf-8 -*- #异常类 class stringTypeError(TypeError): pass #节点类 class ...
随机推荐
- python StringIO和ByteIO
一.StringIO 1.作用:在内存在读写str # 导入模块 from io import StringIO # 实例化StringIO对象 str_io = StringIO() # 向内存中写 ...
- 12.instanceof和类型转换
Instanceof: 判断一个对象是什么类型的~,可以判断两个类之间是否存在父子关系 package com.oop.demo07; public class Person { public voi ...
- Java单体应用 - 架构模式 - 01.三层架构
原文地址:http://www.work100.net/training/monolithic-architecture-3level.html 更多教程:光束云 - 免费课程 三层架构 序号 文内章 ...
- 演示共享布局 Demonstrating Shared Layouts 精通ASP-NET-MVC-5-弗瑞曼 Listing 5-10
- Commvault Oracle备份常用命令
在进行Oracle数据库备份的配置.发起和恢复的过程中,需要用到许多Oracle数据库本身的命令.在此章节中进行命令的梳理,供大家参考. Oracle用户和实例相关命令 Linux/Unix平台 # ...
- Day2-Python3基础-文件操作
1. 字符编码与转码 需知: 1.在python3默认编码是unicode 2.unicode 分为 utf-32(占4个字节),utf-16(占两个字节),utf-8(占1-4个字节), so ut ...
- PCA技术的自我理解(催眠
Principal component analysis(PCA) 中文就是主成成分分析.在学数学建模的时候将这分为了评价类的方法(我实在是很难看出来,在机器学习中是属于无监督学习降维方法的一种线性降 ...
- mac如何用quick look预览多个文件或者图片
1.先选中要查看的多个文件,然后点击 空格键 2.按住 command+return 就可以同时预览多个文件了 如果想全屏预览,则在1中,按住 option+空格键 ,然后再进行2 ,就实现全屏预览了 ...
- 曹工说Spring Boot源码(15)-- Spring从xml文件里到底得到了什么(context:load-time-weaver 完整解析)
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- 分享数百个 HT 工业互联网 2D 3D 可视化应用案例之 2019 篇
继<分享数百个 HT 工业互联网 2D 3D 可视化应用案例>2018 篇,图扑软件定义 2018 为国内工业互联网可视化的元年后,2019 年里我们与各行业客户进行了更深度合作,拓展了H ...