直接插入排序
思想:
直接插入单趟 思想:
end 是当前元素的下标
1.先保存end 的下一个元素 到 tmp
2. 判断 a[end] 是否 大于 tmp 若 a[end] > tmp 将 end 下标的元素 移动到 [end+1] 位置(前面已经 把[end+1]的元素保存到tmp 中 就不担心[end+1] 位置的元素被覆盖)
然后 [--end] [end的前一个元素] 称为新的 end 继续判断 数组的下标[0,n-1] 直到 end 小于 0 结束
若 end 不大于 tmp 就退出循环 将 tmp 放进 end的下一个位置
void InsertSort(int* a, int n)
{
for (int i = 0; i < n; i++)
{
int end = i;
int tmp = a[end + 1];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + 1] = a[end];
--end;
}
else
{
break;
}
}
a[end + 1] = tmp;
}
}
运行结果:
当 i < n ; 最后一个 end 的下是 n-1 tmp 的下标是 n (已当前为例)有效的数组下标 [0,n-1] tmp指向n 数组越界
当 i < n-1 ; 最后一个 end 的下是 n-2 tmp 的下标是 n-1 (已当前为例)有效的数组下标 [0,n-1] tmp指向n-1 完全ok
修改后的代码:
// 直接插入单趟 思想:
// end 是当前元素的下标
// 1.先保存end 的下一个元素 到 tmp
// 2. 判断 a[end] 是否 大于 tmp 若 a[end] > tmp 将 end 下标的元素 移动到 [end+1] 位置(前面已经 把[end+1]的元素保存到tmp 中 就不担心[end+1] 位置的元素被覆盖)
// 然后 [--end] [end的前一个元素] 称为新的 end 继续判断 数组的下标[0,n-1] 直到 end 小于 0 结束
// 若 end 不大于 tmp 就退出循环 将 tmp 放进 end的下一个位置
void InsertSort(int* a, int n)
{
//for (int i = 0; i < n; i++)
// 当 i < n ; 最后一个 end 的下是 n-1 tmp 的下标是 n (已当前为例)有效的数组下标 [0,n-1] tmp指向n 数组越界
// 当 i < n-1 ; 最后一个 end 的下是 n-2 tmp 的下标是 n-1 (已当前为例)有效的数组下标 [0,n-1] tmp指向n-1 完全ok
for (int i = 0; i < n-1; i++)
{
int end = i;
int tmp = a[end + 1];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + 1] = a[end];
--end;
}
else
{
break;
}
}
a[end + 1] = tmp;
}
}
运行结果:
直接插入排序的特性总结:
- 元素集合越接近有序,直接插入排序算法的时间效率越高
- 时间复杂度:O(N ^ 2) 但是再接近有序的情况 比 冒泡排序好
- 空间复杂度:O(1),它是一种稳定的排序算法
- 稳定性:稳定
希尔排序
思想:和插入排序很像
思路:1 先预排序 使数组接近有序
2.再直接插入
希尔排序单趟 思想:
end 是当前元素的下标
1.先保存end+gap 的元素 到 tmp
2. 判断 a[end] 是否 大于 tmp 若 a[end] > tmp 将 end 下标的元素 移动到 [end+gap] 位置(前面已经 把[end+gap]的元素保存到tmp 中 就不担心[end+gap] 位置的元素被覆盖)
然后 [end-= gap] [end的前gap 的元素] 称为新的 end 继续判断 end有效的下标[0,n-gap) 直到 end 小于 0 结束
若 end 不大于 tmp 就退出循环 将 tmp 放进 end+gap的位置
gap = 5 第一趟 为 (n/2) gap 一次跳过5个元素
gap = 2 第二趟 gap 一个跳过2 个元素
gap = 1 第三趟 相当于直接插入gap 越大 gap 跳的越快 大的数 更快到后面
gap 越小 gap 跳的越慢
代码实现:
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 2;
// i < n -gap 防止数组越界
for (int i = 0; i < n - gap; i++)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
希尔排序的特性总结:
希尔排序是对直接插入排序的优化。
时间复杂度:O(N^1.25)
空间复杂度:O(1)
稳定性:不稳定
堆排序
思路:(这里已升序为例)
思想 ; 找到最后一个非叶子节点 向下调整建堆 当前树 的左右子树 都是 大堆 继续调整 根节点 向下调整建堆 直到 整个树结构为大堆
用删除的思想来 实现排序
1.将堆顶部 的元素 和 最后一个元素交换
2.再把最后一个元素不看进堆里面的(是顶部的元素)
3.向下调整建堆
继续 进行 1 2 3 直到堆里面没有元素
//(这里已升序为例)
//找到最后一个非叶子节点 向下调整建堆 当前树 的左右子树 都是 大堆 继续调整 根节点 向下调整建堆 直到 整个树结构为大堆
void AdjustDown(int* a, int n , int parent)
{
//假设左孩子 是左右孩子 较大的
int child = parent * 2 + 1;
while (child < n)
{
if ((child + 1 < n) && a[child + 1] > a[child])//如果右孩子大于 左孩子 就把 右孩子赋值给 child
{
++child; //就把 右孩子赋值给 child
}
//孩子大于父亲就交换
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else //堆已成直接返回
{
break;
}
}
}
void HeapSort(int* a, int n)
{
// 找到最后一个非叶子节点
// (n-1)/2 找到它的父亲
for (int i = (n - 2 / 2); i >= 0; i--)
{
AdjustDown(a, n, i);
}
//用删除的思想来 实现排序
int end = n-1;
while (end >0)
{
Swap(&a[0], &a[end]);
end--; //再把最后一个元素不看进堆里面的(是顶部的元素)
AdjustDown(a, end, 0);
}
}
堆排序的特性总结:
- 堆排序使用堆来选数,效率就高了很多。
- 时间复杂度:O(N * logN)
- 空间复杂度:O(1)
- 稳定性:不稳定
选择排序
思想: 先选出最大的元素 和最小的元素 把最大的元素放到起始位置 begin 后 begin++ 把最小的元素放到结束位置end 后 end-- 再选出次大的和次小的 重复前面的操作
当 begin == end 它们之间已经没有元素了 排完了
代码实现:
void SelectSort(int* a, int n)
{
int min =0, max=0;
int begin = 0, end = n - 1;
// [begin ,end]
while (begin < end)
{
min = begin;
max = begin;
for (int i= begin; i <= end; i++)
{
if (a[i] < a[min])
min = i;
if (a[i] > a[max])
max = i;
}
Swap(&a[end], &a[max]);
//若最后一个就是最小的 就再 更新下
if (end == min)
min = max;
Swap(&a[begin], &a[min]);
begin++;
end--;
}
}