12
社区成员
发帖
与我相关
我的任务
分享指针刷题日记
指针的定义和删除
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace testnode
{
public class ListNode {
public int val;
public ListNode next;
public ListNode(int val = 0,ListNode next = null) {
this.val = val;
this.next = next;
}
}
internal class Program
{
//递归的方式
//代码最简洁
public static ListNode RemoveElementsD(ListNode head, int val)
{
if (head == null)
{
return head;
}
head.next = RemoveElementsD(head.next, val);
return head.val == val ? head.next:head;
}
//情况判断的方式
//效率最高
public static ListNode RemoveElements(ListNode head, int val)
{
ListNode upNode = head;
ListNode doNode = head;
if (head == null)
{
return head;
}
while (upNode != null)//结点为upnode,而不是upnode.next
{
//这个是错误所在,没有对头结点的位置进行重置仍然保留
if (upNode.val == val && doNode.val == val)
{
upNode = upNode.next;
doNode = upNode;
head = doNode;
}
else if (upNode.val == val && doNode.val != val)
{
doNode.next = upNode.next;
upNode = upNode.next;
}
else if(upNode.val != val && doNode.val != val)
{
doNode = upNode;
upNode = upNode.next;
}
}
return head;
}
static void Main(string[] args)
{
int[] headArray = new[] { 6, 2, 6, 3, 4,5, 6 };
int val = 6;
ListNode head = null;
ListNode tail = null;
foreach (var item in headArray)
{
ListNode newNode = new ListNode(item);//创建新的节点
if (head == null)
{
head = newNode;//需要两个节点便于保存和遍历
tail = newNode;
}
else
{
tail.next = newNode;
tail = newNode;
}
}
RemoveElements(head,val);
}
}
}
在unity开发中的实际运用
基于指针的特性,删除插入效率高,遍历效率低,适用于需要频繁插入删除的场景
对于第一种解题方式,通过递归的方式进行求解
,这种方式简洁,但是也存在一定的风险,就是栈溢出 (Stack Overflow): 如果链表非常长(例如几十万个节点),每次递归调用都会占用栈空间。这在Unity或者任何C#应用程序中都可能导致 StackOverflowException。在游戏开发中,我们通常会避免深度递归,因为它不好调试且有性能风险。
但是在某些场景任然适用,比如
任务队列 / 事件链:
示例: 玩家完成一系列动作(如:捡起物品->使用技能->触发动画)。你可以用一个短的 ListNode 链表来表示这些顺序执行的任务。当一个任务完成后,通过类似 RemoveElementsD 的逻辑,可以把已完成的任务节点“删除”并继续下一个。递归在这里可能用于处理嵌套任务。
优势: 链表的插入和删除效率高,特别是中间部分。
接下来看看如何运用到实际开发中
首先定义基本的数据结构
TaskNode用于存储人物节点
csharp
using UnityEngine;
using System.Collections;
public class TaskNode
{
public GameTask Task; // 存储实际的任务对象
public TaskNode Next; // 指向下一个任务节点
public TaskNode(GameTask task)
{
Task = task;
Next = null;
}
}
// 自定义任务链表类
public class TaskLinkedList
{
public TaskNode Head; // 链表的头节点
// 添加任务到链表尾部
public void AddTask(GameTask task)
{
TaskNode newNode = new TaskNode(task);
if (Head == null)
{
Head = newNode;
return;
}
TaskNode current = Head;
while (current.Next != null)
{
current = current.Next;
}
current.Next = newNode;
}
// 获取下一个待执行的任务
public GameTask GetNextPendingTask()
{
if (Head == null)
return null;
if ((Head.Task.IsCompleted || Head.Task.IsSkipped) && Head.Next != null)
{
Debug.Log($"清理头部已完成任务:{Head.Task.Description}");
Head = Head.Next;
return GetNextPendingTask();
}
return Head.Task;
}
public void CleanCompletedTasks()
{
if (Head == null) return;
TaskNode dummyHead = new TaskNode(null); // 虚拟头节点
dummyHead.Next = Head;
TaskNode current = dummyHead;
while (current.Next != null)
{
if (current.Next.Task.IsCompleted || current.Next.Task.IsSkipped) // 检测任务是否完成或者跳过
{
Debug.Log($"清理任务: {current.Next.Task.Description}");
current.Next = current.Next.Next;
}
else
{
current = current.Next; // 移动到下一个节点
}
}
Head = dummyHead.Next; // 更新 Head
}
public void AdvanceHead()
{
if (Head != null)
{
if (Head.Task.IsCompleted || Head.Task.IsSkipped) // 再次确认是否已完成
{
Head = Head.Next; // 直接前进到下一个任务
}
}
}
public void PrintTaskList()
{
if (Head == null)
{
Debug.Log("任务链表为空。");
return;
}
TaskNode current = Head;
string log = "任务链表: ";
while (current != null)
{
log += $"[{current.Task.Description} ({(current.Task.IsCompleted ? "C" : (current.Task.IsSkipped ? "S" : "P"))})] -> "; // "C" completed, "P" pending
current = current.Next;
}
Debug.Log(log + "END");
}
}
其次定义号任务节点,在后续定义具体任务的时候会使用到
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
// 任务基类 所有具体的任务都需要继承它
public abstract class GameTask
{
public string Description { get; protected set; }
public bool IsCompleted { get; protected set; } = false; // 任务的完成状态
public bool IsRunning { get; protected set; } = false; // 任务是否正在运行
public bool IsSkipped { get; set; } = false; //任务是否被跳过
public GameManager GameManagerRef { get; set; }
public GameTask(string description)
{
Description = description;
}
public abstract IEnumerator Execute();
// 标记任务完成
public virtual void MarkAsCompleted()
{
IsCompleted = true;
IsRunning = false;
Debug.Log($"任务 '{Description}' 已完成.");
}
// 标记任务跳过
public virtual void SkipTask()
{
IsSkipped = true;
IsRunning = false;
Debug.Log($"任务 '{Description}' 已跳过.");
}
}
接下来我们需要定义一个用于执行时间链的函数,需要使用到上面定义的NodeAndLink
// TaskExecutor MonoBehaviour
using System.Collections;
using UnityEngine;
public class TaskExecutorCustomLinkedList : MonoBehaviour
{
public TaskLinkedList TaskList = new TaskLinkedList();
private bool isExecutingTask = false;//判断当前是否有其他任务正在执行
public GameManager GameManagerRef { get; set; }
void Start()
{
if (GameManagerRef == null) GameManagerRef = FindObjectOfType<GameManager>();
}
void Update()
{
// 确保 GameManagerRef 不为 null
if (GameManagerRef == null)
{
Debug.LogError("GameManagerRef is not assigned!");
return;
}
// 执行任务的逻辑,避免 Update 过于复杂
if (!isExecutingTask)
{
GameTask task = TaskList.GetNextPendingTask();
if (task != null)
{
StartCoroutine(ExecuteTask(task));
}
else
{
//Debug.Log("所有任务已完成。");
}
}
}
// 添加任务
public void AddTask(GameTask task)
{
task.GameManagerRef = GameManagerRef; // 设置 Game Manager 引用
TaskList.AddTask(task);
Debug.Log($"任务 '{task.Description}' 已添加到队列。");
}
// 执行单个任务 (主执行逻辑,使用协程)
private IEnumerator ExecuteTask(GameTask task)
{
isExecutingTask = true;
// 确保任务的 GameManagerRef 已设置
if (task.GameManagerRef == null)
task.GameManagerRef = GameManagerRef;
// 执行任务
Debug.Log($"开始执行: {task.Description}");
yield return task.Execute();
// 任务执行完毕后,检查状态,并做相应的处理
// 1. 任务已完成,清理链表 (注意这里使用 CleanCompletedTasks!)
// 2. 任务未完成或被跳过
Debug.Log($"任务 '{task.Description}' 执行完毕。");
TaskList.CleanCompletedTasks();
TaskList.AdvanceHead(); // 如果任务已完成或跳过,Head 需要前进
isExecutingTask = false;
}
// 外部调用接口,清理所有已完成的任务
public void CleanAllCompletedTasks()
{
TaskList.CleanCompletedTasks();
}
// 外部调用接口,清空所有未完成的任务
public void ClearAllTasks()
{
TaskList = new TaskLinkedList(); // 创建一个新的空链表
TaskExecutorCustomLinkedListDebugLog("所有任务已清除.");
}
// 调试用日志
public void TaskExecutorCustomLinkedListDebugLog(string message)
{
Debug.Log($"[TaskExecutorCustomLinkedList]: {message}");
}
}
```
根据上述定义,实现具体的类
```csharp
// 具体的任务类: 捡起物品的任务
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PickUpItemTask : GameTask
{
private string itemName;
public PickUpItemTask(string item) : base($"捡起物品: {item}")
{
itemName = item;
}
public override IEnumerator Execute()
{
IsRunning = true;
Debug.Log($"开始执行任务: {Description}");
// 模拟捡起物品的逻辑和延迟
yield return new WaitForSeconds(1.5f); //模拟捡起物品的时间
// 执行完成,标记完成
MarkAsCompleted();
GameManagerRef.UIController.ShowMessage($"你捡起了 {itemName}!");
}
}
// 具体的任务类:使用技能
public class UseSkillTask : GameTask
{
private string skillName;
public UseSkillTask(string skill) : base($"使用技能: {skill}")
{
skillName = skill;
}
public override IEnumerator Execute()
{
IsRunning = true;
Debug.Log($"开始执行任务: {Description}");
// 模拟技能释放的逻辑
yield return new WaitForSeconds(0.8f); // 模拟一些技能时间
MarkAsCompleted();
GameManagerRef.PlayerController.CastSkill(skillName);
GameManagerRef.UIController.ShowMessage($"你使用了 {skillName}!");
}
}
// 具体的任务类: 触发动画
public class TriggerAnimationTask : GameTask
{
private string animName;
public TriggerAnimationTask(string anim) : base($"播放动画: {anim}")
{
animName = anim;
}
public override IEnumerator Execute()
{
IsRunning = true;
Debug.Log($"开始执行任务: {Description}");
// 等待播放动画完成
yield return new WaitForSeconds(2.0f); // 模拟动画播放时间
MarkAsCompleted();
GameManagerRef.PlayerController.PlayAnimation(animName);
GameManagerRef.UIController.ShowMessage($"动画 {animName} 播放完毕。");
}
}
// 复合任务: 可以包含其他子任务
public class CompositeTask : GameTask
{
public List<GameTask> SubTasks = new List<GameTask>();
private int currentSubTaskIndex = 0; // 当前执行的子任务索引
public CompositeTask(string description) : base(description)
{
}
public override IEnumerator Execute()
{
IsRunning = true;
Debug.Log($"开始执行组合任务: {Description}");
// 循环执行子任务
while (currentSubTaskIndex < SubTasks.Count)
{
GameTask subTask = SubTasks[currentSubTaskIndex];
subTask.GameManagerRef = GameManagerRef; // 确保子任务能访问 GameManager
Debug.Log($" 开始执行子任务: {subTask.Description}");
yield return subTask.Execute(); // 执行子任务(递归的核心!)
if (!subTask.IsCompleted && !subTask.IsSkipped)
{
Debug.LogError($" 子任务 '{subTask.Description}' 执行失败,终止组合任务.");
IsRunning = false;
IsSkipped = true; // 标记组合任务为跳过,或者根据逻辑处理
yield break; // 结束协程
}
currentSubTaskIndex++; // 移动到下一个子任务
Debug.Log($" 子任务 '{subTask.Description}' 执行完毕.");
}
// 所有子任务都已完成
MarkAsCompleted();
Debug.Log($"组合任务 '{Description}' 执行完毕.");
}
public void AddSubTask(GameTask task)
{
SubTasks.Add(task);
}
}
还需要定义一个游戏管理类
// 游戏管理器,负责组织
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public TaskExecutorCustomLinkedList TaskExecutor;
public PlayerController PlayerController;
public UIController UIController;
public Button onSkip;
void Awake()
{
// 自动查找或者创建必要组件 - 确保TaskExecutor,PlayerController, UIController 都被找到了
if (TaskExecutor == null)
{
GameObject taskExecutorGO = new GameObject("TaskExecutor");
TaskExecutor = taskExecutorGO.AddComponent<TaskExecutorCustomLinkedList>();
TaskExecutor.GameManagerRef = this; // 设置GameManager引用
}
if (PlayerController == null)
{
GameObject playerGO = new GameObject("Player");
PlayerController = playerGO.AddComponent<PlayerController>();
}
if (UIController == null)
{
GameObject uiGO = new GameObject("UI");
UIController = uiGO.AddComponent<UIController>();
}
}
void Start()
{
// 1. 找到组件
if (TaskExecutor == null) TaskExecutor = FindObjectOfType<TaskExecutorCustomLinkedList>();
if (PlayerController == null) PlayerController = FindObjectOfType<PlayerController>();
if (UIController == null) UIController = FindObjectOfType<UIController>();
// 2. 初始化TaskExecutor 的 GameManager 引用
if (TaskExecutor != null)
{
TaskExecutor.GameManagerRef = this;
}
// 3. 创建任务 (示例)
// (使用AddTask 添加单独的任务)
TaskExecutor.AddTask(new PickUpItemTask("钥匙"));
TaskExecutor.AddTask(new UseSkillTask("火球术"));
TaskExecutor.AddTask(new TriggerAnimationTask("攻击动画"));
// (使用AddTask 添加复合任务)
CompositeTask compoundTask = new CompositeTask("打开宝箱");
compoundTask.AddSubTask(new TriggerAnimationTask("宝箱开启动画"));
compoundTask.AddSubTask(new PickUpItemTask("金币"));
compoundTask.AddSubTask(new UseSkillTask("叮铃声"));
TaskExecutor.AddTask(compoundTask); // 将复合任务添加到队列中
TaskExecutor.TaskList.PrintTaskList();
//当触发某个任务的时候,就可以跳过指定的人物
onSkip.onClick.AddListener(() => {
compoundTask.IsSkipped = true;
});
}
// 外部调用示例:在某个按钮点击时手动清理队列
public void OnClickCleanButton()
{
TaskExecutor.CleanAllCompletedTasks();
TaskExecutor.TaskList.PrintTaskList();
}
// 外部调用示例:取消所有待执行的任务
public void OnClickClearAllButton()
{
TaskExecutor.ClearAllTasks();
}
/// <summary>
/// 按钮调用
/// </summary>
public void skipTask()
{
}
}
其他类,支持扩展,在具体实践中可以添加需要的功能
```csharp
// UI和玩家控制器,用于模拟游戏行为
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public void CastSkill(string skillName)
{
Debug.Log($"Player casts skill: {skillName}");
}
public void PlayAnimation(string animName)
{
Debug.Log($"Player plays animation: {animName}");
}
}
using UnityEngine;
public class UIController : MonoBehaviour
{
public void ShowMessage(string message)
{
Debug.Log($"UI Message: {message}");
}
}