赞
踩
Hello大家好我是开罗小8,今天我来给大家带来如何使用快速排序对背包,排行榜等游戏中常见集合排序,并将其与其他算法进行比较,以及如何对其进行优化。
在游戏领域中,经常会需要使用到排序的功能,例如玩家整理背包后对道具按稀有度,获得日期,ID等进行排序,或者在排行榜中对玩家的得分进行排序。不同的排序算法有不同的适用环境,有的排序在特定情况下排序的速度会更快,但却在另一种情况下不如其他的排序算法。因此我们需要根据不同的情况选择不同的算法。
本篇文章中,使用C#代码,让我们看一下如何将快速排序应用到游戏中。
C# List<>.Sort() 排序的底层实现原理_c#list的底层原理-CSDN博客
快速排序是一种常用的排序算法,时间复杂度为O(nlog n),空间复杂度为O(log n),它是一种不稳定的排序算法,在处理大规模数据时,快速排序的效率是最高的,因此使用最广泛。
不稳定:快速排序是一种不稳定的排序算法,不稳定体现在排序过后,相同值元素在排序前的相对位置可能发生改变,举个例子,比如给一个班的学生按成绩进行排序,小明和小红的分数相同,在排序前,小明在小红的前面,而排序后,小红变到了小明的前面。
特定情况下时间复杂度退化到O(n^2):在待排序集合基本处于有序的情况下,快速排序时间复杂度退化到O(n^2),变成选择排序。
我们以对班级学生按成绩进行排序举例
- public class Stu
- {
- public string Name;
- public int Score;
-
- public Stu(string name, int score)
- {
- Name = name;
- Score = score;
- }
- }
- class Program
- {
- static void Main()
- {
-
- List<Stu> list = new List<Stu>() {
- new Stu("小明", 100), new Stu("小红", 99), new Stu("小刚", 98),
- new Stu("小伟", 99), new Stu("小强", 100),
- };
-
- QuickSort(list, 0, list.Count - 1);
- }
- public static void QuickSort(List<Stu> list, int left, int right)
- {
-
- if (left >= right) return;
-
- Stu pivot = list[left];
- int i = left + 1, j = right;
- while (i < j)
- {
- while (i < j && list[i].Score >= pivot.Score) ++i;
- while (i < j && list[j].Score < pivot.Score) --j;
- if (i < j)
- {
- (list[i], list[j]) = (list[j], list[i]);
- }
-
- }
- if (list[i].Score < pivot.Score) --i;
- (list[i], list[left]) = (list[left], list[i]);
- QuickSort(list, left, i - 1);
- QuickSort(list, i + 1, right);
- }
- }
(list[i], list[j]) = (list[j], list[i]);为交换i和j的值
上面的排序算法正确的对每一个学生的成绩进行了排序,但是,成绩相同的两位同学的相对位置发生了变化,在排序前,小明排在小强的前面,但是排序后小强排到了小明的前面,有时这并不是我们想要的
- public class Stu : IComparable<Stu>
- {
- public string Name;
- public int Score;
- int ID;
-
- public Stu(string name, int score, int id)
- {
- Name = name;
- Score = score;
- ID = id;
- }
-
- public int CompareTo(Stu other)
- {
- if (other.Score != Score) return other.Score - Score;
- return ID - other.ID;
- }
- }
- class Program
- {
- static void Main()
- {
- List<Stu> list = new List<Stu>() {
- new Stu("小明", 100, 1), new Stu("小红", 99, 2), new Stu("小刚", 98, 3),
- new Stu("小伟", 99, 4), new Stu("小强", 100, 5),
- };
- //list.Sort();因为Stu实现了IComparable接口,因此可以直接利用自带的排序方法
- QuickSort2(list, 0, list.Count - 1);
-
- }
- public static void QuickSort2(List<Stu> list, int left, int right)
- {
-
- if (left >= right) return;
- Stu pivot = list[left];
- int i = left + 1, j = right;
- while (i < j)
- {
- while (i < j && list[i].CompareTo(pivot) <= 0) ++i;
- while (i < j && list[j].CompareTo(pivot) > 0) --j;
- if (i < j)
- {
- (list[i], list[j]) = (list[j], list[i]);
- }
-
- }
- if (list[i].CompareTo(pivot) > 0) --i;
- (list[i], list[left]) = (list[left], list[i]);
- QuickSort2(list, left, i - 1);
- QuickSort2(list, i + 1, right);
- }
- }
为了保证排序后相对顺序不变,我加入了一个新的字段ID,ID不会重复,排序的规则变成了以分数优先,其次再按ID进行排序,ID大的排在后面
整体的排序思路变为小于等于pivot的元素放在pivot的左边,大于pivot的元素放在右边,CompareTo用于比较两个元素的大小
继承这个接口后,可以直接利用.net自带的排序方法进行排序,.net自带的方法在排序方面做了更多的优化,效率更高,可以省去自己再写排序的代码的时间。
CompareTo的返回值用于判断大小,如果返回正数,说明比传入的对象大,负数说明小于传入的对象
.net对排序做的优化:
当数组基本有序时,快排会退化成选择排序,时间复杂度变为O(n^2),因此使用插入排序更合适,插入排序在数组基本有序的情况下的时间复杂度为O(n)
冒泡排序做一下优化也可以在数组基本有序的情况下复杂度为O(n),做法为在每一趟排序结束后做一次判断,如果这一趟排序没有元素发生交换,说明数组已经有序,直接终止排序
如果一定要用快排,可以从以下两方面入手
避免数组有序可以使用洗牌算法在排序前先打乱数组,来减少最差情况
随机选取pivot点的实现可以参考文章顶部的相关资料
- public static void Shuffle(int[] nums)
- {
- Random rand = new Random();
- int n = nums.Length;
- for(int i=0;i<n; ++i)
- {
- int index=rand.Next(i, n);
- (nums[i], nums[index]) = (nums[index], nums[i]);
- }
- }
本文介绍了快速排序在游戏领域的应用,展示了快速排序不稳定的特点会导致的问题,并提供解决方案,以及最大程度上减少快排退化成O(n^2)的可能性
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。