单链表的问题没什么模板,大家按照自己习惯都有各自的方法去删除,插入等。
对于力扣的问题也没有太难的题,都是基本的方法的组合,比如从反转链表到k个一组反转链表,合并两个链表到合并k个有序链表,我们从最基本的开始。
public class ListNode {
int val;
ListNode next;
public ListNode(){}
//给两个有参构造,方便建立普通节点和哨兵节点
public ListNode(int val){
this.val = val;
}
public ListNode(int val,ListNode next){
this.val = val;
this.next = next;
}
//重写了toString方法,本地ide调试看起来舒服一点
@Override
public String toString() {
return "" +
"" + val +
"->" + next
;
}
}
private static ListNode BuildNode(int[] nums){
//建立哨兵节点
ListNode new_head = new ListNode(-1);
ListNode cur = new_head;
//按照数组尾插节点进来
for (int val: nums) {
ListNode temp = new ListNode(val);
cur.next = temp;
cur = temp;
}
return new_head.next;
}
本文给出原地反转的方法,还有递归的方法等。
//反转链表
private static ListNode reverse(ListNode head){
//通过cur对链表遍历,将该节点指向前一个pre节点
ListNode pre = null;
ListNode cur = head;
//遍历链表
while(cur!=null){
//保存下一个节点,防止丢失
ListNode new_cur = cur.next;
cur.next = pre;
pre = cur;
cur = new_cur;
}
//返回头节点,此时头节点就是pre
return pre;
}
对于合并两个有序链表的思路:
建立一个头节点和指针pre,对于两个链表的头节点进行比较,较小的加入pre的next。
对于
l1={1,2,4};
l2={3,5}
两个链表:
//合并链表
private static ListNode merge(ListNode head,ListNode head1){
//哨兵节点,方便返回头节点
ListNode new_head = new ListNode(-1);
ListNode pre = new_head;
//选最小的加入新链表
while(head!=null&&head1!=null){
if(head.val< head1.val){
pre.next = head;
head = head.next;
}else{
pre.next = head1;
head1 = head1.next;
}
pre = pre.next;
}
//剩下的链表直接加入尾部
pre.next = head ==null ? head1 : head;
//返回新链表头节点
return new_head.next;
}
private static ListNode Mydelete(ListNode head){
ListNode new_head = new ListNode(-1,head);
ListNode pre = new_head;
ListNode cur = head;
while(cur!=null){
//找到重复的一直走到最后
while(cur.next!=null&&cur.val==cur.next.val){
cur = cur.next;
}
//删除之前的重复的,若没有的重复的,相当于重新指向了下一个节点
pre.next = cur;
pre = cur;
cur = cur.next;
}
return new_head;
}
思路就是合并两个有序链表,加上归并排序的思路:
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
return sort(lists,0,lists.length-1);
}
//归并或者分治的思路
private ListNode sort(ListNode[] lists,int left,int right){
//一直分割到剩一个节点
if(left==right){
return lists[left];
}else{
int mid = (left+right)/2;
ListNode l = sort(lists,left,mid);
ListNode r = sort(lists,mid+1,right);
//合并并且递归
return mergeTwo(l,r);
}
}
//合并两个有序链表
private ListNode mergeTwo(ListNode l1, ListNode l2){
ListNode new_head = new ListNode(-1);
ListNode pre = new_head;
while(l1!=null&&l2!=null){
if(l1.val<l2.val){
pre.next = l1;
l1 = l1.next;
}else{
pre.next = l2;
l2 = l2.next;
}
pre = pre.next;
}
pre.next = l1==null ? l2 : l1;
return new_head.next;
}
}
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if(head==null||head.next==null){
return head;
}
//先用哨兵节点
ListNode new_head = new ListNode(-1,head);
//pre代表反转的链表的前置节点
ListNode pre = new_head;
//cur代表当前链表的尾部
ListNode cur = new_head;
while(cur.next!=null){
//找到当前组的尾部
for(int i=0; i<k&&cur!=null; i++){
cur = cur.next;
}
//如果不够长度直接返回
if(cur==null){
break;
}
//断链之前保存下一个节点
ListNode new_cur = cur.next;
//必须断链,否则后边的一起反转了
cur.next = null;
ListNode[] ls = reverse(pre.next);
//指向反转后的链表
pre.next = ls[0];
//到链表尾部,准备下一次反转
pre = ls[1];
cur = pre;
//将断链接上
cur.next = new_cur;
}
return new_head.next;
}
//反转链表,这次将尾部也存入返回,以便继续向下走
private ListNode[] reverse(ListNode head){
ListNode head1 = head;
ListNode pre = null;
ListNode cur = head;
while(cur!=null){
ListNode new_cur = cur.next;
cur.next = pre;
pre = cur;
cur = new_cur;
}
return new ListNode[]{pre,head1};
}
}
总结:链表的题并没有太大的难度,主要就是遍历,插入,删除比如和递归,排序的结合。
关于合并排序可以参照:leetcode刷题思路-----排序。