默认为从小到大排序
1. 冒泡排序
每一轮,相邻两个比较,一轮结束,最大值排在末尾
int n = arr.Length;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
Swap(j, j + 1);
}
}
}
优化:
- 有序边界,确定最后有序边界 swapPos 边界后面是排序完成的
- 鸡尾酒排序,正向->反向->正向
int n = arr.Length;
int swapPos = 0;
int left = 0;
int right = n - 1;
while (left < right)
{
for (int i = left; i < right; i++)
{
if (arr[i] > arr[i + 1])
{
swapPos = i;
Swap(i, i + 1);
}
}
right = swapPos;
for (int i = right; i > left; i--)
{
if (arr[i] < arr[i - 1])
{
swapPos = i;
Swap(i, i - 1);
}
}
left = swapPos;
}
2. 选择排序
每一轮找出最小值坐标,和当前位置更换
int n = arr.Length;
int min;
for (int i = 0; i < n - 1; i++)
{
min = i;
for (int j = i + 1; j < n; j++)
{
if (arr[j] < arr[min])
min = j;
}
if (min != i)
Swap(min, i);
}
优化
- 一次遍历找出一个最大值和一个最小值
int n = arr.Length;
int min,max;
for (int left = 0, right = n - 1; left < right; left++, right--)
{
min = left;
max = right;
for (int j = left; j <= right; j++)
{
if (arr[j] < arr[min])
min = j;
if (arr[j] > arr[max])
max = j;
}
Swap(min, left);
if (left == max)
{
max = min;
}
Swap(right,max);
}
3. 插入排序
每次循环向前遍历,将大于自己的向后移,最后插入进去
int n = arr.Length;
int temp;
for (int i = 1; i < n; i++)
{
temp = arr[i];
int j = i - 1;
for (; j >= 0 && arr[j] > temp; j--)
{
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
4. 希尔排序
一种插入排序,比较相距一定间隔的元素,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止
int n = arr.Length;
int temp, j;
for (int h = n / 2; h > 0; h /= 2)
{
for (int i = h; i < n; i++)
{
for (j = i - h, temp = arr[i]; j >= 0 && arr[j] > temp; j -= h)
{
arr[j + h] = arr[j];
}
arr[j + h] = temp;
}
}
5. 归并排序
分治思想,将已有序的子序列合并,得到完全有序的序列。
递归法
private int[] temp;
private void MergeSort()
{
temp = new int[arr.Length];
Merge(0, arr.Length - 1);
}
private void Merge(int l, int r)
{
if (l >= r)
return;
int mid = (r + l) / 2;
Merge(l, mid);
Merge(mid + 1, r);
Merge(l, mid, r);
}
private void Merge(int l, int mid, int r)
{
int i = l;
int j = mid + 1;
int index = l;
while (i <= mid && j <= r)
{
if (arr[i] <= arr[j])
{
temp[index++] = arr[i];
i++;
}
else
{
temp[index++] = arr[j];
j++;
}
}
while (i <= mid)
temp[index++] = arr[i++];
while (j <= r)
temp[index++] = arr[j++];
for (int k = l; k <= r; k++)
arr[k] = temp[k];
}
非递归法
int n = arr.Length;
int[] temp = new int[n];
int gap = 1;
int lstart, lend, rstart, rend, index;
while (gap < n)
{
for (int i = 0; i < n; i += gap * 2)
{
lstart = index = i;
lend = Math.Min(i + gap - 1, n - 1);
rstart = lend + 1;
rend = Math.Min(i + 2 * gap - 1, n - 1);
if (lstart >= rend)
continue;
while (lstart <= lend && rstart <= rend)
{
if (arr[lstart] <= arr[rstart])
temp[index++] = arr[lstart++];
else
temp[index++] = arr[rstart++];
}
while (lstart <= lend)
temp[index++] = arr[lstart++];
while (rstart <= rend)
temp[index++] = arr[rstart++];
for (int j = i; j <= rend; j++)
arr[j] = temp[j];
}
gap *= 2;
}
6. 快速排序
- 选择基准:在待排序列中,按照某种方式挑出一个元素,作为“基准”(pivot);
- 分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准左边的元素都比该基准小,在基准右边的元素都比基准大;
- 递归地对两个序列进行快速排序,直到序列为空或者只有一个元素。
优化:
- 当数量较少时,使用插入排序,性能更好
- 选择基准
- 选择最左边的值
- 取最左边,最右边,正中间的值,找出中间值,当做基准移到最左边
- 随机取一个位置,当做基准移到最左边
private const int MAX_INSERT = 10;
private void Quick(int low, int high)
{
if (low >= high)
return;
if (high - low <= MAX_INSERT)
{
Insert(low, high);
return;
}
int point = SelectPoint(low, high);
int l = low + 1, r = high;
while (l <= r)
{
while (l <= r && arr[l] < point) l++;
while (l <= r && arr[r] >= point) r--;
if (l >= r) break;
Swap(l++, r--);
}
Swap(low, r);
Quick(low, r - 1);
Quick(r + 1, high);
}
private void Insert(int low, int high)
{
int temp;
int j;
for (int i = low + 1; i <= high; i++)
{
j = i - 1;
temp = arr[i];
for (; j >= low && arr[j] > temp; j--)
{
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
}
private int SelectPoint(int low,int high)
{
/// 取最左边的值
return arr[low];
/// 随机取基准
int random = new Random().Next(low, high + 1);
Swap(low,random);
return arr[low];
/// 取中间值
int mid = (high - low) / 2 + low;
if (arr[mid] > arr[high])
Swap(mid, high);
if (arr[low] > arr[high])
Swap(low, high);
if (arr[mid] > arr[low])
Swap(low, mid);
return arr[low];
}
7. 堆排序
- 构建最大堆
- 将最大值和末尾替换,然后将剩余的调整为最大堆
- 重复第2步,直到只剩1个数,排序成功
protected override void Sort()
{
int n = arr.Length;
// 构建最大堆
int i = n / 2 - 1;
for (; i >= 0; i--)
DownAdjust(i, n);
i = n - 1;
for (; i >= 1; i--)
{
Swap(0, i);
DownAdjust(0, i);
}
}
private void DownAdjust(int parent, int n)
{
int temp = arr[parent];
int child = 2 * parent + 1;
while (child < n)
{
// 判断左右子节点 取节点值最大的节点
if (child + 1 < n && arr[child] < arr[child + 1])
child++;
if (arr[child] <= temp)
break;
arr[parent] = arr[child];
parent = child;
child = 2 * parent + 1;
}
arr[parent] = temp;
}
8. 基数排序
按照个十百千位排序
int n = arr.Length;
int max = arr[0];
for (int i = 1; i < n; i++)
{
if (arr[i] > max)
max = arr[i];
}
List<int>[] buckets = new List<int>[10];
for (int i = 0; i < 10; i++)
buckets[i] = new List<int>();
int num = 1;
// 最大数位数
while (max / 10 > 0)
{
max /= 10;
num++;
}
int bit = 1;
while (num > 0)
{
for (int i = 0; i < n; i++)
buckets[arr[i] / bit % 10].Add(arr[i]);
int count;
int k = 0;
for (int i = 0; i < 10; i++)
{
count = buckets[i].Count;
for (int j = 0; j < count; j++)
arr[k++] = buckets[i][j];
buckets[i].Clear();
}
num--;
bit *= 10;
}
9. 计数排序
计数排序是一种非比较排序,其核心是将序列中的元素作为键存储在额外的数组空间中,而该元素的个数作为值存储在数组空间中,通过遍历该数组排序
- 找出最大最小两个值
- 构建 max-min+1 数组 temp
- 遍历数组 arr,存入 temp 数组 temp[arr[i]-min]++
int n = arr.Length;
int min = arr[0];
int max = min;
for (int i = 1; i < n; i++)
{
if (min > arr[i])
min = arr[i];
else if (max < arr[i])
max = arr[i];
}
if (min == max)
return;
int d = max - min + 1;
int[] temp = new int[d];
for (int i = 0; i < n; i++)
temp[arr[i] - min]++;
int j;
int k = 0;
for (int i = 0; i < d; i++)
{
j = temp[i];
while (j > 0)
{
arr[k++] = min + i;
j--;
}
}
辅助函数
protected void Swap(int i, int j)
{
Swap(arr, i, j);
}
protected void Swap(int[] arr, int i, int j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public void PrintArr()
{
string str = "";
for (int i = 0; i < arr.Length; i++)
{
str += arr[i];
if (i != arr.Length - 1)
str += ',';
}
Console.WriteLine(str);
}