数据结构与算法(链表)~ 介绍链表以及力扣上几道链表题目的方法和套路

数据结构与算法(链表)

1,链表的数据结构

(1)基本实现(组成):由一个一个结点构成。

自己动手实现:定义了一个含有数据域 和 指针的 结点类。

(2)链表主要的功能(增删改查):定义一些接口方法

 (3)过程中进行重构链表,将 增删改查 或者一些通用的接口或者属性封装到外部抽象类或者接口(方便设计给其他类用这样子):

    

 (整个版本一的链表过程如此)

过程中增删改查实现的具体代码就 略。。。

● 增加:可以在任意一个位置进行增加结点

● 删除:可以在任意一个位置进行删除结点

● 修改:可以在任意一个位置进行修改结点

● 查找:可以查找任意一个位置的结点

✿ 升级版本

① 拥有虚拟头结点的链表(统一了增加、删除时接口里的一些条件的判断)

双端(双指针)链表(加快了查找某个位置的结点~ 之前只能从头到尾的遍历,现在多了一个指针,便可以从尾到头遍历啦)

~~JDK1.8中的LinkedList中就是双向链表(从源码分析得知的)

③ 循环链表(让最后一个结点的指针指向了第一个结点)

④ 双端循环链表(有两个指针+ (最后一个结点的指针last 指向了第一个结点 、第一个结点的prev 指针指向最后一个结点))

2,链表的力扣算法题:

 总结一些小套路吧 (没有通用的套路,就讲一下方法哈)

自己需要先知道的是链表的特点就是一个一个结点构成(一般没有特别强调的链表都是普通的链表(只有一个next 指针):找结点都是只能从头到尾去遍历找它)

(1)138_复制带随机指针的链表 的方法 :

方法一:使用递归+ hashMap (因为随机指针:一个节点可能被多个其他节点指向)
  /** hashMpa 避免重复创建 **/
当我们拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路。一个可行方案是,我们利用回溯的方式,让每个节点的拷贝操作相互独立。

方法二:间隔插秧法啦(把秧苗插进来后,以前是走一步,现在多了一个结点要多走一步)
* 第一步:插入苗
* 第二步:先处理随机指针的复制(原本有随机、next的复制都需要处理?):优先处理随机(之所以要把苗插入,就是为了实现在同一流水线具有同一规则,如果先处理next 导致已经变成两条链表了,拿不到随机了)

(2)141_环形链表 的方法与套路 :

方法一:使用 HashSet 实现不重复的添加 【Set 集合特点:不重复】

// HashSet 底层是HashMap(添加元素时会判断是否已经存在,已经存在会添加不成功)

if (!visited.add(head)) {
      return true;
 }

套路一:快慢指针法 (生活常识:操场跑圈,跑得快的最终会追上跑得慢的)

(3)142_环形链表II 的方法与套路 :

方法一:【Set 集合特点:不重复】,通过 contains() 方法进行判断

套路一:快慢指针法  [ 追上之后,通过将特殊位置点(起点、 交点、相遇点)~ 快慢指针之间的位移差距是两倍--> 推导出起点到交点 = 相遇点到交点]

(4)160_相交链表 的方法与套路 :

方法一:Set集合(装入一条链表,然后以它为标准,依次拿另外一条链表的每个结点与它对比)

套路一:处理一下长度,使得两条链表的长度相同后进行同步运动(相遇了即是交点)

(5)19_删除链表的倒数第N个结点 的方法与套路 :

方法一:通过求总长len - N + 1 得到需要删除链表的前一个结点

套路一:倒序~联系到栈(后进先出~倒序思维):通过栈(后进先出(pop掉 后面 第N个,从而可以拿到待删除结点的前一个结点))

套路二:本质上是将倒数转化成(两个点之间)具体的距离,通过设置两个距离是n 的指针(不断的往后走,走到最后,差距便是倒数)~ 数据-->距离

(6)2_两数相加 的套路 :

套路一 官网用 || 把链表是否为空的所有情况都考虑(加上利用题意:将空链的数据初始为 0, 过程是将两条链的数据进行相加)

套路框架:

        while (l1 != null || l2 != null){
                   if (l1 != null) {    
                     l1 = l1.next;
                 }
                 if (l2 != null) {
                     l2 = l2.next;
                 }        
         }

(7)203_移除链表元素 的方法和套路 :

方法一:递归,在从第二个结点开始进行移除,最后考虑上第一个结点的情况

套路一:加上了一个虚拟头结点后,拿自身结点的next 去 比较,一旦next 是待删除目标结点,则自身便是那个待删除结点的前一个结点

(8)206_反转链表 的方法

方法一:递归

方法二:头插法

(9)21_合并两个有序链表 的方法 和 套路

方法一:递归(将第一条链表从第二个结点开始与 第二条链表进行比较,然后再处理第一条链表剩下的第一个结点的情况)

套路一:两条链同时进行比较大小, 当其中一条链表数据到达了null,再处理剩下另外一条链表的数据

套路二:(6)官网用 || 把链表是否为空的所有情况都考虑(加上利用题意:将空链的数据初始为 0 )

(10)23_合并K个升序链表 的方法 和 套路:

方法一:暴力法(一条一条添加进来的合并法)

套路一:分治合并法 (将 k 条不断地进行划分成两部分,直到 部分中的链表数量为 1,开始二合一)

套路二:优先队列法(就是小根堆(具体代码就是 PriorityQueue类(长度,比较器))~从小到大排序:

每条链表会依据链表头的大小顺序进行排序~ 链表头小的放入前面,大的放到后面(从小到大~ 内部自动排序哈哈哈)),

每次都弹出链表头最小的链表(然后取走链表头,又放回剩余的链表(让它根据剩余链表的头在queue 中进行排序))。

(11)24_两两交换链表中的节点(这题不错) 的方法 和 套路:

方法一:递归(根据题意,明白它是按照一对一对的结点进行交换位置的),递归先处理掉从第二对(第三个结点开始)到最后一对的情况,剩下前面一对(第一、二个结点)再进行处理。

套路一:通过虚拟头结点(解决指针越界的情况):因为 第一个结点需要第二个结点,交换之后,需要指向第三个结点(没有虚拟结点的情况下,需要考虑第三个结点是否存在)

(12)83_删除排序链表中的重复元素 的方法

方法一:遍历删除

方法二:递归 (删除 掉 从第二个结点开始重复的结点,然后考虑递归返回的链表的头,与原先链表的头的比较)

(13)86_分隔链表 的方法

方法一:遍历小的数据放到小的链表中,大的数据放到大的链表中,最后把小链表和大链表连起来

(14)876_链表的中间结点 的方法

方法一:遍历先得到链表长度 len,然后中间的结点,只需移动 len / 2 步

(15)92_反转链表II 的方法

套路一:通过虚拟头结点(可以避免复杂的分类讨论)

原文地址:https://www.cnblogs.com/shan333/p/15451636.html