常见的排序算法有以下7种:
- 冒泡排序
- 选择排序
- 插入排序
- 堆排序
- 希尔排序
- 归并排序
- 快速排序
我们通常说的排序算法往往指的是内部排序算法,即数据记录在内存中进行排序
- 排序算法大体可分为两种:
1.一种是比较排序,时间复杂度为O(nlogn)~O(n^2),主要有冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等
2.另一种是非比较排序,时间复杂度可以达到O(n)
除了排序算法的时间复杂度是我们比较关心的,还有一个就是排序算法的稳定性。
排序算法稳定性的简单形式化定义为:如果Ai = Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称这种排序算法是稳定的。
接下来先介绍4种排序算法:
1.冒泡排序(这里可以从前向后冒泡,也可以从后向前冒泡)
(1)比较两个相邻元素,如果前一个比后一个大,就将两个元素进行交换
(2)依次向后进行比较操作,一趟冒泡下来,最后一个元素为当前这组数中的最大值
(3)继续再进行如上操作,进行第二次冒泡,但是最后一个元素不用进行比较,因为已经有序,以此类推第三次冒泡时,最后两个元素不需要比较
(4)重复上述操作,进行 元素个数-1 次冒泡,排序完成
如图是对一组数进行一趟冒泡排序的结果,按照此方法继续进行冒泡,就可以得到排序结果
下面是代码实现:
void BubbleSort(int array[],size_t size)
{
if(size <= 1)
{
return;
}
//[0,i)表示有序区间
//[i,size)表示待排序区间
size_t i = 0;
for(;i < size ;++i)
{
//这里采取的是从后向前冒泡
size_t j = size -1;
for(;j > i;--j)
{
if(array[j] < array[j-1] )
{
Swap(&array[j],&array[j-1]);
}
}
}
return;
}
2.选择排序
(1)第一次排序,第一个元素和后面的元素依次比较大小,如果第一个元素大的话就交换两个元素的位置,保证一次排序后第一个元素为这组数的最小值
(2)第二次排序,第二个元素和后面的元素依次比较大小,如果第二个元素大的话就交换两个元素的位置,保证第二次排序后第二个元素为剩下数中的最小值
(3)重复上述操作,就可以将该组数进行排序
如图是进行一次选择排序的结果,按照此方法继续进行选择排序就可以得到排序结果
下面是代码实现:
void SelectSort(int array[],size_t size)
{
if(size <= 1)
{
return;
}
//[0,i)表示有序区间
//[i,size)表示待排序区间
size_t i = 0;
for(; i < size ;++i)
{
size_t j = i + 1;
for(; j < size;++j)
{
if(array[i] > array[j])
{
Swap(&array[i],&array[j]);
}
}
}
return;
}
3.插入排序
(1)从第一个元素,该元素可以被认为已经有序
(2)取出下一个元素,先保存起来,在已经排序的元素序列中从后向前比较
(3)如果当前元素大于要插入的元素,就把当前元素向后移
(4)重复步骤3,直到找到已排序的元素小于或等于新元素的位置
(5)将新元素插入到该位置
(6)重复上述操作,即可完成排序
如图是进行插入排序的过程
下面是代码实现:
void InsertSort(int array[],size_t size)
{
if(size <= 1)
{
return;
}
//[0,i)表示有序区间
//[i,size)表示待排序区间
//插入排序是把前面的有序区间当做一个线性表
//然后把 i_value 的值插入到线性表中合适的位置上
size_t i = 1;
for(; i < size;++i)
{
//此时存起来的意义是为了后面的搬运
//一旦array[i]元素被单独保存起来了
//array[i]数值就可以被修改了
int i_value = array[i];
//此时cur是辅助我们进行搬运的下标
//从后向前遍历,找到合适的位置放 i_value 的位置
size_t cur = i;
for(;cur > 0;--cur)
{
//此处我们的初始情况就是拿线性表的最后一个元素和 i_value 比较
//因此 array[cur-1]这里的 cur 取决于 cur 的初始位置
if(array[cur-1] > i_value)
{
//进行搬运
array[cur] = array[cur-1];
}
else
{
//说明已经找到了合适的位置
break;
}
}
//然后把i位置的元素插入到线性表的合适的位置上
array[cur] = i_value;
}
return;
}
4.堆排序
(1)基于数组建立一个堆(如果是升序就建立大堆)
(2)循环的删除堆顶元素,将所有的元素都删除完毕,排序完成
(3)每次删除堆顶元素后,都对堆进行调整,这里有两种方法
方法一:把新元素放到数组的末尾,进行上浮式调整(从前往后遍历)
方法二:采用下沉式的调整(从后往前遍历)
起始位置就是堆的从后往前遍历的第一个非叶子节点
如上给定一个数组建立一个堆已经完成,剩下的就是对堆进行删除堆顶元素,然后下沉式调整
下面是代码实现:
void AdjustDown(int array[],size_t size,size_t index)
{
size_t parent = index;
size_t child = 2 * index + 1 ;
while(child < size)
{
if(child + 1 < size && array[child] < array[child+1])
{
child = child + 1;
}
if(array[parent] < array[child])
{
Swap(&array[parent],&array[child]);
}
parent = child;
child = 2 * parent + 1;
}
return;
}
void HeapCreate(int array[],size_t size)
{
if(size <= 1)
{
return;
}
size_t i = (size -1 -1) / 2;
for(;i > 0;--i)
{
AdjustDown(array,size,i);
}
AdjustDown(array,size,0);
return;
}
void HeapPop(int array[],size_t heap_size)
{
if(heap_size <= 1)
{
return;
}
Swap(&array[0],&array[heap_size-1]);
AdjustDown(array,heap_size - 1,0);
}
void HeapSort(int array[],size_t size)
{
if(size <= 1)
{
return;
}
//1.基于数组建立一个堆(如果是升序,就建立大堆)
HeapCreate(array,size);
//2.循环的删除堆顶元素,将所有的元素都删除完毕,排序完成
size_t i = 0;
for(;i < size;++i)
{
//第二个参数表示数组中哪部分区间是符合堆的规则
//第一个删除之前,[0,size)都是堆
//第二次删除之前,[0,size-1)都是堆
//第三次删除之前,[0,size-2)都是堆
HeapPop(array,size-i);
}
return;
}