C++实现单链表

C++实现单链表

最近在学习数据结构的链表部分,发现大多数网上写的单链表多多少少都有些瑕疵,并且解释不够清晰,对于初学者很不友好。更有甚者C和C++代码混用,对于只学过C++的同学看起来很费劲。虽然这里属于数据结构的基础知识,但是还是建议小白对C++基本的语法能够了解,并且知道单链表的构造,要不然每一句的解释敲的再详细也看不懂。

下面是代码和相应解释:

#include <iostream>
using namespace std;

template <class T>//建立模板,使其能够放入多种数据
class Node//创建结点类
{
public:
	T data;//数据域
	Node<T>* next;//指针域
};

template<class T>
class linkList //创建链表类
{
private:
	//注意每次用Node时要加上模板参数
	Node<T>* front;//建立头指针
public:
	linkList()
	{
		front = new Node<T>; //在堆区开辟头结点,并用头指针维护
		//front->data = 0;//我觉得这不对,因为data是T型,用0置空合适吗
		front->next = NULL;//头结点置空
	}
	~linkList();
	//下面这两种没有参数的链表创建法的局限是只能让T为一些基础类型,
	//没有办法传入复杂类型,如果需要传入复杂类型要事先把数据放入到
	//数组中,然后给成员函数传入数组和数组长度做形参
	void createList1();// 头插法建立链表
	void createList2();//尾插法
	//按照位序查找元素,找到了返回结点指针(这个返回指针很有用,后面会用到)
	//找不到返回NULL
	Node<T>* getElem(int No);
	//按值查找,找到返回位置,找不到返回-1
	int locateElem(T &data);
	//插入操作
	void insert(int No, T data);
	//删除操作
	void Delete(int No);
	//打印操作
	void printList();
};
template <class T>
linkList<T>::~linkList()
{
	Node<T>* p = front;//初始化指针p=头指针,让他指向头结点
	while (p)//当p不为NULL时,执行语句
	{
		//这里不直接删除指针p指向的堆区数据的原因是顺序问题
		//如果删掉p,而没用front备份,那么p将无法偏移。
		front = p;//front随着p走,delete front 来释放堆区数据
		p = p->next;//p为工作指针,p不断偏移
		delete front;
	}
}

template <class T>
void linkList<T>::createList1()//头插法
{
	Node<T>* p = this->front;//建立工作指针,初始化指向头结点
	T data;
	int length = 0;
	cout << "输入链表长度" << endl;
	cin >> length;
	for (int i=0; i < length; i++)
	{
		cout << "输入第" << i + 1 << "个值" << endl;
		cin >> data;
		Node<T>* temp = new Node<T>; //每一个结点都在堆区开辟一个新的,用temp指针维护
		temp->data = data;
		//这里可以先分情况看
		//第一种情况是现在表是空表,那么头结点的指针域指向NULL,那么当前结点的指针域也
		//要指NULL,即和p->next相等。然后再让p->next(头指针指针域)指向当前结点
		//第二种情况是现在不是空表,那么头插时,当前结点要插在头结点和首元结点之间
		//由于在第一次插入的时候,p->next已经指向了首元结点,即第一个temp,
		//所以当前结点也要指向首元结点,即,temp->next=p->next;
		//最后更新p(头结点),让头结点指针域指向当前结点
		//所以这两种情况是一种写法。
		temp->next = p->next;
		p->next = temp;
	}
}

template <class T>
void linkList<T>::createList2()//尾插法
{
	Node<T>* p = front;//建立工作指针
	int length = 0;
	T data;
	cout << "输入新链表长度" << endl;
	cin >> length;
	for (int i = 0; i < length; i++)
	{
		Node<T>* temp = new Node<T>;
		cout << "输入第" << i + 1 << "个数据" << endl;
		cin >> data;
		temp->data = data;
		//这两步就不难了,让当前结点的指针域和工作指针的结点指针域相同
		//就是让工作结点指向当前结点
		//然后更新工作结点p。
		p->next=temp;
		p = temp;
	}
	p->next = NULL;
}

template <class T>
Node<T>* linkList<T>::getElem(int No)
{
	//这个函数时访问不到头结点的
	Node<T>* p = front->next;//这里工作指针就不从头结点开始了,从首元结点开始
	int j = 0;//首元结点算0号位置
	//太巧妙了,j作为计数器一直++,同时p指针不断偏移,直到和No相等。
	//为了防止超出链表长度,&&了p,限制了p不能指空。
	while (p && j != No)
	{
		p = p->next;
		//当没有找到得时候,也就是No超出了链表长度,p最后指向了NULL,
		//返回了一个空指针,所以这里返回值必需返回指针而不是Node<T>
		j++;
	}
	return p;
}

template <class T>
int linkList<T>::locateElem(T& data)
{
	Node<T>* p = front->next;//还是从首元结点开始建立工作指针
	int i = 0;
	while (p)//遍历链表
	{
		if (p->data == data)//同样,这里的==只适用于基础变量型,如果是自定义变量,则需要重载==
		{
			return i;
		}
		else
		{
			p = p->next; //不相等时工作指针偏移
			i++;//位序偏移
		}
	}
	return -1;//找不到返回-1
}
template <class T>
void linkList<T>::insert(int No, T data)
{
	//本质上是在No结点后插入了新结点,然后将No结点数据赋值给新结点
	//再让No结点接受形参data,完成了插入
	
	Node<T>* p = this->getElem(No);//初始化工作指针
	if (p)
	{
		Node<T>* newNode = new Node<T>;//堆区新建结点
		newNode->data = p->data;//把位序指针数据给新节点
		newNode->next = p->next;
		p->data = data;
		p->next = newNode;
	}
	else
	{
		cout << "插入失败" << endl;
	}
}

template <class T>
void linkList<T>::Delete(int No)
{
	Node<T>* p = front;//初始化工作指针
	Node<T>* temp = this->getElem(No);
	if (No > 0&&temp)
	{
		p = this->getElem(No - 1);//工作指针指向被删除结点前一个结点
		p->next = temp->next;
		delete temp;

	}
	else if (No == 0)
	{
		p->next = temp->next;//头结点指向第二个结点
		delete temp;
	}

}
template <class T>
void linkList<T>::printList()
{
	Node<T>* p = front->next;//初始化工作节点,指向首元结点
	while (p)
	{
		cout << p->data << "  ";
		p = p->next;
	}
	cout << endl;
}

int main()
{
	linkList<int> l1;
	l1.createList2();
	l1.printList();
	Node<int> *p=l1.getElem(3);
	cout << p->data << endl;
	l1.Delete(2);
	l1.printList();
	l1.insert(3, 10);
	l1.printList();
	int find = 5;
	int pos = l1.locateElem(find);
	cout << pos << endl;
	system("pause");
	return 0;
}

以下是测试结果:
测试结果

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值