数据结构——单链表

1.介绍

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。

单链表中每个结点的结构如下:

数据域保存了存储在该地址的数据,但指针域的指针却存放的不是该地址,而是下一个元素的地址

因此,整个单链表的结构如下:

 最后一个元素的指针域的指针设为nullptr,表示单链表到此结束

一般情况下,为了操作统一,设置了一个在所有数据结点之前的一个结点,称为头结点。

可以利用该结点的数据部分存储单链表的个数。

而指向头节点的指针称为头指针

2.操作

假设单链表长度为n,头指针为head,每个数据结点数据变量为data,指针变量为next(表示指向下一个地址的指针)。

(1)插入

因为单链表结构的原因,插入我们一般会找到要插入位置的前一个结点,假设为pre。假设要插入的结点为p,之后只需要这样的操作:

p->next=pre->next;

pre->next=p;

即如下图:

不过插入尾部时,pre->next不是上图所表示的结点,而是nullptr,但上面代码仍可适用。

顺序不能颠倒,一旦颠倒,我们就将找不回原来pre->next保存的地址,pre之后的结点将永远也找不回来,这样会造成内存泄漏。

而有无头节点的区别之一,就在于插入一个在所有单链表数据节点之前的位置的结点。

1.假如有头节点,那么我们插入的每一个位置之前都必定会有一个直接前驱结点,那么上面的操作对于有头节点的单链表所有插入位置都适用。

2.假如没有头节点,那么我们插入结点p在第一个位序位置时,需要特殊的进行下面的操作:

p->next=head;

head=p;

即如下图:

插入的时间复杂度和查找到要插入位置前一个结点的操作有关。

插入第一个位置,只需要找到头节点,时间复杂度O(1)。

插入尾部,需要找到最后一个元素,时间复杂度O(n)。

平均情况下,分别有n+1种情况,分别为找到头节点,第一个数据结点...最后一个结点。

因此,平均要找(0+1+2+...+n)/(n+1)=n/2次,因此平均时间复杂度为O(n)。

上面的都是找到前一个结点A,然后将结点B插入在结点A的后面,如果我们要在一个结点A的前面插入一个结点B呢?

比较笨的方法就是找到结点A的前一个结点,进行后插。

更好的是,先插入一个结点B在结点A的后面,然后将数据部分互换。 

(2)删除

删除一个结点,首先确保有结点可以删。

其次,我们需要找到前面的结点pre,假设要删除的结点为p,之后就可以这样操作:

pre->next=p->next;

delete p;

即如下图:

只不过删除最后一个结点,我们的p->next是nullptr,而不是上图表示的结点,但无伤大雅。

这也是有无头节点的另一个区别。

1.假如有头节点,那么我们删除的每一个结点之前都必定会有一个直接前驱结点,那么上面的操作对于有头节点的单链表所有结点都适用。

2.假如,没有头节点,假设要删除的第一个结点为p,此时p=head,那么需要下面的操作:

head=head->next;

delete p;

 因此,有头节点的单链表实在是操作简便的多。因为我们插入删除就不需要再来单独考虑第一个数据元素。

应用:

单链表经典的应用就是计算一个含有未知量的数学表达式。

例如:5*x+25*x²+2*x³

可以将node的数据域记录下每个未知量前面的系数、次方数(也可以不用保存,但需要单链表按次方数顺序排列,系数为0的结点不可省略)

设计成员函数,改变x值,计算表达式。

还可以两个表达式之间合并,合并之前根据对存储的次方大小让两个链表排序

注意当对应次方前面的常量相加为0时,就不用再耗费内存来保存。

下面的代码我们采用有头节点的单链表的方式,其他一些操作代码如下:

#pragma once
#include<iostream>
template<typename T>
class LinkList
{
public:
	struct node 
	{
		T data;
		node* next;
	public:
		node(T d=0,node* n=nullptr):data(d),next(n){}
		~node() {  };
	};
	LinkList();
	~LinkList();
	void insert(int i,const T& val);
	void remove(int i);
	node* search(const T& val)const;//查找,返回对应的结点指针
	void change(int i, const T& val);
	void print()const;//打印单链表
	int size()const;
	void tailInsert();//尾插法创建单链表
	void headInsert();//头插法创建单链表
	
private:
	node* head;
	node* moveTo(int i);//将第i个结点指针返回
};
//时间复杂度O(n)
template<typename T>
typename LinkList<T>::node* LinkList<T>::moveTo(int i)
{
	//如果i为-1,会返回头指针,如果为0,则返回第一个数据节点,错误返回nullptr
	if (i <-1)
		return nullptr;
	node* p = head;
	for (int u = 0; u < i + 1; u++)
	{
		p = p->next;
		if (!p)//防止p已经为nullptr,继续下一个循环的操作。
			break;
	}
	return p;
}
//空间复杂度O(1)
template<typename T>
LinkList<T>::LinkList()
{
	head = new node;
}
//时间复杂度O(n)
template<typename T>
LinkList<T>::~LinkList()
{
	node* p = head;
	head = nullptr;
	while (p)
	{
		node* q = p;
		p = p->next;
		delete q;
	}
}
//时间复杂度O(n)
template<typename T>
void LinkList<T>::insert(int i, const T& val)
{
	//如果i出了范围moveTo函数会发出错误
	node* pre = moveTo(i - 1);
	if (!pre)
	{
		std::cout << "error" << std::endl;
		return;
	}
	node* p = new node(val,pre->next);
	pre->next = p;
}
//时间复杂度O(n)
template<typename T>
void LinkList<T>::remove(int i)
{
	//如果i出了范围moveTo函数会发出错误
	node* pre = moveTo(i - 1);
	if (!pre)
	{
		std::cout << "error" << std::endl;
		return;
	}
	node* p = pre->next;//防止pre是尾元素
	if (!p)
	{
		std::cout << "error" << std::endl;
		return;
	}
	pre->next = p->next;
	delete p;
}
//时间复杂度为O(n)
template<typename T>
typename LinkList<T>::node* LinkList<T>::search(const T& val)const
{
	node* p = head->next;
	while (p)
	{
		if (p->val != val)
			p = p->next;
	}
	//如果没有找到,会返回nullptr
	return p;
}
//时间复杂度为O(n)
template<typename T>
void LinkList<T>::change(int i, const T& val)
{
	node* p = moveTo(i);
	p->val = val;
}
//时间复杂度为O(n)
template<typename T>
void LinkList<T>::print()const
{
	node* p = head->next;
	while (p)
	{
		std::cout << p->data << " ";
		p = p->next;
	}
	std::cout << std::endl;
}
//时间复杂度为O(1)
template<typename T>
int LinkList<T>::size()const
{
	node* p = head->next;
	int count = 0;
	while (p)
	{
		count++;
		p = p->next;
	}
	return count;
}
//时间复杂度为O(n)
template<typename T>
void LinkList<T>::tailInsert()
{
	T temp=0;
	node* p = head;
	while (std::cin >> temp)
	{
		node* q = new node(temp);
		p->next = q;
		p = q;
	}
}
//时间复杂度为O(n)
template<typename T>
void LinkList<T>::headInsert()
{
	T temp=0;
	while (std::cin >> temp)
	{
		node* p = new node(temp,head->next);
		head->next = p;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值