使用最多N个额外内存和N log N时间来混合单个链表

时间:2014-11-02 20:45:31

标签: java linked-list shuffle

我试图通过递归方式对链表进行洗牌,将其分成两个列表然后合并它们以确保随机随机播放。

我面临的两个问题是:

  1. 当我运行程序时,第56行有一个无限循环,我告诉程序将第一个列表洗牌。
  2. 如果列表1和列表2的长度不同以确保随机播放,我无法弄清楚如何添加虚拟变量。
  3. PS - 我通过互联网搜索发现,2个列表的长度应该相同,以确保随机性,但我不知道它背后的逻辑。另外,请告诉我是否有更好的方法比我正在尝试。

    提前致谢!

    public class LinkedListShuffle
    {
        static public class LinkedList<E>       // static nested class
        {
            private int N = 0;
            private Node first = null;
    
            public class Node
            {
                E elem;
                Node next;
            }
    
            public boolean isEmpty()
            {    return N == 0;    }
    
            public void push (E elem)
            {
                Node oldfirst = first;
                first = new Node(); 
                first.elem = elem;
                first.next = oldfirst;
                N++; 
            }
    
            public E pop()
            {
                E elem = first.elem;
                first = first.next;
                N--;
                return elem;
            }
    
            public int size ()
            {    return N;    }
    
        }
    
        public static void shuffle(LinkedList l)
        {
            if (l.size() == 1) return;
    
            LinkedList.Node current = l.first;
    
    
            LinkedList l1 = new LinkedList();
            LinkedList l2 = new LinkedList();
    
            while (! l.isEmpty())
            {
                l1.push(l.pop());
                if (! l.isEmpty()) l2.push(l.pop());
            }
            shuffle(l1);
            shuffle(l2);
    
            /*------------------------------------------    
            * if (l2.size() < l1.size())
            * introduce a dummy node to ensure the
            * randomness in the process of shuffling
            -----------------------------------------*/
    
            merge(l, l1, l2);
    
            /*-----------------------------------------------
            * remove the dummy variable
            * ----------------------------------------------*/       
        }
    
        public static void merge (LinkedList l, LinkedList l1, LinkedList l2)
        {
            while (l1.size() != 0 && l2.size() != 0)
            {
                double chance = StdRandom.uniform(1);
                if (chance < 0.5) l.push(l1.pop());
                else              l.push(l2.pop());
            }
    
            if (! l1.isEmpty())
                while (! l1.isEmpty()) l.push(l1.pop());
            if (! l2.isEmpty())
                while (! l2.isEmpty()) l.push(l2.pop());
        }
    
        public static void main (String[] args)
        {
            LinkedList<String> l = new LinkedList<String>();
            LinkedList<String> copy = new LinkedList<String>();
            l.push("A"); l.push("B"); l.push("C"); l.push("D");
            l.push("E"); l.push("F"); l.push("G"); l.push("H");
            copy = l;
    
            while (copy.size() != 0) StdOut.println(copy.pop()+"  ");
            shuffle(l);
            while (l.size() != 0) StdOut.println(l.pop()+"  ");
        }
    }
    

2 个答案:

答案 0 :(得分:1)

您的问题是,在调用shuffle(l)之前,在主方法中打印时,您正在清空列表。

您正在将列表l分配给名为copy的变量,但此变量不包含列表的副本。它指的是同一个列表。当您致电copy.pop()时,您会从原始列表中删除一个元素。因此,您在空列表中调用shuffle

public static void main (String[] args)
{
    LinkedList<String> l = new LinkedList<String>();
    LinkedList<String> copy = new LinkedList<String>();
    l.push("A"); l.push("B"); l.push("C"); l.push("D");
    l.push("E"); l.push("F"); l.push("G"); l.push("H");
    copy = l;

    while (copy.size() != 0) StdOut.println(copy.pop()+"  "); // remove this line
                                                              // and your method will
                                                              // work
    shuffle(l);
    while (l.size() != 0) StdOut.println(l.pop()+"  ");
}

当然,这意味着您的shuffle方法无法将空列表作为输入处理。

这可以通过微小的解决方案来解决:

public static void shuffle(LinkedList l)
{
    if (l.size() <= 1) return; // instead of == 1
    ...

答案 1 :(得分:0)

我认为随机性可以在分裂阶段引入,在这种情况下,合并只需要连接2个随机混乱的列表。以下是使用Robert Sedgewick的算法课程中的LinkedBag链接列表实现的实现,这显然是您从中完成作业的地方。

public static <T> LinkedBag<T> shuffle(LinkedBag<T> ll) {
    if (ll.size() <= 1) return ll;

    LinkedBag<T>[] sublists = split(ll);

    LinkedBag<T> l1 = shuffle(sublists[0]);
    LinkedBag<T> l2 = shuffle(sublists[1]);

    return merge(l1, l2);
}

private static <T> LinkedBag<T>[] split(LinkedBag<T> ll) {
    LinkedBag<T> l1 = new LinkedBag<>();
    LinkedBag<T> l2 = new LinkedBag<>();

    Iterator<T> it = ll.iterator();
    while (it.hasNext()) {
        if (StdRandom.bernoulli())
            l1.add(it.next());
        else
            l2.add(it.next());
    }

    return (LinkedBag<T>[]) new LinkedBag<?>[]{l1, l2};
}

private static <T> LinkedBag<T> merge(LinkedBag<T> l1, LinkedBag<T> l2) {
    Iterator<T> it = l2.iterator();

    while (it.hasNext()) {
        l1.add(it.next());
    }
    return l1;
}