线性表之单链表

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


**基本思想:**用一个指针域来存储下一个节点的地址,这样连起来就是一个地址不连续的表了。但是它还是线性的,只是存储方式不一样。

**优点:**动态增长,除开内存空间不够大之外,可以无限存储。较为灵活。头添加方式速度最快,其时间复杂度为0(1)。

**缺点:**代码较为复杂,查询较之数组比较慢。

C代码实现下载
C++代码实现下载
java代码实现下载
(备用下载地址)

两种存储方式的比较:
1.添加
顺序表添加操作也分为头尾两种,链表也是,其中顺序表的尾添加和链表的头添加的时间复杂度都是0(1)。

2.插入
经过比较发现,二者的插入平均复杂度都是0(n/2)。其中顺序表的时间耗在元素的后移,链表则是耗在遍历到指定节点前/后插入。

3.修改
修改的话,顺序表可以直接数组索引到该元素,时间复杂度为0(1),链表则需要遍历到指定节点,再进行修改,时间复杂度为0(n)。

4.删除

删除和插入的时间复杂度是一样的。

所以,综上所述,貌似链表的优点就只剩下动态这个特性了。其实顺序表也可以实现动态增长,这个后面会讲到。

功能实现:

节点类的实现如下:

template<typename T>
class Node    //节点类
{
public://初始化构造一个节点
	Node<T>(T d, Node<T>* n = NULL) : data(d), next(n) {} 
public:
	T data;
	Node<T>* next;
};

假设int count = 0; //记录链表的大小

用到的节点如图:

1.bool IsEmpty(); 判断表是否为空

count为0,就返回true,否则返回false

2.int Size(); 获取元素个数

直接返回count

3.void AddFromHead(Node* node); 添加数据(头添加)

头添加代码最简单了,一共两步
1).node4->next = head->next(意思是让node4指向node1)
2).head->next = node4(意思是,让head指向node4)

如图:

4.void AddFromTail(Node* node); 添加数据(尾添加)

尾添加多了一步遍历操作,要先遍历到链表末尾,一共三步
1).遍历到末尾,得到node3节点
2).node3->next = node4(意思是让node3指向node4)
3).node4->next = NULL(意思是,让node4指向NULL)

如图:

5.void Insert(int local, Node* node); 插入数据(指定位置)

插入操作其实就是遍历+头添加操作,一共三步
1).遍历到node2位置,得到node2节点
2).node4->next = node2->next(意思是让node4指向node3)
3).node2->next = node4(意思是,让node2指向node4)

如图:

6.void DeleteFromLocal(int local); 删除数据 (指定位置)

删除操作一共四步
这里需要两个指针,一个pre指向前一个节点,一个cur指向当前节点。
1).遍历到node2位置,得到node2节点(cur)
2).创建临时节点temp,temp = cur
3).pre->next = temp->next(意思是node1节点指向node3节点)
4).delete cur(意思是,释放cur节点)

如图:

7.void DeleteFromElement(T e); 删除数据 (指定元素)

和6差不多

8.int SearchFromElmemt(T e); 根据指定元素查找,返回位置

调用GetLocal();

9.T SearchFromLocal(int local); 根据位置查找指定元素

遍历到指定位置,直接返回该元素

10.void Reverse1(); 逆转表方法1

利用顺序表中的方法,就是头尾交换,但是因为链表中获取某个节点都需要遍历一次,再加上外层一个循环,所以时间复杂度就是0(n^2)了。一共分三步进行。
1).外层一个for循环,控制头尾交换(只是交换数据,指针域并没有改变)
2).依次获取头尾两个节点,用GetNode函数(这里的头尾不是特指第一个和最后一个,而是指各从头尾往中间交换。)
3).交换数据项。

如图:

11.void Reverse2(); 逆转表方法2

这个方法的思想是,把表1的数据,倒序复制到表2中,然后再从表2的数据复制回表1中,即可完成倒叙,其时间复杂度为0(n),这种方法我觉得可行,现在拿空间换时间挺值的。一共分为2步。
1).把表1的数据,利用AddFromHead添加到表2中(这时,数据已经逆序了,需要注意的是,一定要每个节点都复制,不能只简单的赋值(=),否则下面改变一个节点后,后面的节点就被截断了。)
2).把表2的数据依次复制回表1,完成表逆转。

12.void Print(); 遍历元素

就是依次输出。

13.void CopyList(LinkList& sl); 复制表

将表1的节点一一添加到表2中,需要注意的是,添加之前,要创建新的节点,然后把表1的节点依次复制过去,再添加。不要犯两个指针指向同一空间的错误。一共分两步。
1).将表1的数据,复制到表2
2).调用逆转2函数,将数据转回来(因为是采用的头节点添加)

14.void Rewrite(int local, T e); 修改元素

遍历到指定位置,修改其数据域即可

15.void ClearLinkList(); 清空链表

不能直接修改头节点指向NULL,这样后面的节点并没有被释放掉,造成内存泄漏。一共分为4步进行。
1).保存前一个节点的地址
2).向前查找下一个节点
3).释放前一个节点的空间
4).最后修改head指向NULL

16.Node* GetNode(int local); 获取指定位置的节点

遍历+返回

17.void SortByAsc(); 升序排序

直接调用SelectSort函数

18.void SortByDesc(); 降序排序

直接调用SelectSort函数

19.int GetLocal(T e); 返回元素位置

一一比较,找到返回当前位置

20.void SelectSort(bool bAsc = true) 选择排序法

bAsc = true表示升序,false表示降序

线性表之顺序表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值