想想自己好像并没有真正实现过数据结构里面的各类结构和算法,便打算动手用c++实现数据结构。这一篇是实现双链表,双链表较之于单链表,前者可以双向连接,既可以向前,也可以向后;而单链表的方向是单一的、固定的。好的,话不多说,直接上干货吧。
首先,声明双链表节点 DNode,每个节点都有一个字段表示当前节点的值、有一个前驱字段来指向上一个节点、有一个后继字段指向下一个节点。默认构造和拷贝构造的区别是,如果初始化 DNode 时,如果没有指定value、prev、next 的话就使用默认构造函数 DNode();如果初始化时指定了 value 、prev、next 的值,就会使用拷贝构造函数DNode(T t, DNode * prev, DNode * next)。
//声明双链表节点的模板类结构体
template<class T>
struct DNode
{
public:
T value;
DNode * prev;
DNode * next;
public:
//默认构造函数
DNode(){}
//拷贝构造函数
DNode(T t, DNode * prev, DNode * next){
this->value = t;
this->prev = prev;
this->next = next;
}
};
template<class T>是 C++ 模板编程的标识,T表示数据类型,进行实例化时可以自己定义为int,float,char等数据类型,也可以是结构体等自定义数据类型。
模板编程,顾名思义,是一个模板,可以给多个类或结构体套用的模板。我们在使用函数或类或结构体是时,都必须知名数据类型是什么,比如void print(int a,int b)这个函数只能传入int数据类型的参数;而void print(T a,T b)可以是任意类型。
定义好双链表节点结构后,继续定义双链表结构
//声明双链表的模板类
template<class T>
class DoubleLink{
public:
DoubleLink();
~DoubleLink();
int size();
int is_empty();
T get(int index);
T get_first();
T get_last();
int insert(int index, T t);
int insert_first(T t);
int append_last(T t);
int del(int index);
int delete_first();
int delete_last();
private:
int count;
DNode<T> * phead;
DNode<T> * get_node(int index);
};
双链表时由每一个一开始定义的 DNode 节点连接起来的,count 表示双链表中 DNode 节点的数量,phead表示表头结点,get_node(int index)函数用来获取 index 位置的节点,返回的是 DNode * 类型。
- DoubleLink():构造函数,对DoubleLink()的定义如下:
template<class T>
DoubleLink<T>::DoubleLink():count(0){
//创建表头
phead = new DNode<T>();
phead->prev = phead->next = phead;
}
DoubleLink<T>::DOubleLink():count(0):使用了Lambda表达式来初始化 count,赋值为0。
调用拷贝构造函数,即实例化对象时,会自动创建一个表头节点phead,phead的前驱和后继都是它本身。
- ~DoubleLink():析构函数,用来销毁对象
template<class T>
DoubleLink<T>::~DoubleLink(){
//删除所有节点
DNode<T> * ptmp;
DNode<T> * pnode = phead->next;
while (pnode != phead){
ptmp = pnode;
pnode = pnode->next;
delete ptmp;
}
delete phead;
phead = NULL;
}
析构函数的定义与构造函数相反,用来销毁对象,销毁每一个节点。
- int size():获取双链表中节点的数量
//获取双链表节点数
template<class T>
int DoubleLink<T>::size(){
return count;
}
- int is_empty():判断是否为空函数
//判断双链表是否为空
template<class T>
int DoubleLink<T>::is_empty(){
return count == 0;
}
如果count不为0,则返回0,否则返回1。
- T get_node(int index):获取 index 位置的节点,返回的是节点类型
//获取index位置的节点
template<class T>
DNode<T>* DoubleLink<T>::get_node(int index){
if (index < 0 || index >= count){
std::cout << "get node failed! the index in out of bound!" << std::endl;
return NULL;
}
//正向查找
if (index <= count / 2){
int i = 0;
DNode<T> * pindex = phead->next;
for (i; i < index; i++){
pindex = pindex->next;
}
return pindex;
}
//反向查找
DNode<T> *rpindex = phead->prev;
for (int j = 0; j < count - index - 1; j++){
rpindex = rpindex->prev;
}
return rpindex;
}
首先判断 index 是否为有效位置,如果小于0或者超过双链表实际长度,输出错误信息。
若 Index 为有效位置,如果Index在链表的前半部分也就是在链表中间节点位置之前,就从链表头部开始遍历,一直到index位置;
如果index 在链表后半部分,也就是在链表中间节点位置之后,就从链表尾部反向遍历,一直到index位置。
之所以要先判断index在中间节点位置之前还时之后,主要是为能够减少遍历节点的数量,减少开支。
T get(int index):获取index位置节点的值:
//获取index位置的节点的值
template<class T>
T DoubleLink<T>::get(int index){
return get_node(index)->value;
}
T get_first():获取链表第一个结点的值(不是头节点)。
//获取第一个节点的值
template<class T>
T DoubleLink<T>::get_first(){
return get_node(0)->value;
}
T get_last():获取链表最后一个节点的值。直接调用get_node(int index)函数获取最后一个节点的值
//获取最后一个节点的值
template<class T>
T DoubleLink<T>::get_last(){
return get_node(count - 1)->value;
}
int insert(int index,T t):将 t 插入到链表index的位置上。
//将节点插入到第index位置之前
template<class T>
int DoubleLink<T>::insert(int index, T t){
if (index == 0){
return insert_first(t);
}
DNode<T>*pindex = get_node(index);
DNode<T>*pnode = new DNode<T>{ t, pindex->prev, pindex };
pindex->prev->next = pnode;
pindex->prev = pnode;
count++;
return 0;
}
int insert_first(T t):将 t 插入到链表的一个节点位置。
//将节点插入到第一个节点处
template<class T>
int DoubleLink<T>::insert_first(T t){
DNode<T> *pnode = new DNode<T>{ t, phead, phead->next };
phead->next->prev = pnode;
phead->next = pnode;
count++;
return 0;
}
int append_last(T t):将 t 加到链表尾部。注意链表连接的变化。
//将节点插入到最后一个节点处
template<class T>
int DoubleLink<T>::append_last(T t){
DNode<T>*pnode = new DNode<T>{ t, phead->prev, phead };
phead->prev->next = pnode;
phead->prev = pnode;
count++;
return 0;
}
int del(int index):删除 index位置的节点:
//删除index位置的节点
template<class T>
int DoubleLink<T>::del(int index){
DNode<T> * pindex = get_node(index);
pindex->next->prev = pindex->prev;
pindex->prev->next = pindex->next;
delete pindex;
count--;
return 0;
}
每次插入或者删除链表节点,count 都要加1或者减1.
int delete_first():删除第一个节点。
//删除第一个节点
template<class T>
int DoubleLink<T>::delete_first(){
return del(0);
}
int delete_last():删除最后一个节点。
//删除最后一个节点
template<class T>
int DoubleLink<T>::delete_last(){
return del(count - 1);
}
链表的声明和定义要在同一个文件里,声明和定义分文件的话,会报错的,因为模板编程不支持这样,具体可以参考
下面是头文件的全部代码:
// DoubleLink.h
#include <iostream>
//声明双链表节点的模板类结构体
template<class T>
struct DNode
{
public:
T value;
DNode * prev;
DNode * next;
public:
//默认构造函数
DNode(){}
//拷贝构造函数
DNode(T t, DNode * prev, DNode * next){
this->value = t;
this->prev = prev;
this->next = next;
}
};
//声明双链表的模板类
template<class T>
class DoubleLink{
public:
DoubleLink();
~DoubleLink();
int size();
int is_empty();
T get(int index);
T get_first();
T get_last();
int insert(int index, T t);
int insert_first(T t);
int append_last(T t);
int del(int index);
int delete_first();
int delete_last();
private:
int count;
DNode<T> * phead;
DNode<T> * get_node(int index);
};
//定义双链表的模板类
template<class T>
DoubleLink<T>::DoubleLink():count(0){
//创建表头
phead = new DNode<T>();
phead->prev = phead->next = phead;
}
template<class T>
DoubleLink<T>::~DoubleLink(){
//删除所有节点
DNode<T> * ptmp;
DNode<T> * pnode = phead->next;
while (pnode != phead){
ptmp = pnode;
pnode = pnode->next;
delete ptmp;
}
delete phead;
phead = NULL;
}
//获取双链表节点数
template<class T>
int DoubleLink<T>::size(){
return count;
}
//判断双链表是否为空
template<class T>
int DoubleLink<T>::is_empty(){
return count == 0;
}
//获取index位置的节点
template<class T>
DNode<T>* DoubleLink<T>::get_node(int index){
if (index < 0 || index >= count){
std::cout << "get node failed! the index in out of bound!" << std::endl;
return NULL;
}
//正向查找
if (index <= count / 2){
int i = 0;
DNode<T> * pindex = phead->next;
for (i; i < index; i++){
pindex = pindex->next;
}
return pindex;
}
//反向查找
DNode<T> *rpindex = phead->prev;
for (int j = 0; j < count - index - 1; j++){
rpindex = rpindex->prev;
}
return rpindex;
}
//获取index位置的节点的值
template<class T>
T DoubleLink<T>::get(int index){
return get_node(index)->value;
}
//获取第一个节点的值
template<class T>
T DoubleLink<T>::get_first(){
return get_node(0)->value;
}
//获取最后一个节点的值
template<class T>
T DoubleLink<T>::get_last(){
return get_node(count - 1)->value;
}
//将节点插入到第index位置之前
template<class T>
int DoubleLink<T>::insert(int index, T t){
if (index == 0){
return insert_first(t);
}
DNode<T>*pindex = get_node(index);
DNode<T>*pnode = new DNode<T>{ t, pindex->prev, pindex };
pindex->prev->next = pnode;
pindex->prev = pnode;
count++;
return 0;
}
//将节点插入到第一个节点处
template<class T>
int DoubleLink<T>::insert_first(T t){
DNode<T> *pnode = new DNode<T>{ t, phead, phead->next };
phead->next->prev = pnode;
phead->next = pnode;
count++;
return 0;
}
//将节点插入到最后一个节点处
template<class T>
int DoubleLink<T>::append_last(T t){
DNode<T>*pnode = new DNode<T>{ t, phead->prev, phead };
phead->prev->next = pnode;
phead->prev = pnode;
count++;
return 0;
}
//删除index位置的节点
template<class T>
int DoubleLink<T>::del(int index){
DNode<T> * pindex = get_node(index);
pindex->next->prev = pindex->prev;
pindex->prev->next = pindex->next;
delete pindex;
count--;
return 0;
}
//删除第一个节点
template<class T>
int DoubleLink<T>::delete_first(){
return del(0);
}
//删除最后一个节点
template<class T>
int DoubleLink<T>::delete_last(){
return del(count - 1);
}
下面做个测试:
#include "DoubleLink.h"
#include <string>
void int_test(){
DoubleLink<int> *pdlink = new DoubleLink<int>();
pdlink->insert_first(20);
pdlink->append_last(10);
pdlink->insert(1, 90);
std::cout << "双链表第一个节点为:" << pdlink->get_first() << std::endl;
std::cout << "双链表最后一个节点为:" << pdlink->get_last() << std::endl;
pdlink->delete_first();
pdlink->delete_last();
pdlink->del(0);
if (pdlink->is_empty()){
std::cout << "DoubleLink is empty" << std::endl;
}
else{
std::cout << "DoubleLink is not empty" << std::endl;
}
for (int i = 0; i < pdlink->size(); i++){
std::cout << pdlink->get(i) << std::endl;
}
}
void string_test(){
DoubleLink<std::string> *pslink = new DoubleLink<std::string>();
//pslink->append_last("Hello world! key!");
pslink->insert_first("Hello world! key!");
pslink->insert(0, "My name is zky");
pslink->append_last("呵呵");
for (int i = 0; i < pslink->size(); i++){
std::cout << pslink->get(i) << std::endl;
}
}
void object_test(){
struct stu{
int id;
char name[20];
};
static struct stu arr_stu[] = {
{0,"小强"},
{ 1, "小李" },
{2,"小刚"}
};
DoubleLink<stu> *polink = new DoubleLink<stu>();
polink->insert(0, arr_stu[0]);
polink->insert_first(arr_stu[2]);
for (int i = 0; i < polink->size(); i++){
std::cout << polink->get(i).id << " " << polink->get(i).name << std::endl;
}
}
int main(){
//int_test();
//string_test();
object_test();
std::cin.get();
return 0;
}
好了,双链表的学习就到这了,后续会学习其他数据结构。