Java 使用多达N个额外内存和N个log N时间洗牌单个链接列表

Java 使用多达N个额外内存和N个log N时间洗牌单个链接列表,java,linked-list,shuffle,Java,Linked List,Shuffle,我试图通过递归方式洗牌一个链表,将它分成两个列表,然后合并它们以确保随机洗牌 我面临的两个问题是: 当我运行程序时,在第56行有一个无限循环,我告诉程序洗牌第一个列表 我不知道如何在列表1和列表2的长度发生变化的情况下添加虚拟变量,以确保随机洗牌 PS-我通过互联网搜索发现,两个列表的长度应该相同,以确保随机性,但我不知道背后的逻辑。另外,请让我知道是否有比我尝试的更好的方法 提前谢谢 public class LinkedListShuffle { static public clas

我试图通过递归方式洗牌一个链表,将它分成两个列表,然后合并它们以确保随机洗牌

我面临的两个问题是:

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

    提前谢谢

    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()+"  ");
        }
    }
    
    公共类LinkedListShuffle
    {
    静态公共类LinkedList//静态嵌套类
    {
    私有整数N=0;
    private Node first=null;
    公共类节点
    {
    E元素;
    节点下一步;
    }
    公共布尔值为空()
    {返回N==0;}
    公共无效推送(E元素)
    {
    节点oldfirst=第一个;
    第一个=新节点();
    first.elem=elem;
    first.next=oldfirst;
    N++;
    }
    公共E-pop()
    {
    E elem=first.elem;
    first=first.next;
    N--;
    返回元素;
    }
    公共整数大小()
    {返回N;}
    }
    公共静态无效洗牌(LinkedList l)
    {
    如果(l.size()==1)返回;
    LinkedList.Node current=l.first;
    LinkedList l1=新LinkedList();
    LinkedList l2=新建LinkedList();
    而(!l.isEmpty())
    {
    l1.推(l.pop());
    如果(!l.isEmpty())l2.push(l.pop());
    }
    洗牌(l1);
    洗牌(l2);
    /*------------------------------------------    
    *if(l2.size()
    您的问题是,在调用
    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()+"  ");
    }
    

    我认为可以在拆分阶段引入随机性,在这种情况下,合并只需要连接两个随机洗牌的列表。下面是一个使用Robert Sedgewick的algo课程中的
    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;
    }
    
    public静态LinkedBag shuffle(LinkedBag ll){
    
    如果(ll.size()谢谢你的帮助。但这进一步让我怀疑,要获得链表的副本,我是否需要单独实例化它?当使用内置链表时,有一种方法可以在Java中克隆列表。因此,如何执行该操作。此外,我仍然期待着对随机性需要虚拟变量的解释。即使有人能告诉我其他相关的链接来解释,我也会非常感激。另外,如果我使用泛型,我如何添加并最终删除伪变量?@Neha关于创建列表副本,您可以实现克隆()方法,该方法将创建一个新实例并将原始列表的所有元素复制到该实例中内置LinkedList中的一个有。@Neha至于虚拟变量,我发现很难相信两个列表大小之间的差异为1会对随机洗牌的随机性产生很大影响。也许您发现的来源意味着两个列表的大小应该相似。只向列表中添加虚拟元素以后删除它对我来说似乎毫无意义。这是我读到的关于伪变量的来源-您的实现如何使用log n space?每次调用split都会创建两个新列表,其中包含对堆上对象的引用(由客户端创建)。所有列表都应该消耗(n*8+开销)字节总数,其中8字节是HotSpot JVM中引用的大小。显然,这是线性空间,而不是log n。我的答案使用了相同的空间量,但不需要您显然不理解的虚拟节点。不是log n空间,而是额外的log n空间!
    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;
    }