第六节 链表

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。

  

posted @ 2016-10-11 15:20  coldyan  阅读(237)  评论(0编辑  收藏  举报