洗牌,现理解就是讲一个数组打算顺序。
我想了两种方法。
第二种的思路是:如果数组长度为五,则在第一个到第四个随机一个数,与之交换。这样第五的元素就是装了一个随机数,然后再在第一个到第三个随机一个数,与数组第四个元素交换,以此类推。
第一种思路是,套用collections的shuffle方法,代码比较简洁,其实是一样的,但是不比较大小,直接交换。想一下,不比较大小是对的。
/** * 打乱数组,洗牌方法① */ public void shuffle(int[] array) { Random rnd = new Random(); int size = array.length; // Shuffle array for (int i=size; i>1; i--){ int index = rnd.nextInt(i); int temp = array[index]; array[index] = array[i-1]; array[i-1] = temp; } } /** * 打乱数组,冒泡洗牌方法② */ public void bubbleShuffle(int[] arr) { int len = arr.length; for (int i = 0; i < len - 3; i++) { int index = (int)(Math.random()*(len - 2 - i)); if(arr[index]!=arr[len - 1 - i]) swap(arr,len - 1 - i,index); } swap(arr, 0, 1); } public void swap(int[] arr,int a, int b){ int t = arr[a]; arr[a] = arr[b]; arr[b] = t; System.out.println("交换["+a+","+b+"]"); }
品味一下Collections.shuffle()
public static void shuffle(List<?> list) { if (r == null) { r = new Random(); } shuffle(list, r); } private static Random r;
public static void shuffle(List<?> list, Random rnd) { int size = list.size(); if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) { for (int i=size; i>1; i--) swap(list, i-1, rnd.nextInt(i)); } else { Object arr[] = list.toArray(); // Shuffle array for (int i=size; i>1; i--) swap(arr, i-1, rnd.nextInt(i)); // Dump array back into list ListIterator it = list.listIterator(); for (int i=0; i<arr.length; i++) { it.next(); it.set(arr[i]); } } }
/** * Swaps the two specified elements in the specified array. */ private static void swap(Object[] arr, int i, int j) { Object tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
回答一楼VSTUM的问题:
比如说,为什么collections的shuffle方法加入了对RandomAccess的判断?
对List特别的遍历算法中,要尽量判断是属于随机访问RandomAccess(如ArrayList)还是顺序访问SequenceAccess(如LinkedList),
对于实现了RandomAccess接口的类实例而言
for (int i=0, i<list.size(); i++)
list.get(i);
的运行速度要快于下面:
for (Iterator i=list.iterator(); i.hasNext(); )
i.next();