链表专题终极版(JAVA)


链表嘛,穿针引线,指针满天飞的那种。我们需要注意严防非法越界的问题!!!

  • 链表的题通常需要注意两点:
  1. 舍得用变量,千万别想着节省变量,否则容易被逻辑绕晕
  2. head 有可能需要改动时,先增加一个 假head,返回的时候直接取 假head.next,这样就不需要为修改 head 增加一大堆逻辑了。
  • 但我觉得整体来说它的处理还是比较简单的。处理思路比较套路化吧!看到链表题第一步便想双指针法,大部分题目都可以由双指针解决
  • 问题的关键在于我们无法直接得到链表的长度!
  • 无非就是双指针法,虚拟头节点,递归,分治这些!

虚拟头节点可以使得我们的代码处理变得简单,有了它这个占位符,可以避免处理空指针的情况,降低代码的复杂性!


第一部分

合并两个有序的链表

方法一:双指针,虚拟头节点

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode node = new ListNode(-1);
        ListNode pre = node;
        while(list1 != null && list2 != null){
            if(list1.val < list2.val){
                pre.next = list1;
                list1 = list1.next;
            }else{
                pre.next = list2;
                list2 = list2.next;
            }
            pre = pre.next;
        }
        if(list1 != null){
            pre.next = list1;
        }
        if(list2 != null){
            pre.next = list2;
        }
        return node.next;
    }
}

方法二:递归

public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null){
            return list2;
        }else if(list2 == null){
            return list1;
        }
        if(list1.val>list2.val){
            list2.next = Merge(list1,list2.next);
            return list2;
        }else {
            list1.next = Merge(list1.next,list2);
            return list1;
        }
    }

合并K个有序链表

方法一:递归+分治

思路分析:

  • 将两个链表合并。(递归方法合并)
  • 分而治之!求一个mid,将mid左边的合并,右边的合并,最后将左右两边的链表合并。
  • 重复这一过程,直到获取最终的有序链表。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
import java.util.*;
public class Solution {
        //合并K个有序链表的方法
    public ListNode mergeKLists(ArrayList<ListNode> lists) {
        return mergeList(lists,0,lists.size()-1);
    }
    public ListNode mergeList(ArrayList<ListNode> lists,int left,int right){
        if(left==right) return lists.get(left);
        if(left>right) return null;
        int mid=left+(right-left)/2;
        return merge(mergeList(lists,left,mid),mergeList(lists,mid+1,right));
    }
        //合并两个有序链表(和力扣的21题一样)
    public ListNode merge(ListNode l1,ListNode l2){
        if(l1==null)return l2;
        if(l2==null) return l1;
        if(l1.val<l2.val) {
            l1.next=merge(l1.next,l2);
            return l1;
        }else{
            l2.next=merge(l1,l2.next);
            return l2;
        }
    }
}

方法二:使用优先级队列

把链表节点放入一个最小堆,就可以每次获得 k 个节点中的最小节点。

  • 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在某些情况下,我们可能需要找出队列中的最大值或者最小值,例如使用一个队列保存计算机的任务,一般情况下计算机的任务都是有优先级的,我们需要在这些计算机的任务中找出优先级最高的任务先执行,执行完毕后就需要把这个任务从队列中移除。普通的队列要完成这样的功能,需要每次遍历队列中的所有元素,比较并找出最大值,效率不是很高,这个时候,我们就可以使用一种特殊的队列来完成这种需求,优先队列。

优先队列按照其作用不同,可以分为以下两种:

  • 最大优先队列: 可以获取并删除队列中最大的值
  • 最小优先队列: 可以获取并删除队列中最小的值
将元素放入队列:add,offer
将队首元素从队列删除:remove,poll
查看队列内的对首元素:element,peek

Java 优先队列 (PriorityQueue) - 简书 (jianshu.com)

  • 优先队列 pq 中的元素个数最多是 k,所以一次 poll 或者 add 方法的时间复杂度是 O(logk);所有的链表节点都会被加入和弹出 pq所以算法整体的时间复杂度是 O(Nlogk),其中 k 是链表的条数,N 是这些链表的节点总数
ListNode mergeKLists(ListNode[] lists) {
    if (lists.length == 0) return null;
    // 虚拟头结点
    ListNode dummy = new ListNode(-1);
    ListNode p = dummy;
    // 优先级队列,最小堆
    PriorityQueue<ListNode> pq = new PriorityQueue<>(
        lists.length, (a, b)->(a.val - b.val));
    // 将 k 个链表的头结点加入最小堆
    for (ListNode head : lists) {
        if (head != null)
            pq.add(head);
    }

    while (!pq.isEmpty()) {
        // 获取最小节点,接到结果链表中
        ListNode node = pq.poll();
        p.next = node;
        if (node.next != null) {
            pq.add(node.next);
        }
        // p 指针不断前进
        p = p.next;
    }
    return dummy.next;
}
class Solution {
    class Status implements Comparable<Status> {
        int val;
        ListNode ptr;

        Status(int val, ListNode ptr) {
            this.val = val;
            this.ptr = ptr;
        }

        public int compareTo(Status status2) {
            return this.val - status2.val;
        }
    }

    PriorityQueue<Status> queue = new PriorityQueue<Status>();

    public ListNode mergeKLists(ListNode[] lists) {
        for (ListNode node: lists) {
            if (node != null) {
                queue.offer(new Status(node.val, node));
            }
        }
        ListNode head = new ListNode(0);
        ListNode tail = head;
        while (!queue.isEmpty()) {
            Status f = queue.poll();
            tail.next = f.ptr;
            tail = tail.next;
            if (f.ptr.next != null) {
                queue.offer(new Status(f.ptr.next.val, f.ptr.next));
            }
        }
        return head.next;
    }
}
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
import java.util.*;
public class Solution {
    public ListNode mergeKLists(ArrayList<ListNode> lists) {
        if(lists.size() == 0){
            return null;
        }
        ListNode dummy = new ListNode(-1);
        ListNode p = dummy;
        PriorityQueue<ListNode> pq = new PriorityQueue<ListNode>(lists.size(), (a, b) -> (a.val - b.val));
        for(ListNode x : lists){
            if(x != null){
                pq.offer(x);
            }
        }
        while(!pq.isEmpty()){
            ListNode node = pq.poll();
            p.next = node;
            if(node.next != null){
                pq.offer(node.next);
            }
            p = p.next;
        }
        return dummy.next;
    }
}

链表中倒数最后K个结点

输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。如果该链表长度小于k,请返回一个长度为 0 的链表。

方法一:双指针解法

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pHead ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        if(pHead == null){
            return null;
        }
        ListNode low = new ListNode(-1);
        low.next = pHead;
        ListNode fast = low;
        for(int i = 0 ; i < k; i++){
            fast = fast.next;
            if(fast == null){
               return null;
            }
        }
        while(fast.next != null){
            fast = fast.next;
            low = low.next;
        }
        return low.next;
    }
}

方法二:用栈解决

public ListNode FindKthToTail (ListNode pHead, int k) {
        Stack<ListNode> stack = new Stack<>();
        while(pHead != null){
            stack.push(pHead);
            pHead = pHead.next;
        }
        ListNode ans = null;
        for(int i = k;;i--){
            if(stack.isEmpty()){
                return null;
            }else if(i==1){
                ans = stack.pop();
                return ans;
            }else {
                stack.pop();
            }
        }
    }

方法三:差值法

我们可以先对链表进行一次完整遍历,拿到总长度 cntcnt,最后由 cnt - kcntk 即是倒数第 kk 个节点距离 headhea**d 节点的距离。

public ListNode FindKthToTail (ListNode pHead, int k) {
        int count = 0;
        ListNode temp = pHead;
        while(temp!=null){
            count++;
            temp = temp.next;
        }
        count = count - k;
        if(count<0){
            return null;
        }else {
            while (count--!=0){
                pHead = pHead.next;
            }
            return pHead;
        }
    }

删除链表的倒数第n个结点

这题和上一题的思路差不多,我们还是用双指针解决!

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null){
            return null;
        }
        ListNode low = new ListNode(-1);
        low.next = head;
        ListNode res = low;
        ListNode fast = low;
        for(int i = 0; i < n; i++){
            fast = fast.next;
            if(fast == null){
                return null;
            }
        }
        while(fast.next != null){
            fast = fast.next;
            low = low.next;
        }
        low.next = low.next.next;
        return res.next;
    }
}

其实从这道题我们也可以看出来虚拟头结点用起来有多么方便了,它可以为我们省去许多判断空指针之类的条件的语句,用起来用起来!!!

两个链表的第一个公共结点

其余题解见我的上一篇链表题解,这里主要是展现一下双指针解法,体会这种解法的巧妙!

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode pa = pHead1,pb = pHead2;
        while (pb != pa){
            pa = pa == null?pHead2:pa.next;
            pb = pb == null?pHead1:pb.next;
        }
        return pa;
    }

判断链表中是否有环

还是双指针解法!

每当慢指针 slow 前进一步,快指针 fast 就前进两步。如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。

此题注意while里面的循环条件!!!!

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode low = head, fast = head;
        while(fast != null && fast.next != null){
            low = low.next;
            fast = fast.next.next;
            if(low == fast){
                return true;
            }
        }
        return false;
    }
}

链表中环的入口结点

给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

这个便是上一题的进阶版,也就是需要计算环的结点,通过画图推导(距离关系,以及快慢指针速度的二倍关系)我们可以得知:两个指针相遇后,再使用两个指针,一个从链表起始点开始,一个从相遇点开始,每次他们都走一步,直到再次相遇,那么这个相遇点就是环的入口。

public ListNode EntryNodeOfLoop(ListNode pHead) {
        ListNode slow = pHead;//快指针
        ListNode fast = pHead;//慢指针
        while (fast != null && fast.next != null) {
            //快慢指针,快指针每次走两步,慢指针每次走一步
            fast = fast.next.next;
            slow = slow.next;
            //先判断是否有环,
            if (slow == fast) {
                //确定有环之后才能找环的入口
                while (pHead != slow) {
                    //两指针,一个从头结点开始,
                    //一个从相遇点开始每次走一步,直到
                    //再次相遇为止
                    pHead = pHead.next;
                    slow = slow.next;
                }
                return slow;
            }
        }
        return null;
    }

链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

这题还是想到双指针法,快指针一次两步,慢指针一次一步,快指针走到末尾,慢指针则恰好走到中间结点处!

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode low = head, fast = head;
        while(fast != null && fast.next != null){
            low = low.next;
            fast = fast.next.next;
        }
        return low;
    }
}

删除链表的中间结点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteMiddle(ListNode head) {
        ListNode fast = head;
        ListNode low = new ListNode(-1);
        low.next = head;
        ListNode res = low;
        while(fast != null && fast.next != null){
            low = low.next;
            fast = fast.next.next;
        }
        low.next = low.next.next;
        return res.next;
    }
}

第二部分

递归的思想相对迭代思想,稍微有点难以理解,处理的技巧是:不要跳进递归,而是利用明确的定义来实现算法逻辑。处理看起来比较困难的问题,可以尝试化整为零,把一些简单的解法进行修改,解决困难的问题。

值得一提的是,递归操作链表并不高效。和迭代解法相比,虽然时间复杂度都是 O(N),但是迭代解法的空间复杂度是 O(1),而递归解法需要堆栈,空间复杂度是 O(N)。所以递归操作链表可以作为对递归算法的练习或者拿去和小伙伴得瑟之类的,但是考虑效率的话还是使用迭代算法更好。

反转链表

方法一:迭代解法

我觉得这个方法是比较容易理解的,无非是把指针的指向反一下。

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode pre = null, tmp = head, cur = head;
        while(cur != null){
            tmp = tmp.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

方法二:递归解法

ListNode reverse(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    ListNode last = reverse(head.next);
    head.next.next = head;
    head.next = null;
    return last;
}

看起来真的是不知所云!事实上,这个算法常常被用来显示递归的巧妙和优美!

当链表递归反转之后,新的头结点是 last,而之前的 head 变成了最后一个节点,别忘了链表的末尾要指向 null

可以看一下这篇博文的图解:

链表反转(迭代方式、递归方式)_luren2015的博客-CSDN博客_迭代反转链表

反转链表的前n个结点

将链表的前 n 个节点反转(n <= 链表长度)

ListNode successor = null; // 后驱节点
// 反转以 head 为起点的 n 个节点,返回新的头结点
ListNode reverseN(ListNode head, int n) {
    if (n == 1) {
        // 记录第 n + 1 个节点
        successor = head.next;
        return head;
    }
    // 以 head.next 为起点,需要反转前 n - 1 个节点
    ListNode last = reverseN(head.next, n - 1);

    head.next.next = head;
    // 让反转之后的 head 节点和后面的节点连起来
    head.next = successor;
    return last;
}

链表中指定区间反转

给一个索引区间 [m,n](索引从 1 开始),仅仅反转区间中的链表元素。

方法一:迭代反转

思路:建立一个空白节点指向头节点,然后反转[m,n]内的节点。整体思想是:在需要反转的区间里,每遍历一个节点,让这个新节点来到反转部分的起始位置。

具体实现:使用三个指针变量 precurrnext 来记录反转的过程中需要的变量,它们的意义如下:

  • curr:指向待反转区域的第一个节点 left;
  • next:永远指向 curr 的下一个节点,循环过程中,curr 变化以后 next 会变化;
  • pre:永远指向待反转区域的第一个节点 left 的前一个节点,在循环过程中不变。

可以看一下这篇图解,个人认为还是很清晰的!!!

反转链表 II - 反转链表 II - 力扣(LeetCode) (leetcode-cn.com)

public ListNode reverseBetween (ListNode head, int m, int n) {
             //设置虚拟头节点
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next =head;
        ListNode pre = dummyNode;
        for(int i=0;i<m-1;i++){
            pre = pre.next;
        }
        ListNode cur = pre.next;
        ListNode next ;
        for(int i=0;i<n-m;i++){
            next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next = next ;
        }
        return dummyNode.next;
    }

方法二:递归反转

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @param m int整型 
     * @param n int整型 
     * @return ListNode类
     */
    public ListNode reverseBetween (ListNode head, int m, int n) {
        // write code here
        if(m == 1){
            return reverseN(head, n);
        }
        head.next = reverseBetween(head.next, m - 1, n - 1);
        return head;
    }
    ListNode successor = null;
    public ListNode reverseN(ListNode head, int n){
        if(n == 1){
            successor = head.next;
            return head;
        }
        ListNode last = reverseN(head.next, n - 1);
        head.next.next = head;
        head.next = successor;
        return last;
    }
}

链表中的节点每K个一组翻转

将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。

迭代+递归

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    public ListNode reverseKGroup (ListNode head, int k) {
        // write code here
        if(head == null){
            return null;
        }
        ListNode a, b;
        a = b = head;
        for(int i = 0; i < k; i++){
            //不足K个,不需要反转
            if(b == null){
                return head;
            }
            b = b.next;
        }
        // 反转前 k 个元素
        ListNode newHead = reverse(a, b);
        // 递归反转后续链表并连接起来
        a.next = reverseKGroup(b, k);
        return newHead;
    }
    ListNode reverse(ListNode a, ListNode b){
        ListNode pre = null;
        ListNode cur = a, tmp = a;
        while(cur != b){
            tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

第三部分

删除链表中的节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head == null){
            return null;
        }
        if(head.val == val){
            return head.next;
        }
        ListNode pre = head;
        while(pre != null && pre.next != null){
            if(pre.next.val == val){
                pre.next = pre.next.next;
                break;
            }
            pre = pre.next;
        }
        return head;
    }
}

删除链表中节点(无法访问头节点)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

给链表去重

删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次,例如:
给出的链表为1→1→2,返回1→2.

我在之前的博客中已经讨论过这个问题,当时是利用HashSet的去重性做的:

https://blog.csdn.net/m0_51013067/article/details/120572382?spm=1001.2014.3001.5501

现在我们可以利用双指针来解决这个问题:

  • 注意判断条件是:slow.val != fast.val
public ListNode deleteDuplicates (ListNode head) {
        // write code here
        if(head == null || head.next == null){
            return head;
        }
        ListNode p1 = new ListNode(-1);
        p1.next = head;
        ListNode res = p1;
        ListNode p2 = head;
        while(p2 != null){
            if(p1.val != p2.val){
                p1.next = p2;
                p1 = p1.next;
            }
            p2 = p2.next;
        }
        p1.next = null;
        return res.next;
    }

删除链表中的重复结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5 处理后为 1->2->5

注意此题和上一题的区别!!!

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null) {
            return head;
        }
        
        ListNode dummy = new ListNode(0, head);

        ListNode cur = dummy;
        while (cur.next != null && cur.next.next != null) {
            if (cur.next.val == cur.next.next.val) {
                int x = cur.next.val;
                while (cur.next != null && cur.next.val == x) {
                    cur.next = cur.next.next;
                }
            } else {
                cur = cur.next;
            }
        }

        return dummy.next;
    }
}

判断链表是否是回文结构

反转链表后半部分,然后双指针比较即可。

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 the head
     * @return bool布尔型
     */
    public boolean isPail (ListNode head) {
        // write code here
        ListNode slow, fast;
        slow = fast = head;
        //让slow指针指向链表中点
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        //fast指针没有指向null,说明链表长度为奇数,此时slow再向前一步
        if(fast != null){
            slow = slow.next;
        }
        ListNode left = head;
        //从slow开始反转后面的链表,然后开始比较回文串
        ListNode right = reverse(slow);
        while(right != null){
            if(left.val != right.val){
                return false;
            }
            left = left.next;
            right = right.next;
        }
        return true;
    }
    ListNode reverse(ListNode head) {
    ListNode pre = null, cur = head;
    while (cur != null) {
        ListNode next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
  }
}

链表的奇偶重排

给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。注意是节点的编号而非节点的数值。

输入:

{1,2,3,4,5,6}

返回值:

{1,3,5,2,4,6}
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    public ListNode oddEvenList(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode evenHead = head.next;
        ListNode odd = head, even = evenHead;
        while (even != null && even.next != null) {
            odd.next = even.next;
            odd = odd.next;
            even.next = odd.next;
            even = even.next;
        }
        odd.next = evenHead;
        return head;
    }
}

单链表排序

使用优先队列

class Solution {
    public ListNode sortList(ListNode head) {
        PriorityQueue<ListNode> heap = new PriorityQueue<ListNode>((a,b)->a.val-b.val);
        while(head!=null)
        {
            heap.offer(head);
            head=head.next;
        }
        ListNode dummy=new ListNode();
        ListNode cur = dummy;
        while(heap.size()>0)
        {
            cur.next=heap.peek();
            heap.poll();
            cur=cur.next;
        }
        cur.next=null;//注意这一步
        return dummy.next;
    }
}

归并排序

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution 
{
    public ListNode sortList(ListNode head) 
    {
        return merge_sort(head);
    }
    public ListNode merge_sort(ListNode head)
    {
        if (head == null || head.next == null)
            return head;
        ListNode slow = head;
        ListNode fast = head.next;
        while (fast != null && fast.next != null)
        {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode head2 = slow.next;
        slow.next = null;
        ListNode L = merge_sort(head);
        ListNode R = merge_sort(head2);
        return merge(L, R);
    }
    public ListNode merge(ListNode L, ListNode R)
    {
        ListNode dummy = new ListNode(-1);
        ListNode x = dummy;
        while (L != null && R != null)
        {
            if (L.val < R.val)
            {
                x.next = L;
                L = L.next;
            }
            else
            {
                x.next = R;
                R = R.next;
            }
            x = x.next;
        }
        if (L != null)
            x.next = L;
        if (R != null)
            x.next = R;
        return dummy.next;
    }
}

链表相加

链表相加(二)_牛客题霸_牛客网 (nowcoder.com)

这题用了哨兵技巧。

  • 这是道模拟题,模拟人工竖式做加法的过程:
  • 从最低位至最高位,逐位相加,如果和大于等于 10,则保留个位数字,同时向前一位进 1 如果最高位有进位,则需在最前面补 1。

做有关链表的题目,有个常用技巧:添加一个虚拟头结点(哨兵),帮助简化边界情况的判断。

一些细节:由于给定的两个数值是按照「从高位到低位」进行存储,但我们的计算过程是「从低位到高位」进行,因此在进行计算前,应当先对链表进行翻转。同时,在计算完成后,再对答案链表进行。

import java.util.*;

public class Solution {
    ListNode reverse(ListNode root) {
        ListNode prev = null, cur = root;
        while (cur != null) {
            ListNode tmp = cur.next;
            cur.next = prev;
            prev = cur;
            cur = tmp;
        }
        return prev;
    }
    public ListNode addInList (ListNode head1, ListNode head2) {
        ListNode l1 = reverse(head1), l2 = reverse(head2);
        ListNode dummy = new ListNode(0);
        ListNode tmp = dummy;
        int t = 0;
        while (l1 != null || l2 != null) {
            int a = l1 != null ? l1.val : 0;
            int b = l2 != null ? l2.val : 0;
            t = a + b + t;
            tmp.next = new ListNode(t % 10);
            t /= 10;
            tmp = tmp.next;
            if (l1 != null) l1 = l1.next;
            if (l2 != null) l2 = l2.next;
        }
        if (t > 0) tmp.next = new ListNode(t);
        return reverse(dummy.next);
    }
}

今日推歌

----《如一》 任嘉伦

庭前雪一片一片一片下不停
念着你一颦一笑一时不愿醒
策马人间四季 怎将你相迎
我捧着一生一世一心去笃定
终与你一朝一夕一起赏风景
千万里也愿意奔向你

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星回昭以烂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值