class Program
{
static void Main(string[] args)
{
int[] arr = new int[100];
Random r = new Random();
for (int i = 0; i < arr.Length; i++)
{
arr[i] = r.Next(1, 1000);
}
radixSort(arr,0,arr.Length-1,3);
arr.ToList().ForEach(i => Console.Write(" " + i));
Console.ReadKey();
}
public static void BobSort(int[] input)
{
//冒泡排序i循环每次使一个最大元素在末尾
//j循环应该排序在末尾的若干已排序大值 循环次数=总排序次数-已排序次数
int temp = 0;
for (int i = 0; i < input.Length - 1; i++)
{
for (int j = 0; j < input.Length - 1 - i; j++)
{
if (input[j] > input[j + 1])
{
temp = input[j];
input[j] = input[j + 1];
input[j + 1] = temp;
}
}
}
}
public static void InsertSort(int[] arr)
{
//插入排序 类似于理牌
//第一位默认有序,从第二位开始,抽出,插入,元素往后移动
//当抽出元素大于当前比对元素,不作操作,当小于当前比对元素,将后置元素往后依此挪一位,留出空间给要插入的元素
int curval;
int curindex;
for (int i = 1; i < arr.Length; i++)
{
curindex = i;
curval = arr[i];
//直至索引到1时,0时不用再比较,为最小,直接赋值
//每往后比较一位,将元素往后挪一位
while (curindex >= 1 && curval < arr[curindex - 1])
{
arr[curindex] = arr[curindex - 1];
curindex--;
}
arr[curindex] = curval;
}
}
public static void ShellSort(int[] arr)
{
//希尔排序为直接插入排序的改进版,意在交换远端的元素,避免使得某些后置位小元素从当前位置推至第一个的情况
//每次取一个步长,将步长分组内的数据插入排序,再逐步减少步长,达到使整个数组大致升序的样子
//例如100长度的数组 第一次步长为50 ,0 和49 为一组,1和50为一组....第二次步长为25, 0 24 49 74 为一组
//最后一次步长为1时,直接插入排序效率提高,不会出现当前元素推至第一个的情况
for (int gap = arr.Length / 2; gap > 0; gap /= 2)
{
//每次组的数量为步长的长度
for (int i = 0; i < gap; i++)
{
//开始组内插入排序,组内每个数据的间隔为gap,插入排序第一个默认有序,所以j=i+gap(第二个数)
for (int j = i + gap; j < arr.Length; j += gap)
{
//原理参照插入排序
int curindex = j;
int curval = arr[j];
while (curindex >= i + gap && curval < arr[curindex - gap])
{
arr[curindex] = arr[curindex - gap];
curindex -= gap;
}
arr[curindex] = curval;
}
}
}
}
public static int Piovt(int[] arr, int left, int right)
{
//快速排序 1 以第一个元素为标准元素,在数组右侧找到比之小的移动到标准元素坑位,再从左侧找到比之大的,到右侧坑位,坑位在左右交互
// 随着左右指针的距离越来越近直至重合,左侧都比标准元素小,右侧比之大
int l = left, r = right, piovtval = arr[l]; //默认第一个数为标准元素
while (l < r)
{
while (l < r && arr[r] > piovtval) //从右侧找到了大于标准数的,索引下标为r
r--;
if (l < r) //指针如果重合说明两侧已经达到要求,不用再为下轮作准备
{
arr[l] = arr[r]; //把此数扔到标准数坑位,注意此时不是把标准数和找到的数交换位置,只需要把找到的数扔到坑位即可
l++; //下次将从左侧找比标准数大的,此时的arr[l]是上面找到的比标准数小的数,所以l++跳过
}
while (l < r && arr[l] < piovtval) //从左侧找到了大于标准数的,索引下标为r
l++;
if (l < r) ////指针如果重合说明两侧已经达到要求,不用再为下轮作准备
{
arr[r] = arr[l]; //把此数扔到标准数坑位
r--;
}
}
arr[l] = piovtval; //最后指针重合时把标准数放入坑位
return l;
}
public static void QuickSort(int[] arr, int l, int r)
{
int mid;
if (l < r)
{
mid = Piovt(arr, l, r);
QuickSort(arr, l, mid - 1);
QuickSort(arr, mid + 1, r);
}
}
public static void MergeMethod(int[] arr, int low, int mid, int high)
{
// 3根指针,i 第一个数据集合的起始位, mid 结束位
// j 第二个数据集合的起始位, high 结束位
// k 合并集合的插入位置
int i = low;
int j = mid + 1;
int k = 0;
int[] temp = new int[high - low + 1]; //合并集合的存放容器
//指针直到某个集合超限,因为双个都为有序集合,arr[i]<arr[j]的话,i必为所有比较元素中的最小元素,反之亦然
while (i <= mid && j <= high)
{
if (arr[i] < arr[j])
{
temp[k++] = arr[i++];
}
else
{
temp[k++] = arr[j++];
}
}
//上个步骤结束时如果某个集合中还有数,必定为更大值,依此放入合并集合中即可
while (i <= mid)
{
temp[k++] = arr[i++];
}
while (j <= high)
{
temp[k++] = arr[j++];
}
//将合并后的集合更新到原arr中
for (i = low, k = 0; i <= high; i++, k++)
{
arr[i] = temp[k];
}
}
public static void MergeSort(int[] arr, int low, int high, int depth)
{
//新加一个参数,depth用于看递归深度
int mid;
//递归的退出条件
if (low < high)
{
//每次递归要做的事是取中间值,明确下个递归深度的参数条件,本次递归调用方法合并两个数据集合
mid = (low + high) / 2;
Console.WriteLine("左:" + low + " 中:" + mid + " 右:" + high + " 当前深度:" + depth);
MergeSort(arr, low, mid, depth + 1);
MergeSort(arr, mid + 1, high, depth + 1);
MergeMethod(arr, low, mid, high);
}
}
public static void SelectSort(int[] arr)
{
//选择排序 每次找到一个最小的数 按次序放在相应位置
for (int i = 0; i < arr.Length; i++)
{
//标量 的值和小标
int temp = arr[i];
int index = i;
//在i+1到结尾的元素中找比标量小的,找到了设置新的标量,记录下标
//循环结束即找到本次循环的最小值和下标
for (int j = i + 1; j < arr.Length; j++)
{
if (arr[j] < temp)
{
temp = arr[j];
index = j;
}
}
//将本次找到的元素和原始标量交换位置
temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
}
public static void HeapAdjust(int[] arr, int parentIndex, int length)
{
//堆排序 利用数据结构转换,转为平衡二叉树
//平衡二叉树的特点
//父节点n 左节点2n+1 右节点2n+2 索引最大的第一个非叶子节点arr.length/2-1
int temp = arr[parentIndex]; //父节点
int child = parentIndex * 2 + 1; //子节点
while (child < length) //至末尾节点
{
//如果右孩子节点大于左孩子节点则取右节点
if (child + 1 < length && arr[child + 1] > arr[child])
{
child = child + 1;
}
//如果父节点已经比双子节点大那么本次节点调整结束
//这是建立在从第一个非叶子节点开始循环调用堆调整方法,所以底部的节点都符合父节点都比子节点大的规则
if (arr[parentIndex] > arr[child])
{
break;
}
else
{
//子节点和父节点交换
arr[parentIndex] = arr[child];
arr[child] = temp;
}
//交换后 原父节点仍可能比下级节点小,所以重新赋值向下测探 直到while结束 本次堆单元调整结束
parentIndex = child;
child = parentIndex * 2 + 1;
temp = arr[parentIndex];
}
}
static void CreateHeapWithDesc(int[] arr)
{
//建立大根堆,从索引下标最大的非叶子节点开始,至头节点
for (int i = arr.Length / 2 - 1; i >= 0; i--)
{
HeapAdjust(arr, i, arr.Length);
}
}
public static void HeapSort(int[] arr)
{
//将数组转换成大根堆,如果要数组降序转成小根堆
CreateHeapWithDesc(arr);
int temp;
//每次头节点最大值和最小值交换,重新整理大根堆,参与整理的长度排除掉在尾部的最大值
//最后一次无需整理
for (int i = arr.Length - 1; i > 0; i--)
{
temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
HeapAdjust(arr, 0, i); //无需i-1,循环头已减
}
}
public static void radixSort(int[] list, int begin, int end, int digit)
{
int radix = 10;
int i = 0, j = 0;
int[] count = new int[radix]; //基数表 0,1,2..9
int[] bucket = new int[end - begin + 1];
//循环位数次数
for (int d = 1; d <= digit; d++)
{
//清空
for (i = 0; i < radix; i++)
{
count[i] = 0;
}
//得到0-9基数表相应有几个元素
for (i = begin; i <= end; i++)
{
j = getDigit(list[i], d);
count[j]++;
}
//累加计数,得到某个基数槽位内的元素和桶的分配关系
//例如 基数 原count表 后count表
// 0 1 1
// 1 0 1
// 2 3 4
// 3 0 4
//基数2有3个计数, 加之前总共有4个计数,对应是1-4号位桶,2-4 3个桶属于
//后count表的数字代表 基数N 应该分配的最后一个桶,分配了一个桶位同步
//数组作桶,桶索引号-1
for (i = 1; i < radix; i++)
{
count[i] = count[i] + count[i - 1];
}
for (i = end; i >= begin; i--)
{
j = getDigit(list[i], d);
bucket[count[j] - 1] = list[i];
count[j]--;
}
for (i = 0, j = 0; i < bucket.Length; i++, j++)
{
list[i] = bucket[j];
}
}
}
public static int getDigit(int x, int d)
{
int[] a = { 1, 1, 10, 100 };
return ((x / (int)Math.Pow(10, d - 1)) % 10);
}
}