基础概念
1: 内部排序:排序算法需要一次性将所有的数据加载到内存中才可以进行排序;
外部排序:排序算法不需要一次性将所有的数据加载到内存中才可以进行排序;(堆排序 归并排序·)
稳定排序:待排序记录中,相同的元素在排序后相对位置保持不变的排序。
插入排序
上代码
void Insert_Sort(int *arr, size_t sz)
{
for(size_t i = 1; i < sz; ++i)
{
int key = arr[i]; // 当前要插入元素
int end = i - 1;
while(key < arr[end] && end >= 0)
{
arr[end+1] = arr[end];
end--;
}
arr[end + 1] = key;
}
}
需要注意下标 end = i - 1;当前要插入元素需要用key进行保存,避免下标出错,i从1开始,因为arr[0]不需要进行排序。
时间复杂度 : 如果数据的序列与有待排序的序列接近相同,时间复杂度O(n),最差场景这个序列是个逆序,时间复杂度是个O(n^2)。
应用场景:数据接近有序情况下,数据量比较少。
如果数据大量并且并未接近有序,插入排序就需要进行改进为希尔排序
希尔排序
将数据逐步变得有序
在插排基础上对于大量数据进行分组,设置gap值,将数据逐步变得有序。
void Shell_Sort(int *arr, size_t sz)
{
int gap = 3;
while(gap > 0)
{
for(size_t i = gap; i < sz; ++i)
{
int key = arr[i];
int end = i - gap;
while(end >= 0 && key <= arr[end])
{
arr[end+gap] = arr[end];
end -= gap;
}
arr[end + gap] = key;
}
gap--;
}
}
空间复杂度:O(1) 时间复杂度和gap取值有关
希尔排序是不稳定排序(因为是隔着区间的插入排序)
适用于数据量比较大并且比较凌乱
选择排序
选择排序原理:从所给区间选择出最大或者最小的数据进行放置,每次选择之后都要将区间长度缩短。
void Select_Sort(int* arr,size_t len)
{
for(size_t i = 0;i < len - 1;++i)
{
int min = i;
for(size_t j = i; j < len;++j)
{
if(arr[j] < arr[min])
{
min = j;
}
}
Swap(&arr[min],&arr[i]);
}
}
void Select_Sort(int* arr,size_t len)
{
for(int i = len - 1; i > 0;--i)
{
int max = arr[i];
for(int j = i; j >= 0;--j)
{
if(arr[max] < arr[j])
{
max = j;
}
}
Swap(&arr[max],&arr[i]);
}
}
选择排序有什么缺点呢?
在每次比较的时候并不会记忆比较,造成大量比较重复。
优化可以选择堆排序。
堆排序(也是一种选择排序)
堆排序的关键点
1:找到第一个非叶子节点(也就是最后一个叶子节点的parent)
(size - 2 ) / 2
2: 如何根据parent推算出孩子节点?
lchild = parent * 2 + 1
rchild = parent * 2 + 2 = lchild + 1;
3:如何调整堆?
我认为这个是堆排中最关键的问题,我们在从根节点进行堆调整的时候,向下调整会造成该节点孩子节点的不满足堆的性质,如何使用代码来解决?
if(array[child] > array[parent])
{
Swap(&array[child],&array[parent]);
parent = child;
child = parent * 2 + 1;
}
4:如何保证堆最后的有序性?
利用堆的删除,将堆顶元素和最后一个叶子节点交换,缩小区间,这个思路和选择排序一样,所以说堆排序是选择排序的优化。
来看完整代码:
void HeapAdjust(int array[], int size, int parent)//向下调整
{
int child = parent * 2 + 1;
while(child < size)
{
//找较大孩子 先保证parent 的右孩子存在
if(child+1 < size && array[child + 1] > array[child])
{
child += 1;
}
if(array[child] > array[parent])
{
Swap(&array[child],&array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
return;
}
}
}
void HeapSort(int array[], int size)
{
int end = size - 1;
for(int root = (size-1)/2;root >= 0;root--)
{
HeapAdjust(array,size,root);//向下调整
}
//利用堆删除的思想进行排序
while(end)
{
Swap(&array[0] , &array[end]);
HeapAdjust(array,end,0);
end--;
}
}
堆排序空间复杂度 O(1) 时间复杂度 O(Nlog2N) 稳定性: 不稳定
冒泡排序
关键:相邻元素之间的比较
void Bubble_Sort(int *arr,size_t sz)
{
for(size_t i = 0;i < sz - 1;++i)
{
for(size_t j = 0; j < sz - i - 1;++j)
{
if(arr[j] > arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
需要注意:防止 j下标越界
j < sz - i - 1
必须要减一,因为比较时arr[j] 和 arr[j+1]进行的比较,i = 0 时会造成 j 数组越界。
时间复杂度: O(N^2)空间复杂度:O(1) 稳定性 : 稳定
快速排序
关键:
获取基准值函数:
保证返回的下标左边比基准值小,右边比基准值大
在获取基准值的同时进行交换。
int Pation(int* arr, int left, int right)
{
int begin = left;
int end = right-1;
int key = arr[end];
while(begin < end)
{
while(begin < end && arr[begin] <= key)
{
begin++;
}
while(begin < end && arr[end] >= key)
{
end--;
}
if(begin < end)
{
Swap(&arr[begin],&arr[end]);
}
}
if(begin != right-1)//begin 不是最后一个元素
Swap(&arr[begin],&arr[right -1]);
return begin;
}
void Quick_Sort(int *arr ,size_t left ,size_t right)
{
if(right - left > 1)
{
int div = Pation(arr,left,right); // 基准值
Quick_Sort(arr,left,div);
Quick_Sort(arr,div+1,right);
}
}