目录

1、 2.两数相加

1.1 算法原理

1.2 算法代码

2、24.两两交换链表中的节点

2.1 算法原理

2.2 算法代码

3、143. 重排链表

3.1 算法原理

3.2 算法代码

4、合并 K 个升序链表

4.1 算法原理 --- 解法一

4.2 算法原理 --- 解法二

4.2.1 算法代码

4.3 算法原理 --- 解法三

 4.3.1 算法代码

5、K 个一组翻转链表

5.1 算法原理

5.2 算法代码


1、 2.两数相加

. - 力扣(LeetCode)

1.1 算法原理

解题的核心就是, 记录两节点数值之和, 并保留进位信息:

  • tmp // 记录两节点相加之和 => tmp += cur1.val + cur2.val;
  • tmp % 10 // 表示该位应得值(新节点的值) => tail.next = new ListNode(tmp % 10);
  • tmp /= 10 // 表示下一位的进位

1.2 算法代码

/**
 * 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 addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode newHead = new ListNode();
        ListNode tail = newHead;
        int tmp = 0;
        ListNode cur1 = l1;
        ListNode cur2 = l2;
        while(cur1 != null || cur2 != null || tmp != 0) {
            if(cur1 != null) {
                tmp += cur1.val;
                cur1 = cur1.next;
            }
            if(cur2 != null) {
                tmp += cur2.val;
                cur2 = cur2.next;
            }
            tail.next = new ListNode(tmp % 10);
            tail = tail.next;
            tmp /= 10;
        }
        return newHead.next; 
    }
}

2、24.两两交换链表中的节点

. - 力扣(LeetCode)

2.1 算法原理

每翻转两个节点, 需要修改四个引用的指向, 定义4个变量:

  • 当 cur 为 null, 或者 next 为 null 时, 说明链表已全部翻转完成.
  • 其中 next 以及 nnext 的更新, 涉及空指针异常, 需要额外判断

2.2 算法代码

/**
 * 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 Solution1 {
    public ListNode swapPairs(ListNode head) {
        ListNode newHead = new ListNode(0, head);
        if(head == null || head.next == null) return head;
        ListNode prev = newHead, cur = head, next = cur.next, 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、143. 重排链表

. - 力扣(LeetCode)

3.1 算法原理

核心思想: 

  1. 找链表的中间节点, 根据中间节点分割链表(快慢双指针).
  2. 将第二个链表逆序(头插法)
  3. 依次合并两个链表 

3.2 算法代码

/**
 * 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 void reorderList(ListNode head) {
        ListNode fast = head, slow = head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode flg = slow;
        ListNode head2 = new ListNode();
        while(slow != null) {
            ListNode next = slow.next;
            slow.next = head2.next;
            head2.next = slow;
            slow = next;
        }
        ListNode cur1 = head, cur2 = head2.next;
        ListNode newHead = new ListNode();
        ListNode tail = newHead;
        // slow 是要逆序的链表(第二个链表)中的, 第二个链表更长
        while(cur2 != null) {
            // 第一个链表的结尾指向不为 null 
            if(cur1 != flg ) {
                tail.next = cur1;
                tail = cur1;
                cur1 = cur1.next;
            }
            tail.next = cur2;
            tail = cur2;
            cur2 = cur2.next;
        }
    }
}

4、合并 K 个升序链表

. - 力扣(LeetCode)

4.1 算法原理 --- 解法一

暴力的思想, 一个一个的进行合并, 时间复杂度直逼 O(n^3) => 不推荐

4.2 算法原理 --- 解法二

借助优先级队列(小堆), 将每个链表中最小的元素入堆, 选出值最小的那个节点, 进行出堆操作(接着将下一个节点入堆), 并将这个最小的节点拼接到新链表中.

  • 由于 k 个链表一共有 k*n 个元素, 且使用队列选出最小元素后, 队列会进行O(logK)级别的调整, 最终, 可将时间复杂度优化为 O(N*K*logK)

4.2.1 算法代码

/**
 * 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 mergeKLists(ListNode[] lists) {
        if(lists.length == 0) return null;
        // 建小堆
        PriorityQueue<ListNode> queue = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);
        // 将每个链表中最小的节点放到堆中
        for (ListNode node : lists) {
            // 节点可能为空
            if(node != null) 
                queue.offer(node);
        }
        ListNode newHead = new ListNode();
        ListNode tail = newHead;
        // 堆为空时, 说明合并完成
        while(!queue.isEmpty()) {
            ListNode node = queue.poll();
            tail.next = new ListNode(node.val);
            tail = tail.next;
            if(node.next != null) {
                queue.offer(node.next);
            }
        }
        return newHead.next;
    }
}

4.3 算法原理 --- 解法三

以递归方式, 采用 归并排序的思想, 进行合并.

时间复杂度同样可优化为: N*K*O(logK)

  • K 条链表, 递归 logK 层, 每层需要合并 K 次, 每次合并有 N 个节点. => N*K*O(logK)

 4.3.1 算法代码

/**
 * 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 mergeKLists(ListNode[] lists) {
        if(lists.length == 0) return null;
        return mergeSort(lists, 0, lists.length - 1);
    }
    public ListNode mergeSort(ListNode[] lists, int left, int right) {
        if(left >= right) return lists[right];
        int mid = (left + right) / 2;
        ListNode cur1 = mergeSort(lists, left, mid);
        ListNode cur2 = mergeSort(lists, mid + 1, right);

        ListNode newHead = new ListNode();
        ListNode tail = newHead;
        while(cur1 != null && cur2 != null) {
            if(cur1.val <= cur2.val) {
                tail.next = cur1;
                tail = tail.next;
                cur1 = cur1.next;
            }else {
                tail.next = cur2;
                tail = tail.next;
                cur2 = cur2.next;
            }
        }
        if(cur1 != null) {
            tail.next = cur1;
        }
        if(cur2 != null) {
            tail.next = cur2;
        }
        return newHead.next;
    }
}

5、K 个一组翻转链表

. - 力扣(LeetCode)

5.1 算法原理

  • K 个节点为一组, 进行翻转操作: 节点总个数 n / K => 得到一共要翻转的组数
  • 每一组通过头插法进行翻转操作
  • 细节点: 需要记录每组链表的头结点, 将翻转后相邻两组的链表进行连接

5.2 算法代码

 

/**
 * 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 reverseKGroup(ListNode head, int k) {
        int n = 0;
        ListNode cur = head;
        while(cur != null) {
            n++;
            cur = cur.next;
        }
        // 翻转多少组
        n /= k;
        ListNode newHead = new ListNode();
        // 每组链表的头结点
        ListNode prev = newHead;
        ListNode now = head;
        int count = 0;
        for(int i = 0; i < n; i++) {
            // 记录头结点
            ListNode tmp = now;
            while(count != k) {
                ListNode next = now.next;
                now.next = prev.next;
                prev.next = now;
                now = next;
                count++;
            }
            prev = tmp;
            count = 0;
        }
        // 连接后续不需要翻转的节点
        prev.next = now;
        return newHead.next;
    }
}

END

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部