重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。重复地进行直到没有再需要交换的数据,也就是说该数列已经排序完成 ,这样,最小的元素会慢慢的浮到最前面
时间复杂度:O(n²)
空间复杂度:O(1)
代码:
void MaoPao(int[] arr)
{
for(int i = 0; i < arr.Length; i++)
{
for(int j = 0; j < arr.Length - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
}
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
时间复杂度:O(n²)
空间复杂度:O(1)
代码:
void XuanZe(int[] arr)
{
for(int i = 0; i < arr.Length; i++)
{
int index = i;
for(int j = i + 1; j < arr.Length; j++)
{
if (arr[j] < arr[index])
{
index = j;
}
}
int tem = arr[index];
arr[index] = arr[i];
arr[i] = tem;
}
}
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
时间复杂度:O(n²)
空间复杂度:O(1)
代码:
void ChaRu(int[] arr)
{
for(int i = 1; i < arr.Length; i++)
{
int preindex = i;
int current = arr[i];
while(preindex > 0 && current < arr[preindex-1])
{
arr[preindex] = arr[preindex - 1];
preindex--;
}
arr[preindex] = current;
}
}
希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,
时间复杂度:< O(n²)
空间复杂度:O(1)
希尔排序的代码容易记的方法:
先写一遍正常的插入排序,外层加一个关于gap的for循环,吧里面的-1改成-gap
void XiEr(int[] arr)
{
for(int gap = arr.Length / 2; gap > 0; gap /= 2)
{
for (int i = gap; i < arr.Length; i++)
{
int preindex = i;
int current = arr[i];
while (preindex - gap >= 0 && current < arr[preindex - gap])
{
arr[preindex] = arr[preindex - gap];
preindex-= gap;
}
arr[preindex] = current;
}
}
}
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
时间复杂度: O( n * log(n) )
空间复杂度:O(n)
代码:
void GuiBing(int[] arr)
{
//先建立好一个辅助数组
int[] _b = new int[arr.Length];
MergeSort(arr, 0, arr.Length-1,_b);
}
void MergeSort(int[] arr,int left,int right,int[] _b)
{
if (left >= right)
return;
int mid = (left + right) / 2;
MergeSort(arr,left, mid,_b);
MergeSort(arr,mid + 1, right,_b);
int i = left, j = mid + 1;
int len = left;
//合并
while (i<=mid && j<=right)
{
if (arr[i] <= arr[j])
{
_b[len++] = arr[i++];
}
else
{
_b[len++] = arr[j++];
}
}
//将左右边剩余元素填充
while (i <= mid)
{
_b[len++] = arr[i++];
}
while (j <= right)
{
_b[len++] = arr[j++];
}
//拷贝
for(int k = left; k <= right; k++)
{
arr[k] = _b[k];
}
}
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
时间复杂度:
最佳情况: O(n*log n)
最差情况:T(n) = O(n²)
空间复杂度:
O(1)
代码:
void KuaiSu(int[] arr)
{
QuickSort(arr, 0, arr.Length - 1);
}
void Swap(int[] arr,int i,int j)
{
int tem = arr[i];
}
void QuickSort(int[] arr,int left,int right)
{
if (left >= right)
return;
int key = arr[left];
int i = left, j = right;
while (i < j)
{
//从右向左找小于key的
while (i < j && arr[j] >= key) j--;
arr[i] = arr[j];
//从左向右找大于key的
while (i < j && arr[i] < key) i++;
arr[j] = arr[i];
}
arr[i] = key;
QuickSort(arr, left, i);
QuickSort(arr, i + 1, right);
}
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
当输入的元素是n个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。
代码如下:
void JiShu(int[] arr)
{
//前提保证最大值和最小值的差不能太大
int max=arr[0], min=arr[0];
//统计最值
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
int len = max - min + 1;
int[] _b = new int[len];
for(int i = 0; i < len; i++)
{
_b[i] = 0;
}
for(int i = 0; i < arr.Length; i++)
{
_b[arr[i] - min]++;
}
int j = 0;
for(int i = 0; i < len; i++)
{
while (_b[i] != 0)
{
arr[j++] = i;
_b[i]--;
}
}
}
桶排序是将待排序集合中处于同一个值域的元素存入同一个桶中,也就是根据元素值特性将集合拆分为多个区域,则拆分后形成的多个桶,从值域上看是处于有序状态的。对每个桶中元素进行排序,则所有桶中元素构成的集合是已排序的。
相当于进化版的计数排序,只不过讲某个区间的值放在一个桶里,而计数排序是将一个相同的值放进一个桶里
void Tong(int[] arr)
{
//找最值
int max = arr[0], min = arr[0];
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
//划分一个区间的大小为5
int gap = 5;
List<int>[] tongs = new List<int>[(max-min+1) / gap + 1];
for(int i = 0; i < tongs.Length; i++)
{
tongs[i] = new List<int>();
}
//f(x)= x/gap - min/gap
//放到各自的桶里面
for(int i = 0; i < arr.Length; i++)
{
int index = arr[i] / gap - min / gap;
tongs[index].Add(arr[i]);
}
//对桶排序 对arr赋值
int len = 0;
foreach(List<int> t in tongs)
{
if (t.Count != 0)
{
t.Sort();
foreach(int x in t)
{
arr[len++] = x;
}
}
}
}
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:
所谓基数排序,思路也很简单,先按照个位排序,在按照十位排序,在按照百位排序。。。。。就行了
void RadixSort(int[] arr)
{
//获取数组最大值
int max = arr[0];
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] > max)
max = arr[i];
}
int exp = 1;
for (; max / exp > 0; exp *= 10)
{
CountSort(arr, exp);
}
}
void CountSort(int[] arr,int exp)
{
int[] _output = new int[arr.Length];//临时数组
int[] buckets = {
0,0,0,0,0,0,0,0,0,0};
for(int i = 0; i < arr.Length; i++)
{
buckets[(arr[i] / exp) % 10]++;
}
for(int i = 1; i < 10; i++)
{
buckets[i] += buckets[i - 1];
}
for(int i = 0; i < arr.Length; i++)
{
_output[buckets[(arr[i] / exp) % 10] - 1] = arr[i];
buckets[(arr[i] / exp) % 10]--;
}
for(int i = 0; i < arr.Length; i++)
{
arr[i] = _output[i];
}
}
堆排序这里写过思路:添加链接描述
至于怎么构建堆以及堆的各种操作,以后在专门写一篇
小的放左边大的放右边 ,但是当序列有序的时候会变成链表,比较耗时,解决办法是平衡二叉树或者红黑树