LeetCode 430 - Flatten a multilevel linked list


Related: LeetCode 114. Flatten Binary Tree to Linked List
https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/
You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below.
Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list.

Example:
Input:
 1---2---3---4---5---6--NULL
         |
         7---8---9---10--NULL
             |
             11--12--NULL

Output:
1-2-3-7-8-11-12-9-10-4-5-6-NULL

Explanation for the above example:
Given the following multilevel doubly linked list:

We should return the following flattened doubly linked list:
X. Stack
https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/discuss/252772/Easily-understable-Java-Solution-Runtime-1ms-beat-100-Recursive

    public Node flatten(Node h) {
        if (h == null) return h;
        
        Stack<Node> st = new Stack<>();
        Node prev = null;
        st.push(h);
        while (!st.isEmpty()){
            Node cur = st.pop();
            if (prev != null) {
                prev.next = cur;
                cur.prev = prev;
                prev.child = null;
            }
            if (cur.next != null) st.push(cur.next);
            if (cur.child != null) st.push(cur.child);
            prev = cur;
        }
        
        return h;
    }
X. Iterative - O(N^2)
http://www.cnblogs.com/grandyang/p/9688522.html
我们其实也可以不用递归,链表的题不像树的题,对于树的题使用递归可以很简洁,而链表递归和迭代可能差的并不多。如果你仔细对比两种方法的代码,你会发现迭代的写法刚好比递归的写法少了调用递归的那一行,给人一种完全没有必要使用递归的感觉,其实两种解法的操作顺序不同的,递归写法是从最底层开始操作,先把最底层加入倒数第二层,再把混合后的层加入倒数第三层,依此类推,直到都融合到第一层为止。而迭代的写法却是反过来的,先把第二层加入第一层,此时第二层底下可能还有很多层,不必理会,之后等遍历到的时候,再一层一层的加入第一层中,不管哪种方法,最终都可以压平
https://www.cnblogs.com/Dylan-Java-NYC/p/9596026.html
如果当前点cur 没有child, 直接跳到cur.next 进行下次计算.
如果cur 有child, 目标是把cur.child这个level提到cur这个level上. 至于cur.child 这个level上有没有点有child 先不管. 
做法就是cur.child 一直只按next找到tail, 然后这一节插在cur 和 cur.next之间, cur再跳到更新的cur.next上.
Time Complexity: O(n). n是所有点的个数, 每个点只走过constant次数.
20     public Node flatten(Node head) {
21         if(head == null){
22             return head;
23         }
24         
25         Node cur = head;
26         while(cur != null){
27             if(cur.child == null){
28                 cur = cur.next;
29                 continue;
30             }
31             
32             Node child = cur.child;
33             Node childTail = child;
34             while(childTail.next != null){
35                 childTail = childTail.next;
36             }
37             
38             cur.child = null;
39             child.prev = cur;
40             childTail.next = cur.next;
41             if(cur.next != null){
42                 cur.next.prev = childTail;
43             }
44             cur.next = child;
45             cur = cur.next;
46         }
47         
48         return head;
49     }
https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/discuss/150321/Easy-Understanding-Java-beat-95.7-with-Explanation
  1. Start form the head , move one step each time to the next node
  2. When meet with a node with child, say node p, follow its child chain to the end and connect the tail node with p.next, by doing this we merged the child chainback to the main thread
  3. Return to p and proceed until find next node with child.
  4. Repeat until reach null
It seems still to be an O(n) solution. Since we need to visit each node at most twice, one for find the tail of the current branch, and the other for flatten the current node. Correct me if I am wrong.
If so, this is a really nice solution with O(n) time and O(1) space!
This is more like a top down flattening, when encounter a node with child node, we directly flatten the current node, then move to the next node.
the recursive method is more like bottom up flattening, means when we encounter a node with child node, we flatten the child node first, then flatten the current node.
    public Node flatten(Node head) {
        if( head == null) return head;
 // Pointer
        Node p = head; 
        while( p!= null) {
            /* CASE 1: if no child, proceed */
            if( p.child == null ) {
                p = p.next;
                continue;
            }
            /* CASE 2: got child, find the tail of the child and link it to p.next */
            Node temp = p.child;
            // Find the tail of the child
            while( temp.next != null ) 
                temp = temp.next;
            // Connect tail with p.next, if it is not null
            temp.next = p.next;  
            if( p.next != null )  p.next.prev = temp;
            // Connect p with p.child, and remove p.child
            p.next = p.child; 
            p.child.prev = p;
            p.child = null;
        }
        return head;
    }
Flatten a multilevel linked list | GeeksforGeeks
https://hellosmallworld123.wordpress.com/2014/05/29/flatten-a-multilevel-linkedlist/
We approach this problem level by level, first scan all the nodes in the first level, append any list we found in the second level to the end of first level. after we scan to the end of the first level, we will have all the second level appended to the tail. Then we keep going and when we reached the end of the second level, the third level will be appended to the end of the list, and so on. When we finished all the nodes, then we are done.
class ListNode {
    int label;
    ListNode child;
    ListNode next;
    public ListNode(int l) {
        label = l;
    }

}
    public ListNode flattenList(ListNode head) {
        if (head == null)
            return null;
        ListNode curr = head;
        ListNode tail = head;
        // find the tail of the list
        while(tail.next != null) {
            tail = tail.next;
        }
        while(curr != null) {
            if (curr.child != null) {
                tail.next = curr.child;
                // move tail to the end tail
                while(tail.next != null)
                    tail = tail.next;
                curr.child = null;
            }
            curr = curr.next;
        }
        return head;
    }
1) Take "cur" pointer, which will point to head of the fist level of the list
2) Take "tail" pointer, which will point to end of the first level of the list
3) Repeat the below procedure while "curr" is not NULL.
    I) if current node has a child then
 a) append this new child list to the "tail"
  tail->next = cur->child
 b) find the last node of new child list and update "tail"
  tmp = cur->child;
  while (tmp->next != NULL)
   tmp = tmp->next;
  tail = tmp;
    II) move to the next node. i.e. cur = cur->next 
void flattenList(struct node *head)
{
    /*Base case*/
    if (head == NULL)
       return;
    struct node *tmp;
    /* Find tail node of first level linked list */
    struct node *tail = head;
    while (tail->next != NULL)
        tail = tail->next;
    // One by one traverse through all nodes of first level
    // linked list till we reach the tail node
    struct node *cur = head;
    while (cur != tail)
    {
        // If current node has a child
        if (cur->child)
        {
            // then append the child at the end of current list
            tail->next = cur->child;
            // and update the tail to new last node
            tmp = cur->child;
            while (tmp->next)
                tmp = tmp->next;
            tail = tmp;
        }
        // Change current node
        cur = cur->next;
    }
}
X. Recursive
https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/discuss/252772/Easily-understable-Java-Solution-Runtime-1ms-beat-100-Recursive
Traverse through the linked list and whenever you find a node with a child, call flatten function recursively on that child node.
Also, don't forget to maintain a class variable which will keep last visited node which will act as a tail pointer.
Time Complexity: O(2n) since in worst case we will visit each node twice. so O(n)

这道题给了我们一个多层的双向链表,让我们压平成为一层的双向链表,题目中给了形象的图例,不难理解题意。根据题目中给的例子,我们可以看出如果某个结点有下一层双向链表,那么下一层双向链表中的结点就要先加入进去,如果下一层链表中某个结点还有下一层,那么还是优先加入下一层的结点,整个加入的机制是DFS的,就是有岔路先走岔路,走到没路了后再返回,这就是深度优先遍历的机制。好,那么既然是DFS,肯定优先考虑递归啦。方法有了,再来看具体怎么递归。由于给定的多层链表本身就是双向的,所以我们只需要把下一层的结点移到第一层即可,那么没有子结点的结点就保持原状,不作处理。只有对于那些有子结点的,我们需要做一些处理,由于子结点链接的双向链表要加到后面,所以当前结点之后要断开,再断开之前,我们用变量next指向下一个链表,然后我们对子结点调用递归函数,我们suppose返回的结点已经压平了,那么就只有一层,那么就相当于要把这一层的结点加到断开的地方,所以我们需要知道这层的最后一个结点的位置,我们用一个变量last,来遍历到压平的这一层的末结点。现在我们就可以开始链接了,首先把子结点链到cur的next,然后把反向指针prev也链上。此时cur的子结点child可以清空,然后压平的这一层的末节点last链上之前保存的next结点,如果next非空,那么链上反向结点prev。这些操作完成后,我们就已经将压平的这一层完整的加入了之前层断开的地方,我们继续在之前层往下遍历即可
https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/discuss/157606/Java-Recursive-Solution
https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/discuss/159746/Java-1ms-Recursion-beats-100-with-explaination
Go through the linked list;
If
  1. Node cur.child == null, skip, don't go into recursion.
  2. Node cur.child != null, recurse and get child's tail node. Node tmp = cur.next, (1)connect cur <-> child, (2)connect child's tail <->tmp.
The helper function returns the tail node of current level.
public Node flatten(Node head) {
    helper(head);
    return head;
}

private Node helper(Node head) {
    Node cur = head, pre = head;
    while(cur != null) {
        if(cur.child == null) {
            pre = cur;
            cur = cur.next;
        } else {
            Node tmp = cur.next;
            Node child = helper(cur.child);
            cur.next = cur.child;
            cur.child.prev = cur;
            cur.child = null;
            child.next = tmp;
            if(tmp != null) tmp.prev = child;
            pre = child;
            cur = tmp;
        }
    }
    return pre;
}
https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/discuss/184156/Easy-Understand-Java-Recursive-solution-beat-100-with-Explanation
The idea is simple. We always keep a pre global variable to keep track of the last node we visited and connect current node head with this pre node. So for each recursive call, we do
  • Connect last visited node with current node by letting pre.next point to current node head and current node's prev point to pre
  • Mark current node as pre. pre = head
  • If there is head.child, we recursively visit and flatten its child node
  • Recursively visit and flatten its next node
/*Global variable pre to track the last node we visited */
    Node pre = null;
    public Node flatten(Node head) {
        if (head == null) {
            return null; 
        }
/*Connect last visted node with current node */
        if (pre != null) {
            pre.next = head; 
            head.prev = pre;
        }

        pre = head; 
/*Store head.next in a next pointer in case recursive call to flatten head.child overrides head.next*/
        Node next = head.next; 

        flatten(head.child);
        head.child = null;

        flatten(next);        
        return head; 
    }

Linkedin:
massivealgorithms.blogspot.com/2015/11/linkedin-interview-misc.html
http://www.1point3acres.com/bbs/thread-147555-1-1.html
2. 双向链表,但是每一个点还可以有up,down pointer, 已知一个链表里没有环,要求把这个链表变成标准双向链表,每个点的具体位置排列无所谓。楼主开始反应是递归,写好后面试官说优化一下,,空间要求是constant space,然后尽管面试官一直在提示tail recursion,还是没想出来(据说地里有原题,可惜当时楼主没看到。。。跪了= =!)

Related:
Flattening a Linked List | GeeksforGeeks
Given a linked list where every node represents a linked list and contains two pointers of its type:
(i) Pointer to next node in the main list (we call it ‘right’ pointer in below code)
(ii) Pointer to a linked list where this node is head (we call it ‘down’ pointer in below code).
All linked lists are sorted. See the following example
       5 -> 10 -> 19 -> 28
       |    |     |     |
       V    V     V     V
       7    20    22    35
       |          |     |
       V          V     V
       8          50    40
       |                |
       V                V
       30               45
Write a function flatten() to flatten the lists into a single linked list. The flattened linked list should also be sorted. For example, for the above input list, output list should be 5->7->8->10->19->20->22->28->30->35->40->45->50.
The idea is to use Merge() process of merge sort for linked lists. We use merge() to merge lists one by one. We recursively merge() the current list with already flattened list.
The down pointer is used to link nodes of the flattened list.
Read full article from Flatten a multilevel linked list | GeeksforGeeks

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts