[英]How to shuffle or randomize a linked list
在這種情況下,我嘗試將圖像的LinkedList改組或隨機化,但是我進行設置的方式似乎不確定
改組非常簡單,您有了列表,記下最后一個條目,然后取第一個條目並將其放在列表中的隨機位置,然后是下一個第一個條目,依此類推,直到最上面的條目是您記下的條目最后,您將該條目放置在列表中的隨機位置,然后列表被隨機排列。
這是我的代碼:
class ShuffleClass
{
private LinkedList<Image> library;
private Image lastCard;
private Image topCard;
private Random rng;
private int place;
private LinkedListNode<Image> node;
public LinkedList<Image> shuffle(LinkedList<Image> library)
{
this.library = library;
lastCard = library.Last.Value;
rng = new Random();
while (library.First.Value != lastCard)
{
topCard = library.First.Value;
library.RemoveFirst();
place = rng.Next(1,library.Count+1);
if (place == library.Count)
{
library.AddBefore(library.Last, topCard);
}
else
{
node = library.Find(library.ElementAt(place));
library.AddBefore(node, topCard);
}
}
topCard = library.First.Value;
library.RemoveFirst();
place = rng.Next(0,library.Count+1);
if(place == library.Count)
{
library.AddBefore(library.Last, topCard);
}
else
{
node = library.Find(library.ElementAt(place));
library.AddBefore(node, topCard);
}
return library;
}
}
您可以使用隨機類來隨機排列列表:
public static void Shuffle()
{
Random Rand = new Random();
LinkedList<int> list = new LinkedList<int>(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 });
foreach (int i in list)
Console.Write("{0} ", i);
Console.WriteLine();
int size = list.Count;
//Shuffle the list
list = new LinkedList<int>(list.OrderBy((o) =>
{
return (Rand.Next() % size);
}));
foreach (int i in list)
Console.Write("{0} ", i);
Console.WriteLine();
}
輸出可能是這樣的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
10 2 17 7 9 15 8 14 1 12 13 16 4 18 3 5 11 20 19 6
您發現的代碼的基本問題是,它不會終止。 您只能調用AddBefore()
,該方法無法在鏈表的最后一個元素之后添加一個元素,因此鏈表的最后一個元素不可能更改,也沒關系將其值移到的開頭。列表。
即使您解決了該問題,循環仍然是不正確的,因為沒有理由期望僅當您完成對列表的混排后,列表中的最后一個值才會移到第一個值。
如果您確實想就地對列表進行隨機排序,則仍然需要從基本正確的隨機排序算法開始,即Fisher-Yates,但是其中swap函數知道如何交換鏈接列表中的元素,而不僅僅是交換數組元素(如Fisher-Yates的通常實現一樣)。
但是請注意,以這種方式進行操作相當昂貴,尤其是當列表變大時,因為您不得不多次掃描列表(每次交換兩次)。
如果您將一系列索引改組到列表中,然后從中構建一個新列表,則可以將成本降低一半。 例如:
class Program
{
static void Main(string[] args)
{
LinkedList<int> test = new LinkedList<int>(Enumerable.Range(0, 10)),
shuffled = Shuffle(test);
Console.WriteLine(string.Join(", ", shuffled));
}
static LinkedList<T> Shuffle<T>(LinkedList<T> source)
{
LinkedList<T> result = new LinkedList<T>();
int[] choices = Enumerable.Range(0, source.Count).ToArray();
ShuffleArray(choices);
foreach (int choice in choices)
{
result.AddLast(ElementAt(source, choice));
}
return result;
}
static void ShuffleArray<T>(T[] array)
{
// Naturally, in real code you'd want to reuse the same Random object
// across multiple calls, by making it static readonly
Random random = new Random();
for (int i = array.Length; i > 1; i--)
{
int j = random.Next(i);
if (i - 1 != j)
{
T t = array[i - 1];
array[i - 1] = array[j];
array[j] = t;
}
}
}
static T ElementAt<T>(LinkedList<T> source, int index)
{
LinkedListNode<T> current = source.First;
while (index-- > 0)
{
current = current.Next;
}
return current.Value;
}
}
如果您確實想要和/或需要就地整理列表,則可以執行以下操作:
class Program
{
static void Main(string[] args)
{
LinkedList<int> test = new LinkedList<int>(Enumerable.Range(0, 10));
ShuffleLinkedList(test);
Console.WriteLine(string.Join(", ", test));
}
static void ShuffleLinkedList<T>(LinkedList<T> list)
{
// Naturally, in real code you'd want to reuse the same Random object
// across multiple calls, by making it static readonly
Random random = new Random();
for (int i = list.Count; i > 1; i--)
{
SwapNodes(list, i - 1, random.Next(i));
}
}
static void SwapNodes<T>(LinkedList<T> list, int i, int j)
{
if (i != j)
{
LinkedListNode<T> node1 = NodeAt(list, i), node2 = NodeAt(list, j),
nodeBefore1 = node1.Previous, nodeBefore2 = node2.Previous;
if (nodeBefore1 == node2)
{
list.Remove(node1);
AddAfter(list, nodeBefore2, node1);
}
else if (nodeBefore2 == node1)
{
list.Remove(node2);
AddAfter(list, nodeBefore1, node2);
}
else
{
list.Remove(node1);
list.Remove(node2);
AddAfter(list, nodeBefore2, node1);
AddAfter(list, nodeBefore1, node2);
}
}
}
static void AddAfter<T>(LinkedList<T> list, LinkedListNode<T> after, LinkedListNode<T> add)
{
if (after != null)
{
list.AddAfter(after, add);
}
else
{
list.AddFirst(add);
}
}
static LinkedListNode<T> NodeAt<T>(LinkedList<T> source, int index)
{
LinkedListNode<T> current = source.First;
while (index-- > 0)
{
current = current.Next;
}
return current;
}
}
請注意,在這兩個示例中,如何將邏輯分解為更小的方法。 當您嘗試將所有內容放到一個方法中時,很難獲得正確的結果,而在這樣做后,甚至更難調試不正確的結果,因為很難檢查和驗證代碼中的每一個較小的邏輯。隔離。
不過,可以利用數據結構的鏈接列表性質來改進就地方法。 即,雖然數組可以通過交換進行重排而受益,所以我們不必在數組中四處移動元素,但是可以通過選擇隨機元素,將其刪除並將其移到末尾來對列表進行重排。 只要確保從列表的越來越短的子集中進行選擇,我們就可以得到均勻分布的隨機播放。
例如:
class Program
{
static void Main(string[] args)
{
LinkedList<int> test = new LinkedList<int>(Enumerable.Range(0, 10));
ShuffleLinkedList(test);
Console.WriteLine(string.Join(", ", test));
}
static void ShuffleLinkedList<T>(LinkedList<T> list)
{
// Naturally, in real code you'd want to reuse the same Random object
// across multiple calls, by making it static readonly
Random random = new Random();
for (int i = list.Count; i > 1; i--)
{
LinkedListNode<T> node = NodeAt(list, random.Next(i));
if (list.Last != node)
{
list.Remove(node);
list.AddLast(node);
}
}
}
static LinkedListNode<T> NodeAt<T>(LinkedList<T> source, int index)
{
LinkedListNode<T> current = source.First;
while (index-- > 0)
{
current = current.Next;
}
return current;
}
}
無需雜亂的節點交換。
最后,最有效的時間方式(用O(n)代替O(n ^ 2)),但要花一些額外的中間內存,是將列表中的所有元素簡單地復制到數組中, ,然后將它們添加回原始列表:
class Program
{
static void Main(string[] args)
{
LinkedList<int> test = new LinkedList<int>(Enumerable.Range(0, 10));
Shuffle(test);
Console.WriteLine(string.Join(", ", test));
}
static void Shuffle<T>(LinkedList<T> source)
{
T[] choices = new T[source.Count];
for (int i = 0; i < choices.Length; i++)
{
choices[i] = source.First.Value;
source.RemoveFirst();
}
ShuffleArray(choices);
foreach (T choice in choices)
{
source.AddLast(choice);
}
}
static void ShuffleArray<T>(T[] array)
{
// Naturally, in real code you'd want to reuse the same Random object
// across multiple calls, by making it static readonly
Random random = new Random();
for (int i = array.Length; i > 1; i--)
{
int j = random.Next(i);
if (i - 1 != j)
{
T t = array[i - 1];
array[i - 1] = array[j];
array[j] = t;
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.