算法:链表题(持续更)

coding now / 2023-08-05 / 原文

本人刷题时思考的几个解法,欢迎交流
力扣链接:合并 2 个有序链表
力扣链接:合并K个有序链表


目录
  • 合并 2 个有序链表
  • 合并 k 个有序链表
    • K 个中 minNode
    • 排序队列取minNode队头
      • 手动实现的排序队列
      • 优先级队列
    • 分治:两个一组逐渐合并


合并 2 个有序链表

合并 2 个有序链表
操作模型:

for -> cur = min(list1 OR list2)

细节优化前:

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode dummy = new ListNode(0);
        ListNode p = dummy;
        while(list1 != null || list2 != null){
            if(list1 == null){
                p.next = list2;
                break;
            } else if(list2 == null) {
                p.next = list1;
                break;
            }
            if(list1.val < list2.val){
                p.next = list1;
                list1 = list1.next;
            } else {
                p.next = list2;
                list2 = list2.next;
            }
            p = p.next;
        }
        return dummy.next;
    }
}

细节优化后:

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null) return list2;
        if(list2 == null) return list1;

        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;
        ListNode p1 = list1;
        ListNode p2 = list2;

        while(p1 != null && p2 != null) {
            if(p1.val < p2.val){
                cur.next = p1;
                p1 = p1.next;
            } else {
                cur.next = p2;
                p2 = p2.next;
            }
            cur = cur.next;
        }
        cur.next = p1 != null ? p1 : p2;
        return dummy.next;
    }
}

合并 k 个有序链表

K 个中 minNode

每次迭代,比较 K 个链表头,求最小值 minNode,加入返回链表
直到迭代完所有链表
时间复杂度 O(K * N)
运行时间:210ms
操作模型:

for -> (cur = min(lists.forEach -> node))

优化细节前代码:

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null) return null;
        if(lists.length == 0) return null;

        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;

        ListNode min;
        int minPos;
        boolean keep = true;

        while(keep){
            keep = false;
            minPos = 0;
            min = lists[0];
            
            for(int p = 0; p < lists.length; p++) {
                if(lists[p] == null) continue;

                if(keep != true) keep = true;

                if(min == null || (lists[p]).val < min.val) {
                    min = lists[p];
                    minPos = p;
                }
            }

            if(keep != true) break;

            cur.next = min;
            lists[minPos] = min.next;
            cur = cur.next;
        }

        return dummy.next;
    }
}

优化细节后:

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;

        ListNode minNd;
        int minPos;

        while(true){
            minPos = -1;
            minNd = null;
            
            for(int i = 0; i < lists.length; i++) {
                if(lists[i] == null) continue;

                if(minNd == null || lists[i].val < minNd.val) {
                    minNd = lists[i];
                    minPos = i;
                }
            }
            if(minPos == -1) break;

            cur.next = minNd;
            lists[minPos] = lists[minPos].next;
            cur = cur.next;
        }

        return dummy.next;
    }
}

排序队列取minNode队头

手动实现的排序队列

运行时间:216ms

class Solution {
    static class SortedQueue {
        private static final LinkedList<ListNode> list = 
			new LinkedList<>();

        ListNode popHead() {
            return list.removeFirst();
        }
        void sortedPut(ListNode insert){
            if (insert == null) return;
            int index = 0;
            for (ListNode node : list) {
                if (node.val >= insert.val){
                    list.add(index, insert);
                    return;
                }
                index++;
            }
            if (index != 0){
                list.addLast(insert);
            } else {
                list.addFirst(insert);
            }
            return;
        }
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            for (ListNode node : list){
                builder.append(node.val).append(" ");
            }
            return builder.toString();
        }
    }
}

未加嵌套的单次逻辑:

public ListNode mergeKLists(ListNode[] lists) {
     ListNode dummy = new ListNode(0);
     ListNode p = dummy;

     SortedQueue queue = new SortedQueue();

     p.next = queue.popHead();
     if(p.next.next != null){
        queue.sortedPut(p.next.next);
     }
     p = p.next;
 }

加入嵌套、嵌套退出的条件:

while(queue.list.size() != 0){ ...... }

完整代码:

class Solution {
    static class SortedQueue {
        static final LinkedList<ListNode> list = new LinkedList<>();

        ListNode popHead() {
            return list.removeFirst();
        }
        void sortedPut(ListNode insert){
            if (insert == null) return;
            int index = 0;
            for (ListNode node : list) {
                if (node.val >= insert.val){
                    list.add(index, insert);
                    return;
                }
                index++;
            }
            if (index != 0){
                list.addLast(insert);
            } else {
                list.addFirst(insert);
            }
            return;
        }
    }

    public ListNode mergeKLists(ListNode[] lists) {
        ListNode dummy = new ListNode(0);
        ListNode p = dummy;

        SortedQueue queue = new SortedQueue();
        for(ListNode node : lists){
            queue.sortedPut(node);
        }

        while(queue.list.size() != 0){
            p.next = queue.popHead();
            if(p.next.next != null){
                queue.sortedPut(p.next.next);
            }
            p = p.next;
        }
        return dummy.next;
    }
}

优先级队列

本质是小根堆,小根堆的根节点就是最小值,相比于我实现的队列,它的插入效率更高
运行时间:4ms

class Solution {
   public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) return null;
        PriorityQueue<ListNode> queue = new PriorityQueue<>(lists.length, new Comparator<ListNode>() {
            @Override
            public int compare(ListNode o1, ListNode o2) {
                if (o1.val < o2.val) return -1;
                else if (o1.val == o2.val) return 0;
                else return 1;
            }
        });
        ListNode dummy = new ListNode(0);
        ListNode p = dummy;
        for (ListNode node : lists) {
            if (node != null) queue.add(node);
        }
        while (!queue.isEmpty()) {
            p.next = queue.poll();
            p = p.next;
            if (p.next != null) queue.add(p.next);
        }
        return dummy.next;
    }
}

分治:两个一组逐渐合并

运行时间:1ms

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0){
            return null;
        }
        return spilt(lists , 0 , lists.length - 1);
    }
    public ListNode spilt(ListNode[] lists , int i , int j){
        if(i == j){
            return lists[i];
        }
        int m = (i + j) >>> 1;
        ListNode left = spilt(lists , i , m);
        ListNode right = spilt(lists , m+1 , j);
        return mergeTwoLists(left, right);
    }
    public ListNode mergeTwoLists(ListNode p1, ListNode p2) {
        ListNode s = new ListNode(-1, null);
        ListNode p = s;
        while (p1 != null && p2 != null) {
            if (p1.val < p2.val) {
                p.next = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }
        p.next = p1 != null ? p1 : p2;
        return s.next;
    }
}