zoukankan      html  css  js  c++  java
  • 第六节 链表

    1.什么时候使用dummy node:

    当所返回的链表的头不确定的时候使用。

    2.链表基本功(链表的题型通常都是分步骤地对链表进行基本操作):

      a.链表中插入一个节点

      b.链表中删除一个节点

          c.链表reverse,代码如下:

    public ListNode reverse(ListNode head) {
            if (head == null) {
                return null;
            }
            ListNode pre = head;
            ListNode cur = head.next;
            
            while (cur != null) {
                ListNode temp = cur;
                cur = cur.next;
                temp.next = pre;
                pre = temp;
            }
            
            head.next = null;
            return pre;
            
        }

      

      d.merge两个排序链表

     public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            ListNode dummy = new ListNode(0);
            ListNode cur = dummy;
            
            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;
                }
            }
            
            if (l1 != null) {
                cur.next = l1;
            }
            
            if (l2 != null) {
                cur.next = l2;
            }
            
            return dummy.next;
        }

      

      e.找链表的中点(快慢指针的方法)

    public ListNode findMid(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;
            }
            return slow;
        }

    3.题目剖析

    (1)merge k sorted lists

      方法一:分治

      时间复杂度分析:

        T(N,k) = T(N1, k/2) + T(N2, k/2) + N1 + N2

             = T(N1, k/2) + T(N2, k/2) + N

             = T(N11, k/4)  + T(N12, k/4) + N11 + N12 + T(N21, k/4) + T(N22, k/4) + N21 + N22 + N

             = T(N11, k/4)  + T(N12, k/4) + N1 + T(N21, k/4) + T(N22, k/4) + N2 + N

             = T(N11, k/4)  + T(N12, k/4) + T(N21, k/4) + T(N22, k/4) + 2N

             ......

             = T(n1, 1) + T(n2, 1) +...+ T(nk, 1) + Nlgk = O(Nlgk)

          代码:

    public class Solution {
        /**
         * @param lists: a list of ListNode
         * @return: The head of one sorted list.
         */
        public ListNode mergeKLists(List<ListNode> lists) {
            if (lists.size() == 0) {
                return null;
            }
            return mergeHelper(lists, 0, lists.size() - 1);
        }
        
        private ListNode mergeHelper(List<ListNode> lists, int start, int end) {
            if (start == end) {
                return lists.get(start);
            }
            
            int mid = start + (end - start) / 2;
            ListNode left = mergeHelper(lists, start, mid);
            ListNode right = mergeHelper(lists, mid + 1, end);
            return mergeTwoLists(left, right);
        }
        
        private ListNode mergeTwoLists(ListNode list1, ListNode list2) {
            ListNode dummy = new ListNode(0);
            ListNode tail = dummy;
            while (list1 != null && list2 != null) {
                if (list1.val < list2.val) {
                    tail.next = list1;
                    tail = list1;
                    list1 = list1.next;
                } else {
                    tail.next = list2;
                    tail = list2;
                    list2 = list2.next;
                }
            }
            if (list1 != null) {
                tail.next = list1;
            } else {
                tail.next = list2;
            }
            
            return dummy.next;
        }
    }

      方法二:最小堆

      时间复杂度分析:

        T(N, k) = N * lgk = O(Nlgk)

      代码:

    public class Solution {
        /**
         * @param lists: a list of ListNode
         * @return: The head of one sorted list.
         */
        public ListNode mergeKLists(List<ListNode> lists) {  
            // write your code here
            if (lists == null || lists.size()==0) {
                return null;
            }
            Comparator<ListNode> listNodeComparator = new Comparator<ListNode>() {
                public int compare(ListNode l1, ListNode l2) {
                    if (l1 == null && l2 == null) {
                        return 1;
                    } else if (l1 == null) {
                        return 1;
                    } else if (l2 == null) {
                        return -1;
                    } else {
                        return l1.val - l2.val;
                    }
                }
            };
            
            ListNode dummy = new ListNode(0);
            ListNode cur = dummy;
            
            PriorityQueue<ListNode> minHeap = new PriorityQueue<ListNode>(lists.size(), listNodeComparator);
            for (ListNode l : lists) {
                if (l != null) {
                    minHeap.offer(l);
                }
            }
            
            while (minHeap.peek() != null) {
                cur.next = minHeap.poll();
                cur = cur.next;
                if (cur.next != null) {
                    minHeap.offer(cur.next);
                }
            }
            cur.next = null;
            return dummy.next;  
        } 
    }

          方法三:两两合并

      时间复杂度分析:

      T(N, k) = N + T(N, k/2)

          = 2N + T(N, k/4)

          = 3N + T(N, k/8)

           ......

          = Nlgk + T(N, 1) = O(Nlgk)

         代码:

    public class Solution {
        /**
         * @param lists: a list of ListNode
         * @return: The head of one sorted list.
         */
        public ListNode mergeKLists(List<ListNode> lists) {  
            // write your code here
            if (lists.size() == 0) {
                return null;
            }
            if (lists.size() == 1) {
                return lists.get(0);
            }
            int size = lists.size();
            List<ListNode> nextLists = new ArrayList<ListNode>();
            for (int i = 0; i<=size-1; i+=2) {
                if (i == size-1) {
                    nextLists.add(lists.get(i));
                    break;
                }
                ListNode afterMerge = mergeTwoLists(lists.get(i), lists.get(i+1));
                nextLists.add(afterMerge);
            }
            return mergeKLists(nextLists);
        }
        
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            ListNode dummy = new ListNode(0);
            ListNode cur = dummy;
            
            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;
                }
            }
            
            if (l1 != null) {
                cur.next = l1;
            }
            
            if (l2 != null) {
                cur.next = l2;
            }
            
            return dummy.next;
        }
    }

    (2)Copy List with Random Pointers

      方法一:使用哈希表,需要消耗O(N)的额外空间

      代码:

    public class Solution {
        /**
         * @param head: The head of linked list with a random pointer.
         * @return: A new head of a deep copy of the list.
         */
        public RandomListNode copyRandomList(RandomListNode head) {
            // write your code here
            
            Map<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
            RandomListNode cur = head;
            RandomListNode copyDummy = new RandomListNode(0);
            RandomListNode copyCur = copyDummy;
            
            while (cur != null) {
                copyCur.next = new RandomListNode(cur.label);
                copyCur = copyCur.next;
                map.put(cur, copyCur);
                cur = cur.next;
            }
            
            cur = head;
            while (cur != null) {
                copyCur = map.get(cur);
                copyCur.random = map.get(cur.random);
                cur = cur.next;
            }
            
            return copyDummy.next;
            
        }
    }

      方法二:第一遍扫的时候巧妙运用next指针, 开始数组是1->2->3->4 。 然后扫描过程中 先建立copy节点 1->1`->2->2`->3->3`->4->4`, 然后第二遍copy的时候去建立边的copy, 拆分节点, 一边扫描一边拆成两个链表,这里用到两个dummy node。第一个链表变回 1->2->3 , 然后第二变成 1`->2`->3` 。消耗额外空间O(1)。

      代码:

    public class Solution {
        /**
         * @param head: The head of linked list with a random pointer.
         * @return: A new head of a deep copy of the list.
         */
        public RandomListNode copyRandomList(RandomListNode head) {
            // write your code here
            copyNext(head);
            copyRandom(head);
            return split(head);
            
        }
        
        public RandomListNode split(RandomListNode head) {
            RandomListNode dummy = new RandomListNode(0);
            RandomListNode cur = dummy;
            
            while (head != null) {
                cur.next = head.next;
                cur = cur.next;
                head.next = head.next.next;
                head = head.next;
            }
            return dummy.next;
            
        }
        
        public void copyRandom(RandomListNode head) {
            
            while (head != null) {
                RandomListNode copy = head.next;
                if (copy.random != null) {
                    copy.random = copy.random.next;
                }
                head = head.next.next;
            }
            
        }
        
        public void copyNext(RandomListNode head) {
          
            while (head != null) {
                RandomListNode copy = new RandomListNode(head.label);
                copy.next = head.next;
                copy.random = head.random;
                head.next = copy;
                head = head.next.next;
            }
    
        }
    }

    (3) Linked List Cycle I and II

      使用快慢指针,代码略,见lintcode。

      

  • 相关阅读:
    SpringMVC框架搭建
    java事务的概念
    SpringMVC框架
    JAVA多线程面试题
    MD5加密
    java对象和xml的转换
    eclipse环境配置
    关于枚举类的使用
    定时器的使用
    关于AS-OS
  • 原文地址:https://www.cnblogs.com/coldyan/p/5949422.html
Copyright © 2011-2022 走看看