OneBigLoser
OneBigLoser
发布于 2024-07-20 / 16 阅读
0
0

LinkedList学习

基本概念

LinkedList 是一种链式存储结构,它由一系列节点(Node)组成,每个节点包含数据和指向下一个节点的指针。链表中的节点不需要连续存储,每个节点通过指针(引用)连接在一起。

用途

LinkedList 常用于以下情况:

  • 动态数据管理:适用于数据量不固定,经常需要插入和删除操作的情况。

  • 队列和栈:可以用链表实现队列(FIFO)和栈(LIFO)结构。

  • 图和树:链表可以用于实现图和树的邻接表等。

在游戏开发中的用途

在游戏开发中,LinkedList 也有很多实际应用:

  • 管理游戏对象:如敌人列表、任务列表,方便动态增加和删除对象。

  • 事件队列:处理游戏中的事件队列,按顺序处理事件。

  • 路径存储:存储路径点(如寻路算法中的路径),方便动态调整路径。

语法规则及声明

在 C# 中,LinkedList 位于 System.Collections.Generic 命名空间下。我们可以使用 LinkedList<T> 来声明一个链表,其中 T 表示链表中存储的元素类型。

using System.Collections.Generic;

LinkedList<int> numbers = new LinkedList<int>();
LinkedList<string> names = new LinkedList<string>();

常用方法

  • AddFirst(T value):在链表开头添加一个元素。

  • AddLast(T value):在链表末尾添加一个元素。

  • Remove(T value):移除链表中的指定元素。

  • RemoveFirst():移除链表中的第一个元素。

  • RemoveLast():移除链表中的最后一个元素。

  • Find(T value):查找链表中指定值的节点。

  • Count:获取链表中元素的数量。

  • Clear():清空链表中的所有元素。

示例代码及解释

示例1:基本操作

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        LinkedList<int> numbers = new LinkedList<int>();

        // 在链表末尾添加元素
        numbers.AddLast(1);
        numbers.AddLast(2);
        numbers.AddLast(3);

        // 在链表开头添加元素
        numbers.AddFirst(0);

        // 输出链表中的所有元素
        foreach (int number in numbers)
        {
            Console.WriteLine(number);
        }

        // 查找并移除元素
        LinkedListNode<int> node = numbers.Find(2);
        if (node != null)
        {
            numbers.Remove(node);
        }

        Console.WriteLine("After removing 2:");

        // 再次输出链表中的所有元素
        foreach (int number in numbers)
        {
            Console.WriteLine(number);
        }
    }
}
using System;
using System.Text;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        // Create the link list.
        string[] words =
            { "the", "fox", "jumps", "over", "the", "dog" };
        LinkedList<string> sentence = new LinkedList<string>(words);
        Display(sentence, "The linked list values:");

        // Add the word 'today' to the beginning of the linked list.
        sentence.AddFirst("today");
        Display(sentence, "Test 1: Add 'today' to beginning of the list:");

        // Move the first node to be the last node.
        LinkedListNode<string> mark1 = sentence.First;
        sentence.RemoveFirst();
        sentence.AddLast(mark1);
        Display(sentence, "Test 2: Move first node to be last node:");

        // Change the last node to 'yesterday'.
        sentence.RemoveLast();
        sentence.AddLast("yesterday");
        Display(sentence, "Test 3: Change the last node to 'yesterday':");

        // Move the last node to be the first node.
        mark1 = sentence.Last;
        sentence.RemoveLast();
        sentence.AddFirst(mark1);
        Display(sentence, "Test 4: Move last node to be first node:");

        // Indicate the last occurence of 'the'.
        sentence.RemoveFirst();
        LinkedListNode<string> current = sentence.FindLast("the");
        IndicateNode(current, "Test 5: Indicate last occurence of 'the':");

        // Add 'lazy' and 'old' after 'the' (the LinkedListNode named current).
        sentence.AddAfter(current, "old");
        sentence.AddAfter(current, "lazy");
        IndicateNode(current, "Test 6: Add 'lazy' and 'old' after 'the':");

        // Indicate 'fox' node.
        current = sentence.Find("fox");
        IndicateNode(current, "Test 7: Indicate the 'fox' node:");

        // Add 'quick' and 'brown' before 'fox':
        sentence.AddBefore(current, "quick");
        sentence.AddBefore(current, "brown");
        IndicateNode(current, "Test 8: Add 'quick' and 'brown' before 'fox':");

        // Keep a reference to the current node, 'fox',
        // and to the previous node in the list. Indicate the 'dog' node.
        mark1 = current;
        LinkedListNode<string> mark2 = current.Previous;
        current = sentence.Find("dog");
        IndicateNode(current, "Test 9: Indicate the 'dog' node:");

        // The AddBefore method throws an InvalidOperationException
        // if you try to add a node that already belongs to a list.
        Console.WriteLine("Test 10: Throw exception by adding node (fox) already in the list:");
        try
        {
            sentence.AddBefore(current, mark1);
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine("Exception message: {0}", ex.Message);
        }
        Console.WriteLine();

        // Remove the node referred to by mark1, and then add it
        // before the node referred to by current.
        // Indicate the node referred to by current.
        sentence.Remove(mark1);
        sentence.AddBefore(current, mark1);
        IndicateNode(current, "Test 11: Move a referenced node (fox) before the current node (dog):");

        // Remove the node referred to by current.
        sentence.Remove(current);
        IndicateNode(current, "Test 12: Remove current node (dog) and attempt to indicate it:");

        // Add the node after the node referred to by mark2.
        sentence.AddAfter(mark2, current);
        IndicateNode(current, "Test 13: Add node removed in test 11 after a referenced node (brown):");

        // The Remove method finds and removes the
        // first node that that has the specified value.
        sentence.Remove("old");
        Display(sentence, "Test 14: Remove node that has the value 'old':");

        // When the linked list is cast to ICollection(Of String),
        // the Add method adds a node to the end of the list.
        sentence.RemoveLast();
        ICollection<string> icoll = sentence;
        icoll.Add("rhinoceros");
        Display(sentence, "Test 15: Remove last node, cast to ICollection, and add 'rhinoceros':");

        Console.WriteLine("Test 16: Copy the list to an array:");
        // Create an array with the same number of
        // elements as the linked list.
        string[] sArray = new string[sentence.Count];
        sentence.CopyTo(sArray, 0);

        foreach (string s in sArray)
        {
            Console.WriteLine(s);
        }

        Console.WriteLine("Test 17: linked list Contains 'jumps' = {0}",
            sentence.Contains("jumps"));
        
        // Release all the nodes.
        sentence.Clear();

        Console.WriteLine();
        Console.WriteLine("Test 18: Cleared linked list Contains 'jumps' = {0}",
            sentence.Contains("jumps"));

        Console.ReadLine();
    }

    private static void Display(LinkedList<string> words, string test)
    {
        Console.WriteLine(test);
        foreach (string word in words)
        {
            Console.Write(word + " ");
        }
        Console.WriteLine();
        Console.WriteLine();
    }

    private static void IndicateNode(LinkedListNode<string> node, string test)
    {
        Console.WriteLine(test);
        if (node.List == null)
        {
            Console.WriteLine("Node '{0}' is not in the list.\n",
                node.Value);
            return;
        }

        StringBuilder result = new StringBuilder("(" + node.Value + ")");
        LinkedListNode<string> nodeP = node.Previous;

        while (nodeP != null)
        {
            result.Insert(0, nodeP.Value + " ");
            nodeP = nodeP.Previous;
        }

        node = node.Next;
        while (node != null)
        {
            result.Append(" " + node.Value);
            node = node.Next;
        }

        Console.WriteLine(result);
        Console.WriteLine();
    }
}

//This code example produces the following output:
//
//The linked list values:
//the fox jumps over the dog

//Test 1: Add 'today' to beginning of the list:
//today the fox jumps over the dog

//Test 2: Move first node to be last node:
//the fox jumps over the dog today

//Test 3: Change the last node to 'yesterday':
//the fox jumps over the dog yesterday

//Test 4: Move last node to be first node:
//yesterday the fox jumps over the dog

//Test 5: Indicate last occurence of 'the':
//the fox jumps over (the) dog

//Test 6: Add 'lazy' and 'old' after 'the':
//the fox jumps over (the) lazy old dog

//Test 7: Indicate the 'fox' node:
//the (fox) jumps over the lazy old dog

//Test 8: Add 'quick' and 'brown' before 'fox':
//the quick brown (fox) jumps over the lazy old dog

//Test 9: Indicate the 'dog' node:
//the quick brown fox jumps over the lazy old (dog)

//Test 10: Throw exception by adding node (fox) already in the list:
//Exception message: The LinkedList node belongs a LinkedList.

//Test 11: Move a referenced node (fox) before the current node (dog):
//the quick brown jumps over the lazy old fox (dog)

//Test 12: Remove current node (dog) and attempt to indicate it:
//Node 'dog' is not in the list.

//Test 13: Add node removed in test 11 after a referenced node (brown):
//the quick brown (dog) jumps over the lazy old fox

//Test 14: Remove node that has the value 'old':
//the quick brown dog jumps over the lazy fox

//Test 15: Remove last node, cast to ICollection, and add 'rhinoceros':
//the quick brown dog jumps over the lazy rhinoceros

//Test 16: Copy the list to an array:
//the
//quick
//brown
//dog
//jumps
//over
//the
//lazy
//rhinoceros

//Test 17: linked list Contains 'jumps'= True

//Test 18: Cleared linked list Contains 'jumps'  = False
//

示例2:在游戏开发中的应用

using System;
using System.Collections.Generic;

public class Enemy
{
    public string Name { get; set; }
    public int Health { get; set; }

    public Enemy(string name, int health)
    {
        Name = name;
        Health = health;
    }

    public void TakeDamage(int damage)
    {
        Health -= damage;
        Console.WriteLine($"{Name} took {damage} damage, remaining health: {Health}");
    }
}

public class Game
{
    private LinkedList<Enemy> enemies;

    public Game()
    {
        enemies = new LinkedList<Enemy>();
    }

    public void AddEnemy(Enemy enemy)
    {
        enemies.AddLast(enemy);
    }

    public void RemoveEnemy(Enemy enemy)
    {
        enemies.Remove(enemy);
    }

    public void UpdateEnemies()
    {
        foreach (var enemy in enemies)
        {
            enemy.TakeDamage(10);
        }
    }
}

public class Program
{
    public static void Main()
    {
        Game game = new Game();

        // 添加敌人
        game.AddEnemy(new Enemy("Goblin", 100));
        game.AddEnemy(new Enemy("Orc", 150));

        // 更新敌人状态
        game.UpdateEnemies();

        // 移除一个敌人
        Enemy orc = new Enemy("Orc", 150);
        game.RemoveEnemy(orc);
    }
}

优点和缺点

优点

  1. 动态大小:链表可以根据需要动态调整大小。

  2. 高效插入/删除:插入和删除操作不需要移动大量元素,只需修改指针,时间复杂度为 O(1)。

  3. 内存利用率高:不需要连续的内存空间。

缺点

  1. 访问效率低:不能通过索引快速访问元素,时间复杂度为 O(n)。

  2. 额外内存开销:每个节点需要存储额外的指针,增加内存开销。

  3. 复杂性:链表的实现和管理比数组更复杂。

基础题目和进阶题目

基础题目

  1. 创建一个 LinkedList<int>,添加 5 个整数,并输出所有元素。

  2. 创建一个 LinkedList<string>,添加一些名字,然后输出所有名字。

  3. 创建一个 LinkedList<int>,实现一个方法,计算并输出链表中所有元素的和。

进阶题目

  1. 创建一个 LinkedList<int>,实现一个方法,反转链表中的元素顺序。

  2. 创建一个 LinkedList<int>,实现一个方法,从链表中删除所有偶数元素。

  3. 实现一个双链表(DoublyLinkedList),并实现插入、删除、查找等操作。

  4. 实现一个循环链表(CircularLinkedList),并实现插入、删除、查找等操作。


评论