基本概念
顺序存储
顺序存储(Sequential Storage)是指将数据按顺序存放在连续的内存空间中。最常见的顺序存储结构是数组。
特点:
数据在内存中是连续存储的。
每个元素有固定的索引,通过索引可以快速访问元素。
链式存储
链式存储(Linked Storage)是指数据通过指针或引用进行链接,每个数据元素包含数据和指向下一个元素的指针。最常见的链式存储结构是链表。
特点:
数据在内存中不需要连续存储。
每个元素包含数据和指向下一个元素的指针。
用途
顺序存储的用途
顺序存储适用于以下情况:
数据量固定或变化不大。
需要频繁的随机访问。
链式存储的用途
链式存储适用于以下情况:
数据量不固定,经常需要插入和删除操作。
不需要频繁的随机访问。
在游戏开发中的用途
顺序存储在游戏开发中的用途
存储静态数据:游戏中的配置数据、静态表格数据等。
快速访问:需要快速访问某个特定元素时,比如游戏地图的二维数组表示。
链式存储在游戏开发中的用途
动态数据管理:比如游戏中的任务链、动态生成的对象列表。
对象池管理:管理动态生成和销毁的对象,比如子弹、敌人等。
语法规则及声明
顺序存储(数组)
// 声明并初始化一个整型数组
int[] numbers = new int[10];
// 访问和赋值
numbers[0] = 1;
int firstNumber = numbers[0];
链式存储(链表)
using System;
using System.Collections.Generic;
// 声明并初始化一个链表
LinkedList<int> linkedList = new LinkedList<int>();
// 添加元素
linkedList.AddLast(1);
linkedList.AddLast(2);
linkedList.AddLast(3);
// 遍历链表
foreach (int number in linkedList)
{
Console.WriteLine(number);
}
示例代码及解释
顺序存储示例
using System;
public class Program
{
public static void Main()
{
// 声明并初始化一个整型数组
int[] numbers = new int[5] { 1, 2, 3, 4, 5 };
// 输出数组中的元素
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine($"Element at index {i}: {numbers[i]}");
}
// 修改数组中的元素
numbers[2] = 10;
Console.WriteLine($"Element at index 2 after modification: {numbers[2]}");
}
}
链式存储示例
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// 声明并初始化一个链表
LinkedList<int> linkedList = new LinkedList<int>();
// 添加元素
linkedList.AddLast(1);
linkedList.AddLast(2);
linkedList.AddLast(3);
// 输出链表中的元素
foreach (int number in linkedList)
{
Console.WriteLine(number);
}
// 删除元素
linkedList.Remove(2);
Console.WriteLine("After removing element 2:");
// 再次输出链表中的元素
foreach (int number in linkedList)
{
Console.WriteLine(number);
}
}
}
优点和缺点
顺序存储
优点:
快速访问:通过索引可以快速访问任意元素。
内存利用率高:数据连续存储,内存利用率高。
缺点:
插入和删除操作效率低:需要移动大量元素。
内存空间要求高:需要连续的内存空间。
链式存储
优点:
插入和删除操作效率高:只需要修改指针。
内存利用率高:不需要连续的内存空间。
缺点:
访问效率低:不能通过索引快速访问元素。
内存开销大:需要存储指针或引用。
基础题目和进阶题目
基础题目
创建一个
int
数组,存储 5 个整数,并输出所有元素。创建一个
LinkedList<string>
,添加一些名字,然后输出所有名字。创建一个
int
数组,计算并输出所有元素的和。
进阶题目
创建一个
int
数组,实现一个方法,反转数组中的元素顺序。创建一个
LinkedList<int>
,实现一个方法,从链表中删除所有偶数元素。创建一个
LinkedList<string>
,实现一个方法,按字典顺序排序链表中的元素。
常用的顺序存储结构
1. 数组(Array)
数组是最基本的顺序存储结构,用于存储固定数量的同类型元素。
int[] numbers = new int[5];
numbers[0] = 1;
numbers[1] = 2;
优点:
通过索引可以快速访问元素,时间复杂度为 O(1)。
内存利用率高,数据连续存储。
缺点:
插入和删除操作效率低,时间复杂度为 O(n)。
数组大小固定,无法动态调整。
2. 动态数组(Dynamic Array)
动态数组是数组的扩展版本,可以根据需要动态调整大小。C# 中的 List<T>
就是一个常用的动态数组。
List<int> numbers = new List<int>();
numbers.Add(1);
numbers.Add(2);
优点:
支持动态调整大小。
提供了丰富的方法,操作方便。
缺点:
在扩展数组时可能会有性能开销。
插入和删除操作效率低,时间复杂度为 O(n)。
3. 字符串(String)
字符串本质上是一个字符数组,用于存储和操作文本。
string text = "Hello, World!";
char firstChar = text[0];
优点:
提供了丰富的字符串操作方法。
可以通过索引快速访问字符。
缺点:
不可变,一旦创建就不能修改。
对于大量的字符串操作,可能会导致性能问题。
常用的链式存储结构
1. 单链表(Singly Linked List)
单链表是链表的一种,每个节点包含数据和指向下一个节点的指针。
public class Node
{
public int Data;
public Node Next;
public Node(int data)
{
Data = data;
Next = null;
}
}
public class SinglyLinkedList
{
public Node Head;
public void Add(int data)
{
Node newNode = new Node(data);
if (Head == null)
{
Head = newNode;
}
else
{
Node current = Head;
while (current.Next != null)
{
current = current.Next;
}
current.Next = newNode;
}
}
}
优点:
插入和删除操作效率高,时间复杂度为 O(1)。
内存利用率高,不需要连续的内存空间。
缺点:
访问效率低,时间复杂度为 O(n)。
需要额外的内存来存储指针。
2. 双链表(Doubly Linked List)
双链表是一种链表,每个节点包含数据、指向下一个节点和前一个节点的指针。
public class DoublyNode
{
public int Data;
public DoublyNode Next;
public DoublyNode Prev;
public DoublyNode(int data)
{
Data = data;
Next = null;
Prev = null;
}
}
public class DoublyLinkedList
{
public DoublyNode Head;
public void Add(int data)
{
DoublyNode newNode = new DoublyNode(data);
if (Head == null)
{
Head = newNode;
}
else
{
DoublyNode current = Head;
while (current.Next != null)
{
current = current.Next;
}
current.Next = newNode;
newNode.Prev = current;
}
}
}
优点:
支持双向遍历。
插入和删除操作效率高,时间复杂度为 O(1)。
缺点:
访问效率低,时间复杂度为 O(n)。
需要更多的内存来存储前后指针。
3. 循环链表(Circular Linked List)
循环链表是一种链表,最后一个节点指向第一个节点,形成一个环状结构。
public class CircularNode
{
public int Data;
public CircularNode Next;
public CircularNode(int data)
{
Data = data;
Next = null;
}
}
public class CircularLinkedList
{
public CircularNode Head;
public void Add(int data)
{
CircularNode newNode = new CircularNode(data);
if (Head == null)
{
Head = newNode;
Head.Next = Head;
}
else
{
CircularNode current = Head;
while (current.Next != Head)
{
current = current.Next;
}
current.Next = newNode;
newNode.Next = Head;
}
}
}
优点:
支持循环遍历。
插入和删除操作效率高,时间复杂度为 O(1)。
缺点:
访问效率低,时间复杂度为 O(n)。
需要额外的内存来存储指针。
4. 跳表(Skip List)
跳表是一种链表,每个节点除了指向下一个节点外,还包含多个层级的指针,用于跳跃访问,提高查找效率。
csharp
Copy
// 跳表的实现较为复杂,这里简单介绍其概念
public class SkipListNode
{
public int Value;
public SkipListNode[] Forward;
public SkipListNode(int level, int value)
{
Forward = new SkipListNode[level + 1];
Value = value;
}
}
public class SkipList
{
private readonly int _maxLevel;
private readonly SkipListNode _header;
public SkipList(int maxLevel)
{
_maxLevel = maxLevel;
_header = new SkipListNode(maxLevel, 0);
}
// 其他方法如插入、删除、查找等略
}
优点:
提供了更快的查找效率,平均时间复杂度为 O(log n)。
插入和删除操作也比较高效。
缺点:
实现较为复杂。
需要额外的内存来存储多层级指针。