其实最重要还是理解,不用刻意去记代码怎么写的。
选择排序:
每次从数组里选出一个最小的放在最前面,以此类推。
冒泡排序:
从后往前,两两交换,每次把最小的一个放到最前面。
插入排序:
每次把未排序的下一个元素插入到已排序的数组中。
希尔排序:
也叫缩小增量排序,每次跨度为数组总长度/2,以此类推。
快速排序:
找一个基准值,把比基准值大的都放数组右边,把比基准值小的,都放数组左边,而且依次递归。
堆排序:
可以实现部分排序,比如想在1000个数中找出最大的100个数,这个时候,没必要把剩下900个数都排序。
原理为:
a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
(个人强烈建议,当数据量特别大时,快速排序和希尔排序,所用时间要远远小于前面几种排序方式)
附上各种排序算法的复杂度,虽然讲暂时不太清楚为什么是这个值
排序法 | 最差时间分析 | 平均时间复杂度 | 稳定度 | 空间复杂度 |
冒泡排序 | O(n2) | O(n2) | 稳定 | O(1) |
快速排序 | O(n2) | O(n*log2n) | 不稳定 | O(log2n)~O(n) |
选择排序 | O(n2) | O(n2) | 稳定 | O(1) |
二叉树排序 | O(n2) | O(n*log2n) | 不一顶 | O(n) |
插入排序 | O(n2) | O(n2) | 稳定 | O(1) |
堆排序 | O(n*log2n) | O(n*log2n) | 不稳定 | O(1) |
希尔排序 | O | O | 不稳定 | O(1) |
多的就不说了,直接上代码。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using Debug = UnityEngine.Debug;
public class SortManager : MonoBehaviour
{
private List<int> _oldInts = new List<int>() { 57, 44, 11, 45, 67, 888, 90, 53 };
private List<int> intlists;
private List<string> stringlists;
private int? num;
// Use this for initialization
void Start()
{
}
private void Sort(Action ac)
{
_oldInts.Clear();
for (int i = 0; i < 10000; i++)
{
int tem = UnityEngine.Random.Range(0, 10000);
_oldInts.Add(tem);
}
Stopwatch watch = new Stopwatch();
watch.Start();
ac();
watch.Stop();
Debug.Log(watch.ElapsedMilliseconds);
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
Sort(() =>
{
SelectSort(_oldInts);
});
}
if (Input.GetKeyDown(KeyCode.B))
{
Sort(() =>
{
BubleSort(_oldInts);
});
}
if (Input.GetKeyDown(KeyCode.C))
{
Sort(() =>
{
InsertSort(_oldInts);
});
}
if (Input.GetKeyDown(KeyCode.D))
{
Sort(() =>
{
ShellSort(_oldInts);
});
}
if (Input.GetKeyDown(KeyCode.O))
{
Sort(() => { QuickSort(_oldInts, 0, _oldInts.Count - 1); });
}
if (Input.GetKeyDown(KeyCode.P))
{
Sort(() => { sort(_oldInts); });
}
if (Input.GetKeyDown(KeyCode.M))
{
ShowOldLists();
}
}
/// <summary>
/// 选择排序,每次循环把后面未排序完的元素中找到最小的放在数组后面
/// </summary>
/// <param name="lists"></param>
private void SelectSort(List<int> lists)
{
for (int i = 0; i < lists.Count - 1; i++)
{
for (int j = i + 1; j < lists.Count; j++)
{
if (lists[i] > lists[j])
{
int temValue = lists[i];
lists[i] = lists[j];
lists[j] = temValue;
}
}
}
OutputLists(lists);
}
/// <summary>
/// 冒泡排序,每次把最小的一个放在最前面,依次到最后一个
/// </summary>
/// <param name="lists"></param>
private void BubleSort(List<int> lists)
{
for (int i = 0; i < lists.Count; i++)
{
for (int j = lists.Count - 1; j > i; j--)
{
if (lists[j] < lists[j - 1])
{
int temValue = lists[j - 1];
lists[j - 1] = lists[j];
lists[j] = temValue;
}
}
}
OutputLists(lists);
}
/// <summary>
/// 插入排序,每次把下一个元素,插入到已经排好顺序的数组中
/// </summary>
/// <param name="lists"></param>
private void InsertSort(List<int> lists)
{
for (int i = 0; i < lists.Count; i++)
{
for (int j = i; j > 0; j--)
{
if (lists[j] > lists[j - 1])
{
break;
}
else
{
int tem = lists[j];
lists[j] = lists[j - 1];
lists[j - 1] = tem;
}
}
}
OutputLists(lists);
}
/// <summary>
/// 希尔排序,也叫缩小增量排序
/// </summary>
/// <param name="list"></param>
private void ShellSort(List<int> list)
{
int step = list.Count / 2;
bool change = false;
while (step >= 1)
{
do
{
change = false;
for (int i = 0; i < list.Count - step; i++)
{
if (list[i] > list[i + step])
{
int tem = list[i + step];
list[i + step] = list[i];
list[i] = tem;
change = true;
}
}
} while (change);
step = step / 2;
}
OutputLists(list);
}
private void OutputLists(List<int> lists)
{
//for (int i = 0; i < lists.Count; i++)
//{
// Debug.Log(lists[i]+"\n");
//}
}
public static void sort(List<int> arr)
{
//1.构建大顶堆
for (int i = arr.Count / 2 - 1; i >= 0; i--)
{
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(arr, i, arr.Count);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for (int j = arr.Count - 1; j > 0; j--)
{
swap(arr, 0, j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr, 0, j);//重新对堆进行调整
}
}
/**
* 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
* @param arr
* @param i
* @param length
*/
public static void adjustHeap(List<int> arr, int i, int length)
{
int temp = arr[i];//先取出当前元素i
for (int k = i * 2 + 1; k < length; k = k * 2 + 1)
{//从i结点的左子结点开始,也就是2i+1处开始
if (k + 1 < length && arr[k] < arr[k + 1])
{//如果左子结点小于右子结点,k指向右子结点
k++;
}
if (arr[k] > temp)
{//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}
else
{
break;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
/**
* 交换元素
* @param arr
* @param a
* @param b
*/
public static void swap(List<int> arr, int a, int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
private void QuickSort(List<int> lists, int left, int right)
{
if (left >= right)
return;
int key = lists[left];
int i = left;
int j = right;
while (i < j)
{
while (i < j && lists[j] >= key)
{
j--;
}
lists[i] = lists[j];
while (i < j && lists[i] <= key)
{
i++;
}
lists[j] = lists[i];
}
lists[i] = key;
QuickSort(lists, left, i - 1);
QuickSort(lists, i + 1, right);
}
private void ShowOldLists()
{
OutputLists(_oldInts);
}
}
这是四种排序方式打印出来的时间消耗:
快速排序和希尔排序的时间消耗要远小于其他几种排序,至于那个时间复杂度,抱歉,大学逃课去了,真心忘了。