算法原理
链表常用的技巧和操作总结
常用技巧
1.画图,画图更加形象直观,便于我们理解
2.引入虚拟"头结点"(便于处理边界情况,方便对链表操作)
3.不要吝啬空间,大胆定义变量
比如在下图所示的情况,如果想将cur添加到prev后面,如果不定义变量的话会比较复杂,但如果我们定义一个变量指向prev.next,那么情况就变得简单了,我们就不需要考虑步骤的先后顺序

4.快慢双指针
定义快慢双指针,可以用来判断环,找链表中环入口,找倒数第n个结点(具体的题可以见链表的博客)
常用操作
1.创建一个新节点
2.头插法 在逆序链表中很好用
3.尾插法 定义一个tail
题目解析
1.两数相加
https://leetcode.cn/problems/add-two-numbers/description/
题目描述
给两个非空的链表,表示两个非空的整数,他们的每位数都是逆序存储的,并且每个节点只存一个数字,将两数相加,并以相同的形式返回和的链表
算法原理
解法:模拟两数相加的过程
比如:一个链表 2->4->3 一个链表是5->6->3
我们先定义一个newHead来指向头结点,这样便于返回
然后用变量t来标识进位
同时也要注意边界
代码实现
java
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head=new ListNode();
ListNode cur1=l1,cur2=l2;
int t=0;
ListNode cur=head;
while(cur1!=null||cur2!=null||t!=0){
if(cur1!=null){
t+=cur1.val;
cur1=cur1.next;
}
if(cur2!=null){
t+=cur2.val;
cur2=cur2.next;
}
cur.next=new ListNode(t%10);
cur=cur.next;
t/=10;
}
return head.next;
}
}
2.两两交换链表中的节点
https://leetcode.cn/problems/swap-nodes-in-pairs/description/
题目描述
给你一个链表,两两交换相邻的节点,并返回交换后的头节点(注意,只能交换节点,而不能只交换节点的值)
eg: 一个链表 1->2->3->4 交换后2->1->4->3
算法原理
解法一:递归
解法二:循环,迭代(模拟)
如果链表为空/链表只有一个节点,直接返回

代码实现
java
class Solution {
public ListNode swapPairs(ListNode head) {
if(head==null||head.next==null) return head;
ListNode newHead=new ListNode();
newHead.next=head;
ListNode prev=newHead;
ListNode cur=prev.next;
ListNode next=cur.next;
ListNode nnext=next.next;
while(cur!=null&&next!=null){
prev.next=next;
next.next=cur;
cur.next=nnext;
prev=cur;
cur=nnext;
if(cur!=null){
next=cur.next;
}
if(next!=null){
nnext=next.next;
}
}
return newHead.next;
}
}
3.重排链表
https://leetcode.cn/problems/reorder-list/description/
题目描述
给定一个链表的头结点head,单链表表示为L0->L1->...Ln,重排链表后,链表为L0->Ln->L1->Ln-1->...
(注意不能只是改变链表中的值,要进行节点交换)
算法原理
1.找到链表的中间节点(要用到快慢双指针)
2.把后面的部分逆序(双指针,头插)
3.合并两个链表(双指针)

代码实现
java
class Solution {
public void reorderList(ListNode head) {
if(head==null||head.next==null||head.next.next==null) return;
//1.找到链表的中间节点
ListNode slow=head,fast=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
}
// 2.将slow后面的部分逆序(头插)
ListNode nH=new ListNode(0);
ListNode cur=slow.next;
slow.next=null;
while(cur!=null){
ListNode next=cur.next;
cur.next=nH.next;
nH.next=cur;
cur=next;
}
//3.合并两个链表
ListNode cur1=head,cur2=nH.next;
ListNode ret=new ListNode(0);
ListNode prev=ret;
while(cur1!=null){
prev.next=cur1;
prev=cur1;
cur1=cur1.next;
if(cur2!=null){
prev.next=cur2;
prev=cur2;
cur2=cur2.next;
}
}
return ;
}
}
4.合并k个升序链表
https://leetcode.cn/problems/merge-k-sorted-lists/description/
题目描述
给一个链表数组,每个链表都按照升序排列,请将所有链表合并到一个升序链表中,返回合并后的链表
例如

算法原理
解法一:暴力解法(以合并两个有序链表为基,两两合并,再合并) 时间复杂度0(nK^2)
解法二:利用优先级队列做优化 将所有链表放入优先级队列(可以以ListNode作为泛型参数 ,自己提供比较方法,小跟堆) 然后定义一个头节点,不断从优先级队列出元素接在头结点后面
解法三:分治-递归

代码实现
java
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> heap=new PriorityQueue<>((v1,v2)-> v1.val-v2.val);
for(ListNode head:lists){
if(head!=null){
heap.offer(head);
}
}
ListNode ret=new ListNode(0);
ListNode prev=ret;
while(!heap.isEmpty()){
ListNode t=heap.poll();
prev.next=t;
prev=t;
if(t.next!=null) heap.offer(t.next);
}
return ret.next;
}
}
java
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
return merge(lists,0,lists.length-1);
}
public ListNode merge(ListNode[] lists,int left,int right){
if(left>right){
return null;
}
if(left==right){
return lists[left];
}
//1.先平分数组
int mid=(left+right)/2;
//2.递归左右两边
ListNode l1=merge(lists,left,mid);
ListNode l2=merge(lists,mid+1,right);
//3.合并两个有序链表
return mergeTwo(l1,l2);
}
public ListNode mergeTwo(ListNode l1,ListNode l2){
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
ListNode head=new ListNode(0);
ListNode cur=head;
ListNode cur1=l1,cur2=l2;
while(cur1!=null&&cur2!=null){
if(cur1.val<cur2.val){
cur.next=cur1;
cur=cur1;
cur1=cur1.next;
}else{
cur.next=cur2;
cur=cur2;
cur2=cur2.next;
}
}
if(cur1!=null){
cur.next=cur1;
}
if(cur2!=null){
cur.next=cur2;
}
return head.next;
}
}
5.k个一组翻转链表
https://leetcode.cn/problems/reverse-nodes-in-k-group/description/
题目描述
给链表的头结点head,每k个结点一组进行翻转,请返回修改后的链表(注意,要进行节点的交换)

算法原理
解法:模拟
1.首先要求出要逆序多少组 :n(要先遍历一遍链表)
2.重复n次长度为k的链表的逆序即可(注意头插)
代码实现
java
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
//1.先求出要逆序多少组
ListNode cur=head;
int n=0;
while(cur!=null){
cur=cur.next;
n++;
}
n/=k;
//2.重复n次
ListNode nH=new ListNode(0);
ListNode prev=nH;
cur=head;
for(int i=0;i<n;i++){
ListNode tmp=cur;
for(int j=0;j<k;j++){
ListNode next=cur.next;
cur.next=prev.next;
prev.next=cur;
cur=next;
}
prev=tmp;
}
prev.next=cur;
return nH.next;
}
}