• 链表算法


    链表1

    class ListNode {
        int val;
        ListNode next;
    
        ListNode(int x) {
            val = x;
            next = null;
        }
    }
    

    找出两个单链表相交的起始节点

    /**
     * 思路:
     *     去除长链表左边长的部分,将两个链表变成相等长度的链表,一起前进,找出是否有相等的位置
     */
    class Solution1 {
        public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
            if (headA == null || headB == null) {
                return null;
            }
    
            ListNode a = headA;
            int lena = 0;
            ListNode b = headB;
            int lenb = 0;
            while (a != null) {
                a = a.next;
                lena++;
            }
            while (b != null) {
                b = b.next;
                lenb++;
            }
    
            if (lena > lenb) {
                int len = lena - lenb;
                while (len-- > 0) {
                    headA = headA.next;
                }
                while (headA != headB && headA != null) {
                    headA = headA.next;
                    headB = headB.next;
                }
                if (headA == headB) return headA;
            }
            if (lena < lenb) {
                int len = lenb - lena;
                while (len-- > 0) {
                    headB = headB.next;
                }
                while (headA != headB && headA != null) {
                    headA = headA.next;
                    headB = headB.next;
                }
                if (headA == headB) return headA;
            }
            if (lena == lenb) {
                while (headA != headB && headA != null) {
                    headA = headA.next;
                    headB = headB.next;
                }
                if (headA == headB) return headA;
            }
            return null;
        }
    
        /**
         * 大神解法
         */
        public ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
            if (headA == null || headB == null) return null;
            ListNode pA = headA, pB = headB;
            while (pA != pB) {
                pA = pA == null ? headB : pA.next;
                pB = pB == null ? headA : pB.next;
            }
            return pA;
        }
    }
    

    判断链表是否有环

    /**
     * 1. HashSet:
     *     时间复杂度:O(n),对于含有 nn 个元素的链表,我们访问每个元素最多一次。
     *         添加一个结点到哈希表中只需要花费 O(1) 的时间。
     *     空间复杂度:O(n),空间取决于添加到哈希表中的元素数目,最多可以添加 n 个元素。
     * 
     * 2. 双指针:
     *     时间复杂度:O(n)
     *     空间复杂度:O(1)
     */
    class Solution2 {
        public boolean hasCycle(ListNode head) {
            ListNode low = head;
            ListNode fast = head;
            if (head == null) {
                return false;
            }
            while (fast.next != null && low.next != null) {
                low = low.next;
                fast = fast.next;
                if (fast.next != null) {
                    fast = fast.next;
                    if (low == fast) return true;
                }
            }
            return false;
        }
    }
    

    给定一个链表,返回链表开始入环的第一个节点。如果链表无环,则返回 null

    /**
     * 1. HashSet
     * 
     * 2. 快慢指针:指针从 相遇点 出发和从 链表的头 出发,最后会遍历相同数目的节点后在环的入口处相遇。
     */
    class Solution3 {
        public ListNode detectCycle(ListNode head) {
            HashSet<ListNode> set = new HashSet<>();
            while (head != null) {
                if (set.contains(head)) return head;
                set.add(head);
                head = head.next;
            }
            return null;
        }
    }
    

    将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的

    /**
     * 示例:
     *     输入:1->2->4, 1->3->4
     *     输出:1->1->2->3->4->4
     */
    
    /**
     * 方法1:
     *     递归:进去保存链表,回去返回
     * 
     * 时间复杂度:O(n + m)。
     * 空间复杂度:O(n + m)。
     */
    class Solution5 {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            if (l1 == null) return l2;
            if (l2 == null) return l1;
            else if (l1.val < l2.val) {
                l1.next = mergeTwoLists(l1.next, l2);
                return l1;
            } else {
                l2.next = mergeTwoLists(l1, l2.next);
                return l2;
            }
        }
    }
    
    
    /**
     * 方法2:
     *     迭代
     * 
     * 时间复杂度:O(n + m)。
     * 空间复杂度:O(1)。
     */
    class Solution6 {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            ListNode listNode = new ListNode(-1);
            ListNode cur = listNode;
            while (l1 != null && l2 != null) {
                if (l1.val < l2.val) {
                    cur.next = l1;
                    cur = cur.next;
                    l1 = l1.next;
                } else {
                    cur.next = l2;
                    cur = cur.next;
                    l2 = l2.next;
                }
            }
            cur.next = l1 == null ? l2 : l1;
            return listNode.next;
        }
    }
    

    合并 k 个排序链表,返回合并后的排序链表

    /**
     * 1. 暴力法:放数组里排序
     *     时间复杂度:O(NlogN)
     *     空间复杂度:O(N)
     * 
     * 2. 分治
     *     时间复杂度:O(Nlogk) ,其中 k 是链表的数目。
     *     空间复杂度:O(1)。
     * 
     * 3. 优先队列
     *     先添加k个链表的头,然后根据优先队列取出最小的。
     *     然后把最小的节点的下一个添加到优先队列,再取最小的。
     *     如果null则不添加到优先队列中。
     *     直到优先队列为空。
     * 
     *     时间复杂度:O(Nlogk)
     *     空间复杂度:O(n)
     */
    class Solution7 {
        public ListNode mergeKLists(ListNode[] lists) {
            if (lists.length == 0) return null;
            return recursion(lists, 0, lists.length - 1);
        }
    
        private ListNode recursion(ListNode[] lists, int left, int right) {
            if (left == right) return lists[left];
            int mid = (left + right) >> 1;
            ListNode l1 = recursion(lists, left, mid);
            ListNode l2 = recursion(lists, mid + 1, right);
            return merger(l1, l2);
        }
    
        private ListNode merger(ListNode l1, ListNode l2) {
            if (l1 == null) return l2;
            if (l2 == null) return l1;
            else if (l1.val < l2.val) {
                l1.next = merger(l1.next, l2);
                return l1;
            } else {
                l2.next = merger(l1, l2.next);
                return l2;
            }
        }
    }
    

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表

    /**
     * 示例:
     *     给定 1->2->3->4, 你应该返回 2->1->4->3.
     */
    
    /**
     * 递归
     */
    class Solution8 {
        public ListNode swapPairs(ListNode head) {
            if (head == null || head.next == null) {
                return head;
            }
            ListNode next = head.next;
            head.next = swapPairs(next.next);
            next.next = head;
            return next;
        }
    }
    
    /**
     * 非递归
     */
    class Solution9 {
        public ListNode swapPairs(ListNode head) {
            ListNode fakeHead = new ListNode(0);
            fakeHead.next = head;
            ListNode pre = fakeHead;    //前一个遍历的节点
            ListNode ptr = pre.next;    //当前节点
    
            while (ptr != null && ptr.next != null) {
                ListNode temp = ptr.next;   //要与ptr交换位置的节点
                ListNode next = temp.next;  //下一个要遍历的节点,先存储起来
                pre.next = temp;    //因为pre是上一个的节点,所以需要连接下一个被交换后的节点
                temp.next = ptr;    //交换节点
                
                pre = ptr;    //前一个遍历的节点
                ptr = next;    //当前节点
            }
            pre.next = ptr; //!!很重要,最后的连接整理
    
            return fakeHead.next;
        }
    }
    

    链表2

    class Node {
        public int val;
        public Node next;
        public Node random;
    
        public Node() {
        }
    
        public Node(int _val, Node _next, Node _random) {
            val = _val;
            next = _next;
            random = _random;
        }
    }
    

    返回链表的深拷贝

    /**
     * 给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
     * 
     * 要求返回这个链表的深拷贝。
     * 
     * 思路:递归,入栈时复制node,出栈时将node相连
     * 
     * 时间复杂度:O(N),其中 N 是链表中节点的数目。
     * 空间复杂度:O(N) 。我们需要维护一个回溯的栈,同时也需要记录已经被深拷贝过的节点,也就是维护一个已访问字典。
     */
    class Solution4 {
        Map<Node, Node> map = new HashMap<>();
    
        public Node copyRandomList(Node head) {
            if (head == null) {
                return null;
            }
            //当前节点已经存在,则无需创建,直接返回节点
            if (map.containsKey(head)) {
                return map.get(head);
            }
            //创建节点,并保存再map中
            Node node = new Node(head.val, null, null);
            map.put(head, node);
            //回溯,将节点相连
            node.next = copyRandomList(head.next);
            node.random = copyRandomList(head.random);
            return node;
        }
    }
    
  • 相关阅读:
    二、静、动态代理(9~10)~~~~
    luogu P3572 [POI2014]PTA-Little Bird 单调队列优化dp
    luogu P6113 【模板】一般图最大匹配 带花树
    Codeforces Round #646 (Div. 2) C——Game On Leaves 思维
    Codeforces Round #646 (Div. 2) E——Tree Shuffling 思维
    luogu P2979 [USACO10JAN]Cheese Towers S 变形dp背包
    luogu P6577 【模板】二分图最大权完美匹配
    Educational Codeforces Round 88 (Rated for Div. 2) D
    Codeforces Round #645 (Div. 2) E
    Codeforces Round #645 (Div. 2) D
  • 原文地址:https://www.cnblogs.com/loveer/p/11755462.html
Copyright © 2020-2023  润新知