数据结构学习笔记——C++实现双向循环链表模板类(超详解)

定义了两个头文件分别放置结点类模板(Node.h)和双链表模板(DoubleLinkList.h),
然后在源文件的main函数中测试。

Node.h

#pragma once
# include <iostream>

template <class DataType>
class Node
{
	
public:
	DataType data;
	Node<DataType> *prior;
	Node<DataType> *next;
	Node();
	Node(DataType data_, Node<DataType> *prior_, Node<DataType> *next_);

};
template <class DataType>
Node<DataType>::Node()//定义一个空结点
{
	prior = NULL;
	next = NULL;
}

template <class DataType>
Node<DataType>::Node(DataType data_, Node<DataType> *prior_ , Node<DataType> *next_ )//定义一个完整结点
{
	prior = prior_;
	next = next_;
	data = data_;
}

DoubleLinklist.h

/*
实现双向循环链表
实现的运算操作有
 增 ,删 ,改 ,查 
 初始化, 清空 ,求线性表长度
*/

#include "Node.h"
using namespace std;
template <class DataType>
class DoubleLinkList
{
	int length;
	Node<DataType> *head;
public:
	DoubleLinkList();//初始化————初始化一个空表,
	DoubleLinkList(const  DataType data_[],int n);//初始化————初始化一个含有数据的表
	void Insert(const DataType data_, int pos);//增————向某个位置pos之前插入一个数据data_
	void Delete(int pos);//删————删除某个位置pos处的结点
	void Change(const DataType data_,int pos);//改————改动某个位置pos处的结点
	DataType Search1(int pos);//查————根据下标查数据
	int Search2(const DataType &e);//查————根据数据查下标
	void Clear();//清空————仅保留头节点
	int GetLength();//得到长度
	void PrintAll();//遍历链表输出各节点数值
	~DoubleLinkList();//析构————删除所有结点,释放所有指针

};


//初始化————初始化一个空表(只有头节点)
template <typename  DataType>
DoubleLinkList<typename  DataType>::DoubleLinkList()
 {
	head = new Node<DataType>; //动态分配一个空结点,即头节点
	assert(head);
	head->prior = head;//头节点的首指针指向其本身
	head->next = head;//头节点的尾指针指向其本身
	length = 0;
 }


//初始化————初始化一个含有数据的表
//实参用数组传入要写入的数据
//由于C++中没有自带直接求数组长度的函数 
//所以需要手动把数组的长度n写进去
template <typename  DataType>
DoubleLinkList<typename  DataType>::DoubleLinkList(const DataType data_[],int n)
{
	head = new Node<DataType>; //动态分配一个空结点,即头节点
	Node<DataType> *p = head;//创建一个指向结点的指针p后面将用这个指针遍历,实现不断的加数据结点
	for (int i = 0; i < n; i++) {
		//这两行代码其实包含了四个操作
		//step1 :new运算符建立下一个结点 且通过构造函数将数据域赋值为data_[i] 
		//step2:通过构造函数将该结点的首指针指向p(即上一个节点) 尾指针指向NULL
		//step3:通过赋值运算符将上一个结点的尾指针指向下一个结点
		//step4 将指针p移动至下一个结点 以便下一步的迭代
		p->next = new Node<DataType>(data_[i], p,NULL);
		p = p->next;
	}
	length = n;
	p->next = head;
	head->prior = p;
}


//增————向某个位置pos插入一个数据data_
template <typename  DataType>
void DoubleLinkList<typename  DataType>::Insert(const  DataType data_, int pos)
{
	//step0:准备工作 建立要遍历的指针p和新建要插入的数据结点add
	Node<DataType> *p = head;//创建一个指向结点的指针p后面将用这个指针遍历
	Node<DataType> *add = new Node<DataType>(data_, NULL, NULL);//建立一个新结点,包含要插入的数据data_
	//step1:判断插入位置是否正确
	if (pos<1 || pos>length+1)
	{
		std::cout << "插入数据失败" << endl;
	}
	
	else
	{
		//step2,用指针p进行遍历,直到达到指定位置
		int i;
		for(i = 0; i < pos; i++)
		{
			p = p->next;
		}
		//step3 ,开始插入
		add->prior = p->prior;
		p->prior->next = add;
		p->prior = add;
		add->next = p;
		length += 1;
		std::cout << "插入数据成功" << endl;
	}

}

//删————删除某个位置pos的结点
template <typename  DataType>
void DoubleLinkList<typename  DataType>::Delete(int pos)
{
	//step0:准备工作 建立要遍历的指针p和新建要插入的数据结点add
	Node<DataType> *p = head;//创建一个指向结点的指针p后面将用这个指针遍历
	//step1:判断删除位置是否正确
	if (pos<1 || pos>length)
	{
		std::cout << "删除数据失败" << endl;
	}
	else
	{
		int i;
		for(i = 0; i < pos; i++)
		{
			p = p->next;
		}
	//step2:开始删除
		p->prior->next = p->next;
		p->next->prior = p->prior;
		length = length - 1;
		std::cout << "删除数据成功" << endl;
		delete p;//千万注意此处要delete指针p!
	}
}


//改————改动某个位置pos处的结点
template <typename  DataType>
void DoubleLinkList<typename  DataType>::Change(const DataType data_,int pos)
{
	//step0:准备工作 建立要遍历的指针p和新建要改动的数据结点add
	Node<DataType> *p = head;//创建一个指向结点的指针p后面将用这个指针遍历
	
	//step1:判断插入位置是否正确
	if (pos<1 || pos>length + 1)
	{
		std::cout << "改动数据失败" << endl;
	}

	else
	{
		int i;
		//step2,用指针p进行遍历,直到达到指定位置
		for(i = 0; i < pos; i++)
		{
			p = p->next;
		}
		//step3 ,开始改动
		Node<DataType> *change = new Node<DataType>(data_, p->prior, p->next);//建立一个新结点,包含要改动的数据data_
		p->prior->next = change;
		p->next->prior = change;
		std::cout << "改动数据成功" << endl;
		delete p;//千万注意此处要delete指针p!
	}

}
//查————根据下标查数据
template <typename  DataType>
DataType DoubleLinkList<typename  DataType>::Search1(int pos)
{
	//step0:准备工作 建立要遍历的指针p
	Node<DataType> *p = head;//创建一个指向结点的指针p后面将用这个指针遍历
	//step1:判断查找位置是否正确
	if (pos<1 || pos>length)
	{
		std::cout << "查找数据失败" << endl;
		return 0;
	}
	else
	{
		int i;
		for(i = 0; i < pos; i++)
		{
			p = p->next;
		}
		//step2:返回查找数据
		return p->data;
		std::cout << "查找数据成功" << endl;

	}
}


//查————根据数据查下标
template <typename  DataType>
int DoubleLinkList<typename  DataType>::Search2(const DataType &e)
{
	//step0:准备工作 建立要遍历的指针p
	Node<DataType> *p = head;//创建一个指向结点的指针p后面将用这个指针遍历
	//step1:开始查找
	int i = 0;
	while((i<length)&&(p->data != e))
	{
		i++;
		p = p->next;
	}
	if (p == head)
	{
		return 0;
		std::cout << "找不到该数据" << endl;
	}
	else
	{
		return i;
		std::cout << "成功找到该数据" << endl;
	}
	
}

//清空
template <typename  DataType>
void DoubleLinkList<typename  DataType>::Clear()
{
	int i = 0;
	while (i < length)
	{
		i++;
		Delete(1);
	}
	length = 0;
}
//析构
template <typename  DataType>
DoubleLinkList<typename  DataType>::~DoubleLinkList()
{
	Clear();
	delete head;
	cout << "析构函数已执行" << endl;
}

//求线性表长度
template <typename  DataType>
int DoubleLinkList<typename  DataType>::GetLength()
{
	return length;
}
//遍历输出
template <typename  DataType>
void DoubleLinkList<typename  DataType>::PrintAll()
{
	int i;
	Node<DataType> *p = head;//创建一个指向结点的指针p后面将用这个指针遍历
	cout << "该链表的所有数据如下" << endl;
	for (i = 0; i < length; i++)
	{
		p = p->next;
		cout << p->data << " ";
	}
}

 

main

 

#include "DoubleLinkList.h"
#include<iostream>
int main()
{
	int data[4] = { 2, 3, 4, 12};
	DoubleLinkList<int> list(data, 4);//初始化
	list.PrintAll();
	cout << "线性表的长度为" << list.GetLength() << endl;//获取链表的长度
	list.Insert(62,4);//增
	list.PrintAll();
	list.Delete(3);//删
	list.PrintAll();
	list.Change(35, 1);//改
	list.PrintAll();
	int data1 = list.Search1(3);
	int pos1 = list.Search2(35);
	cout << "链表中位置3处的数据为  " << data1 << "数据35在链表中的位置为  " << pos1;//查
	while (1);

	
}

总结出现过的小问题:

1.定义链表模板类的时候
除了构造函数,其他成员函数中不要初始化头节点head = new Node<DataType>; 
因为这样会导致该函数后面的指针p指针指向新的head,即变成空指针,无法指向链表头节点。
2.要注意输入过程中输入法中英文的切换 ,经常出现中文的括号,特别不易察觉。
3.在类模板的成员函数定义时 ,
如template <typename  DataType>
DoubleLinkList<typename  DataType>::DoubleLinkList() 
 最好使用typename关键字 用class可能会报错

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值