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

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

Node.h

#pragma once
# include 

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

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

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

DoubleLinklist.h

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

#include "Node.h"
using namespace std;
template 
class DoubleLinkList
{
	int length;
	Node *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 
DoubleLinkList::DoubleLinkList()
 {
	head = new Node; //动态分配一个空结点,即头节点
	assert(head);
	head->prior = head;//头节点的首指针指向其本身
	head->next = head;//头节点的尾指针指向其本身
	length = 0;
 }


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


//增————向某个位置pos插入一个数据data_
template 
void DoubleLinkList::Insert(const  DataType data_, int pos)
{
	//step0:准备工作 建立要遍历的指针p和新建要插入的数据结点add
	Node *p = head;//创建一个指向结点的指针p后面将用这个指针遍历
	Node *add = new Node(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 
void DoubleLinkList::Delete(int pos)
{
	//step0:准备工作 建立要遍历的指针p和新建要插入的数据结点add
	Node *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 
void DoubleLinkList::Change(const DataType data_,int pos)
{
	//step0:准备工作 建立要遍历的指针p和新建要改动的数据结点add
	Node *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 *change = new Node(data_, p->prior, p->next);//建立一个新结点,包含要改动的数据data_
		p->prior->next = change;
		p->next->prior = change;
		std::cout << "改动数据成功" << endl;
		delete p;//千万注意此处要delete指针p!
	}

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

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

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

 

main

 

#include "DoubleLinkList.h"
#include
int main()
{
	int data[4] = { 2, 3, 4, 12};
	DoubleLinkList 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
因为这样会导致该函数后面的指针p指针指向新的head,即变成空指针,无法指向链表头节点。
2.要注意输入过程中输入法中英文的切换 ,经常出现中文的括号,特别不易察觉。
3.在类模板的成员函数定义时 ,
如template
DoubleLinkList::DoubleLinkList() 
 最好使用typename关键字 用class可能会报错

你可能感兴趣的:(数据结构学习笔记——C++实现双向循环链表模板类(超详解))