目录
快速排序:采用“分治法”思想。把大的拆分为小的,小的再拆分为更小的。
比较型排序:
冒泡排序:
冒泡排序
/// 冒泡排序的优点:每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。
/// 如例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,
/// 同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,
/// 以此类推……也就是说,没进行一趟比较,每一趟少比较一次,一定程度上减少了算法的量。
int[] arr = new int[] { 11, 33, 22, 55, 66, 88, 99, 199, 999, 77, 66, 6, 1 };
public void Maopaosorted()
{
//原理: 比较两个相邻的元素,将值大的元素交换到右边,相等值不交换
// N个数字要排序完成,总共进行N-1轮排序,每i轮的排序次数为(N-轮数)次 --从0轮开始,因为一
//开始全部都需要比较
//核心:双循环。外部循环为 比较 轮数,内部循环为 比较次数
for (int i = 0; i < arr.Length - 1 ; i++)
{
//arr.Length-1 是因为前面一个与后面一个比较。不减就会数组越界
for (int j = 0; j < arr.Length-1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
}
快速排序:采用“分治法”思想。把大的拆分为小的,小的再拆分为更小的。
参考链接:图解快排——快速排序算法(quick sort) - 知乎 (zhihu.com)
思想: 快速排序在每一轮挑选一个基准元素,并设置左右两个指针,左指针指向元素的开始,右指针指向元素的结尾。一开始左指针提出来为基准元素,该指针指向设为空。指向为空的指针不操作,操作有指向元素的指针,与基准元素比较。比基准元素大的放右边,小的放左边,与基准元素相等继续移动。当左右指针指向同一个位置时,放入此轮基准数,此轮排序完成。当完成一轮排序后,基准数左边全部小于基准数,右边全部大于基准数。
核心:分治、递归
/// <summary>
/// 快速排序
/// 核心:分治、递归
/// </summary>
public void Kuaisupaixu(int[] suzu,int left,int right)
{
if (left >= right) return;
int pivot = suzu[left];
int l =left; int r =right;
while (l < r)
{
while (l < r && suzu[r] >= pivot)
{
r--;
}
if (l < r)
{
suzu[l] = suzu[r];
l++;
}
while(l < r && suzu[l] <= pivot)
{
l++;
}
if (l < r)
{
suzu[r] = suzu[l];
r--;
}
}
suzu[l] = pivot;
//第一轮排序完后,分了左右,右边全部比右边小
//中间的基准数不用在比较
Kuaisupaixu(suzu, left, l-1);//左边分治
Kuaisupaixu(suzu, l+1, right);//右边分治
}
使用:
Kuaisupaixu(arr, 0, arr.Length - 1);
插入排序:将一个数据插入到另一个有序容器
C#实现——十大排序算法之插入排序 - 欧气柠檬 - 博客园 (cnblogs.com)
思想:从无序数组的第二个元素开始取出 比较值。使该比较值与之前的元素进行比较:该比较值小于比较元素时,该比较元素插入到前面,比较元素和后面的元素往后移一位。当无序数组遍历完后排序结束;
/// <summary>
/// 核心思想;双循环
/// 外层循环遍历无序元素,内层循环遍历有序元素 -- 因为是用无序元素与有序元素一一比较
/// </summary>
/// <param name="suzu"></param>
public void Insertpaixu(int[] suzu)
{
//无序
for (int i = 1; i < suzu.Length; i++)//默认下表为0的元素是有序的
{
int temp = suzu[i];//取出本轮比较值
int insetIndex = i;//优化后新增的
//有序
for (int j = i - 1; j >=0; j--)//无序元素为有序元素的后一位
{
if (temp < suzu[j])//有序元素值大于本轮的无序比较值;有序元素往后移一位
{
suzu[j + 1] = suzu[j];//有序元素往后移一位
//suzu[j] = temp;//空出位置放入比较值--可以优化:【当前为每比较一次都需要插入】--优化为:移动完位置,最后插入
insetIndex = j;//优化后新增的
}
}
suzu[insetIndex] = temp;//优化后新增的
}
}
再次优化:思路:使用for循环有序的数组,使用if比较。可得出有一个不满足条件之后其他的都不满足条件了,就可以退出循环了;--可以使用whie循环,既能循环又能判断。
/// <summary>
/// 核心思想;双循环
/// 外层循环遍历无序元素,内层循环遍历有序元素 -- 因为是用无序元素与有序元素一一比较
/// </summary>
/// <param name="suzu"></param>
public void Insertpaixu(int[] suzu)
{
//无序
for (int i = 1; i < suzu.Length; i++)//默认下表为0的元素是有序的
{
int temp = suzu[i];//取出本轮比较值
//遍历 有序部分
int j = i;//本轮循环开始时,J为有序数组的个数【无序的开始下标】;有序元素从后往前遍历
while (j > 0 && temp < suzu[j-1])//suzu[j-1]通过下标获取有序元素
{
suzu[j] = suzu[j-1];//该有序的元素往后移动一位
j--;//记录空出来的下标索引
}
suzu[j] = temp;//如果j没有发生--,插入的位置还是temp无序元素本身所在的位置
}
}
选择排序:
它的基本思想是将待排序序列分成已排序和未排序两部分,每次从未排序中找到最小值,然后将其插入到已排序序列的末尾,直到全部元素都排序完成。【最小值与未排序元素第一个交换位置】
/// <summary>
/// 选择排序
/// 核心:双循环
/// 外层循环为:有序部分。内层循环为无序部分
/// 本轮的最小值 与 轮次 位置的元素交换位置【】
/// </summary>
/// <param name="suzu"></param>
public void Xuanzepaixu(int[] suzu)
{
//有序
for (int i = 0; i < suzu.Length; i++)
{
//无序
int minIndex = i;//从下标为0的开始
for (int j = i+1; j < suzu.Length; j++)
{
if (suzu[j] < suzu[minIndex])
{
minIndex = j;//记录最小元素下标
}
}
//交换位置
int temp = suzu[i];
suzu[i] = suzu[minIndex];
suzu[minIndex] = temp;
}
}
希尔排序:
思想:希尔排序是将数据分组,将每一组数据进行插入排序。直到间隔小于 1 结束排序【间隔为1是最后一轮排序】。
步骤:将数据按照一定的间隔分组【通常为总长度的一半,奇偶数均可以】,将每组数据在组内进行插入排序;完成后在进行下一轮 间隔 分组【间隔为:上轮间隔的一半】。
大量数据时:希尔排序优于插入排序
参考链接:希尔排序详解(Shell Sort)-CSDN博客
/// <summary>
/// 希尔排序
/// 核心:三层循环
/// 希尔排序是将数据分组,将每一组进行插入排序。
/// 最外层确定 间隔增量【】,内层是插入排序对分组的元素排序
/// </summary>
/// <param name="data"></param>
public void ShellSort(int[] data)
{
for (int gap = data.Length / 2; gap > 0; gap = gap / 2)//获取到间隔和分组数量
{
for (var i = gap; i < data.Length; i++)//无序元素
{
var j = i;//每次比较得到插入的索引
var current = data[i];//获取到以间隔量开始的无序元素,此元素为无序的后一位
while (j - gap >= 0 && current < data[j - gap])//j - gap >= 0确保插入位不小于0;current < data[j - gap]无序元素与前面的有序元素比较
{
data[j] = data[j - gap];
j = j - gap;
}
data[j] = current;
}
}
}
归并排序:
核心:分治、递归
思想:将数据分成子数据直到成为单个数据,两两比较,合并答案【合并的数组是已经排好序的,直接比较两个数组的头部得到最小值,放入新容器】。需要额外空间。
参考链接:排序算法 - 归并排序详解-CSDN博客
抄录补充自己理解
/// <summary>
/// 归并排序--使用的下标
/// </summary>
/// <param name="suzu">待排序数组</param>
/// <param name="left">最左索引</param>
/// <param name="right">最右索引</param>
/// <param name="tempArr">临时数组</param>
void guibinpaixu(int[] suzu,int left,int right,int[] tempArr)
{
if(left>=right) { return; }
int mid = (left+right)/2;//中间索引--mid为左边子序列的结束索引。 mid+1为右边子序列的开始索引
//分治
guibinpaixu(suzu,left,mid,tempArr);//左子序列拆分,直到成为单个数据
guibinpaixu(suzu,mid+1,right,tempArr);//右子序列拆分,直到成为单个数据
//当上边的分治拆分完后,return出来,就是单个数据,在进行合并,依次往回合并
hebin(suzu, left, right, mid, tempArr);//每组拆分到最后的合并
}
/// <summary>
/// 合并操作
/// 把左边序列、右边序列合并,放入临时容器
/// </summary>
/// <param name="suzu">待排序数据</param>
/// <param name="left">最左索引</param>
/// <param name="right">最右索引</param>
/// <param name="mid">中间索引</param>
/// <param name="tempArr">临时数组</param>
void hebin(int[] suzu,int left,int right,int mid, int[] tempArr)
{
int l = left;//左边序列的头部索引
int r = mid + 1;//右边序列的头部索引,【如果不加1,那么就是左边的最后索引】
int t = 0;//临时数组的当前索引
// 1. 先把左右(有序)序列的数据按照规则填充到中转数组 temp
// 直到左右俩边的有序序列有一边处理完毕为止
while (l <= mid && r <= right)
{
//左边有序序列头部元素 <= 右边有序序列头部元素
if (suzu[l] <= suzu[r])
{
//将左边 小的 数据 ,填充到临时数组,并将左边头部和临时数组索引 ++;
tempArr[t] = suzu[l];
l++;
t++;
}
else
{
//反之,右边头部索引元素 放入到 临时数组,并将右边头部和临时数组索引 ++;
tempArr[t] = suzu[r];
r++;
t++;
}
}
//2.把有剩余数据的子序列 的剩余数据依次全部填充到 temp数组
//2.1 如果左边有序序列还有剩余元素, 将剩余元素全部填充到 temp
while (l <= mid)
{
tempArr[t] = suzu[l];
l++;
t++;
}
// 2.2 如果右边有序序列还有剩余元素, 将剩余元素全部填充到 temp
while (r <= right)
{
tempArr[t] = suzu[r];
r++;
t++;
}
// 3. 把临时数组 temp 元素 拷贝到 原始数组 suzu
//将此轮【最右索引以左】的无序元素 替换为 排好序的元素
t = 0;//临时数组元素的索引 每轮都是从0开始
int tempLeft = left;//left为此轮的最左元素,不一定是从0开始
while (tempLeft <= right)
{
suzu[tempLeft] = tempArr[t];
t++;
tempLeft++;
}
}
使用
int[] ints = new int[] {1,7,6,5,8 };
int[] temp = new int[ints.Length];
guibinpaixu(ints, 0, ints.Length - 1, temp);
foreach (var item in ints)
{
Debug.Log(item);
}
非比较型整数排序
计数排序:空间换时间;适用于排序一定范围内的整数
参考链接:C#计数排序算法 - 追逐时光者 - 博客园 (cnblogs.com)
/// <summary>
/// 计数排序
/// </summary>
/// <param name="suzu"></param>
private void jishupaixu(int[] suzu)
{
if (suzu.Length < 1) return;
int min = suzu[0];
int max = min;
//1.找出待排序元素的最大值、最小值
for (int i = 0; i < suzu.Length; i++)
{
if (suzu[i]<min)min = suzu[i];
if (suzu[i]>max)max = suzu[i];
}
//申请临时计数数组
int[] jishu = new int[max-min +1];
//2.统计元素出现的个数;计数数组【下标+最小值】为待排序元素,值为元素个数
//【suzu[i] - min】为待排序元素在计数数组中的位置
for (int i = 0; i < suzu.Length; i++)
{
jishu[suzu[i] - min] += 1;
}
存储排序结果
//int[] temp = new int[suzu.Length];
//3.填充排序容器
int tempIndex = 0;
for (int jishuindex = 0; jishuindex < jishu.Length; jishuindex++)//外层循环是遍历计数容器
{
while (jishu[jishuindex] > 0)//内层是计数数组个数大于0,依次插入排序容器
{
jishu[jishuindex] -= 1;
suzu[tempIndex] = jishuindex + min;
tempIndex++;
}
}
}
桶排序:
思路:为将待排序元素确定一定数量的(范围)桶,把待排序元素放入符合范围的桶中,对桶内元素进行排序,最后把桶内元素返回到原数组;
参考链接:1.9 桶排序 | 菜鸟教程 (runoob.com)
关键代码:
//确定桶的个数
int tongCount = (max - min)/arrLength + 1;
//放入哪个桶
int tongIndex = (suzu[i] - min)/arrLength;
/// <summary>
/// 桶排序
///
/// </summary>
/// <param name="suzu"></param>
public void tongpaixu(int[] suzu)
{
int arrLength = suzu.Length;
if(arrLength <= 1) return;
//确定桶的个数
int min = suzu[0];
int max = min;
for (int i = 0; i < suzu.Length; i++)
{
if (suzu[i]<min) min = suzu[i];
if (suzu[i]>max) max = suzu[i];
}
int tongCount = (max - min)/arrLength + 1;
List<List<int>> tongs = new List<List<int>>();
//实例化桶
for (int i = 0; i < tongCount; i++)
{
tongs.Add(new List<int>());
}
//桶中放入范围内的数据
for (int i = 0; i < arrLength; i++)
{
//放入哪个桶
int tongIndex = (suzu[i] - min)/arrLength;
//数据放入桶
tongs[tongIndex].Add(suzu[i]);
}
//对桶内元素排序;--少量元素使用 插入排序
for (int i = 0; i < tongs.Count; i++)
{
charupaixu(tongs[i]);
}
//桶中元素放回到数组中
int index = 0;
for (int i = 0; i < tongCount; i++)
{
for (int j = 0; j < tongs[i].Count; j++)
{
suzu[index] = tongs[i][j];
index++;
}
}
}
void charupaixu(List<int> suzu)
{
if (suzu.Count <= 1) return;
for (int i = 1; i < suzu.Count; i++)
{
int temp = suzu[i];
int j = i;
while (j > 0 && temp < suzu[j-1])
{
suzu[j] = suzu[j-1];
j--;
}
suzu[j]=temp;
}
}