Algorithm for Shuffling a Linked List in n log n time

后端 未结 7 512
北荒
北荒 2021-01-30 05:37

I\'m trying to shuffle a linked list using a divide-and-conquer algorithm that randomly shuffles a linked list in linearithmic (n log n) time and logarithmic (log n) extra space

7条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2021-01-30 06:10

    Here is one possible solution:

    #include 
    
    typedef struct node_s {
       struct node_s * next;
       int data;
    } node_s, *node_p;
    
    void shuffle_helper( node_p first, node_p last ) {
       static const int half = RAND_MAX / 2;
       while( (first != last) && (first->next != last) ) {
          node_p firsts[2] = {0, 0};
          node_p *lasts[2] = {0, 0};
          int counts[2] = {0, 0}, lesser;
          while( first != last ) {
             int choice = (rand() <= half);
             node_p next = first->next;
             first->next = firsts[choice];
             if( !lasts[choice] ) lasts[choice] = &(first->next);
             ++counts[choice];
             first = next;
          }
    
          lesser = (counts[0] < counts[1]);
    
          if( !counts[lesser] ) {
             first = firsts[!lesser];
             *(lasts[!lesser]) = last;
             continue;
          }
    
          *(lasts[0]) = firsts[1];
          *(lasts[1]) = last;
    
          shuffle_helper( firsts[lesser], firsts[!lesser] );
    
          first = firsts[!lesser];
          last = *(lasts[!lesser]);
       }
    }
    
    void shuffle_list( node_p thelist ) { shuffle_helper( thelist, NULL ); }
    

    This is basically quicksort, but with no pivot, and with random partitioning.

    The outer while loop replaces a recursive call.

    The inner while loop randomly moves each element into one of two sublists.

    After the inner while loop, we connect the sublists to one another.

    Then, we recurse on the smaller sublist, and loop on the larger.

    Since the smaller sublist can never be more than half the size of the initial list, the worst case depth of recursion is the log base two of the number of elements. The amount of memory needed is O(1) times the depth of recursion.

    The average runtime, and number of calls to rand() is O(N log N).

    More precise runtime analysis requires an understanding of the phrase "almost surely."

提交回复
热议问题