排序:
排序算法的稳定性
内部排序算法和外部排序算法
排序算法的实现原理,代码实现,稳定性,时间复杂度,空间复杂度,应用场景
插入排序:
时间复杂度:最优O(N)最差O(N^2);
插入排序有最优的场景:如果数据的序列与用户所需序列是接近相同,比如:用户要升序,序列就是一个升序序列—>O(N);
最差场景:用户需要升序,数据恰好是降序—>O(N^2);
稳定性:稳定;
代码实现:
void InsertSort(int* array, int size,Cmp cmp)
{
int i = 1;
for (i = 1; i < size; i++)
{
int end = i - 1;
int key = array[i];
while (end >= 0 && cmp(key,array[end]))
{
array[end + 1] = array[end];
end--;
}
array[end + 1] = key;
}
}
应用场景:数据有序或者接近有序(数据与用户所需顺序一致)&数据量比较少
问题:----------------------------------------------------------------
数据凌乱(随机)而且数据量较大—要求:利用插入排序的思想来进行排序;
**解决方法:**将用户所给场景向插入排序的场景进行转变;
- 数据凌乱—>想办法让数据逐渐接近有序;(比较困难)
- 数据量大—>想办法让数据量变小(简单)
所以分组;
分组方式:让数据按照一定间隔分组,间隔(gap)渐渐减少,直到gap = 1;该种分组方式是希尔想出来的—>希尔排序(shellsort);
希尔排序:
代码实现
void ShellSort(int* array,int size)
{
int gap = size;
while(gap > 1 )
{
//gap = gap/2;希尔给的
//gap每次给个素数(质数)(7,5,3,1)
gap = gap/3+1;//kunth大量数据证明效率高
for(int i = gap;i<size;i++)
{
int key = array[i];
int end = i - gap;
while(end >= 0 && key < array[end])
{
array[end+gap] = array[end];
end -= gap;
}
array[end+gap] = key;
}
//gap--;
}
}
gap 不同人有不同看法;
看排序算法是否稳定:排序算法原理在插入时候是否隔着区间(隔着元素)来进行插入;
排序算法原理在交换时候是否隔着区间(隔着元素)来进行插入;–>是则不稳定
空间复杂度:O(1);
空间复杂度:kunth方法:O(N^1.25 )~ O(1.6N^1.25 )
稳定性:不稳定;
选择排序
代码实现
void Swap(int* left, int* right)
{
int tmp = *left;
*left = *right;
*right = tmp;
}
//选择排序
void SelectSort(int* array, int size, Cmp cmp)
{
for (int i = 0; i < size - 1; i++)
{
int minPos = 0;
for (int j = 1; j < size - i; j++)
{
if (cmp(array[j], array[minPos]))
minPos = j;
}
if (minPos != size - i - 1)
{
Swap(&array[minPos], &array[size - i - 1]);
}
}
}
时间复杂度:O(N^2);
空间复杂度:O(1);
稳定性不稳定;
选择排序的缺陷:整个序列前面元素重复比较;
思考:怎么才能够不做重复的事情?
堆排序:
1.建堆:建大堆还是小堆
//1.建堆—> 升序–>大堆,降序–>小堆
//向下调整—>从倒数第一个非叶子节(size-2)/2[(最后一个节点的下标-1)/2]点开始到根节点
2.堆排序
void AdjustDown(int* array,int size,int parent)
{
int child = 2*parent+1;
while(child<size)
{
if(child+1<size&&array[child+1]>array[child])
child++;
if(array[parent]<array[child])
{ Swap(&array[parent],&array[child]);
parent = child;
child = 2*parent+1;
}
else
break;
}
}
void HeapSort(int* array, int size)//堆排序
{
//1.建堆---> 升序-->大堆,降序-->小堆
//向下调整--->从倒数第一个非叶子节(size-2)/2[**(最后一个节点的下标-1)/2**]点开始到根节点
for (int root = (size - 1 - 1) / 2; root >= 0; root--)
{
AdjustDown(array, size, root);
}
//2.排序---->利用堆删除的思想
int end = size - 1;
while (end > 0)
{
Swap(&array[0], &array[end]);
AdjustDown(array, end, 0);
end--;
}
}
空间复杂度:O(1);
时间复杂度:O((3N/2)logN)–>O(NlogN)
向下调整–>O(logN);
**稳定性:**不稳定;
交换排序
冒泡排序:
for()—>外层循环:控制冒泡趟数
for()—>内层循环:控制冒泡方式
void BubbleSort(int* array, int size)//冒泡排序
{
//控制冒泡的趟数
for (int i = 0; i < size - 1; i++)//-1的目的是可以少冒一趟,因为最后一次冒泡区间只剩下一个元素;
{
//冒泡方式:将相邻元素比较-->不满足条件就交换
/*
//法一:
//j:后一个元素的下标;
for (int j = 1; j < size - i; j++)
{
if (array[j - 1] > array[j])
Swap(&array[j - 1], &array[j]);
}
*/
//法二:
//j:前一个元素的下标;
for(int j = 0;j<size-i-1;j++)
{
if(array[j] > array[j+1])
Swap(&array[j],&array[j+1]);
}
}
}
代码二
void BubbleSort2(int* array, int size)//冒泡排序//处理{0,1,2,3,5,4}这种
{
//控制冒泡的趟数
for (int i = 0; i < size - 1; i++)//-1的目的是可以少冒一趟,因为最后一次冒泡区间只剩下一个元素;
{
int flag = 1;
//冒泡方式:将相邻元素比较-->不满足条件就交换
for (int j = 1; j < size - i; j++)
{
if (array[j - 1] > array[j])
{
flag = 0;//如果flag被改为0则这趟冒泡时区间还无序;
Swap(&array[j - 1], &array[j]);
}
}
if (flag)
break;
}
}
时间复杂度:O(N^2);
空间复杂度:O(1);
稳定性:稳定;
快速排序:
数据分割:hore提出快排思想的大佬
取区间最左侧或者最右侧的数据为基准值;
(用begin标记数组最前面的元素,end标记最后面的元素,begin从前往后移动,end从后往前移动,如果begin和end没遇到或者没有错过,begin找到一个比基准值大的就停止,end找到一个比基准值小的就停止,交换begin和end所指元素最后交换相遇位置的和最右侧的元素 );
//分割代码
//hoare:提出快排思想的大佬提出的
int partion(int* array, int left, int right)
{
int begin = left;
int end = right - 1;
int key = array[end];//基准值
while (begin < end)
{
//让begin从前往后找,找>基准值的元素,找到就停止
while (begin<end && array[begin] <= key)
begin++;
//让end从后往前找,找<基准值的元素,找到就停止
while (begin<end && array[end] >= key)
end--;
if(begin<end)
Swap(&array[begin], &array[end]);
}
if(begin!=right-1)
Swap(&array[begin], &array[right - 1]);
return begin;
}
//排序代码
void QuickSort(int* array, int left, int right)//快速排序[left,right)
{
if (right - left > 1)//元素个数>1
{
//partion函数将区间[left,right)用最右侧基准值划分为前面元素<基准值
//后面元素大于基准值;
int div = partion(array, left, right);
//基准值的左侧[left,div)
QuickSort(array, left, div);
//基准值的右侧[div+1,right)
QuickSort(array, div + 1, right);
}
}