排序的概念
排序:就是一串数按照递增或递减的顺序排列
稳定性:排序后两个数字的相对次序保持不变,则这种排序是稳定的,否则不稳定
举个例子:
内部排序和外部排序
内部排序:数据元素全部放在内存中的排序
外部排序:数据元素太多不能同时放在内存中,且排序过程不能在内外存之间移动的顺序
排序算法实现、复杂度及稳定性分析
考点:
1.选择题
2.面试期间:
1.插入排序
假设我按从小到大排序,后面元素,与前一个元素比较,后面元素小,则向后移,再与前元素比较,依次循环,直到找到合适的位置(比前面元素大,比后面元素小),插入就可以了
时间复杂度: O(1)
空间复杂度 : O(N)
稳定性:稳定
使用场景:接近有序,数据量较小
void Insertsort(int *array, int n)
{
for (int i = 1; i < n; i++)
{
int key = array[i];//key是与前面元素比较的元素
int end = i - 1;//有序中的最后一个元素
while (key < array[end]&&end >= 0)
{
array[end + 1] = array[end];
end--;
}
array[end + 1] = key;
}
}
2.希尔排序
如果数据量比较大,且不接近有序,依旧按照插入排序的思想排序,就可以采用希尔排序
希尔排序:按照一定间隔划分为组,让数据量变小,在组内进行插入排序
时间复杂度: O(N^1.25 ~ 1.6N^1.25)
空间复杂度:O(1)
稳定性:不稳定
void shellsort(int *a, int n)
{
int gap = gap/3+1;
while (gap > 0)
{
for (int i = 0; i < n; i++)//分组交替依次插入
{
int end = i;
int key = a[i + gap];
while (key < a[end] && end>=0&& i+gap < n)
{
a[end + gap] = a[end];
end-=gap;
}
a[end+gap] = key;
}
}
}
3.选择排序(直接选择排序)
若要排升序,则在元素中找最大的,与最后一个元素交换,再缩范围(剩余的元素中)继续之前的步骤;也可以找最小的,与第一个元素交换,再缩范围(剩余的元素中)继续之前的步骤。排降序,则与升序是相反的
选择排序很好理解,但效率不是很好,实际很少用
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定
void Select1(int *a, int size)
{
for (int i = 0; i < size - 1; i++)//选择躺数
{
int max = 0;
for (int start = 0; start < size-i; start++)//每一次选择的方式
{
if (a[start]>a[max])
max = start;
}
if (max != size - 1 - i)
Swap(&a[max], &a[size - 1- i]);
}
}
可以直接一起让最小的、最大的元素一起排序
void Select2(int *n, int size)
{
int start = 0;
int end = size - 1;
while (start < end)
{
int min = start;
int max = end;
for (int i = start + 1; i <= end; i++)
{
if (n[i]>n[max]) max = i;
if (n[i] < n[min]) min = i;
}
if (max != end) Swap(&n[max], &n[end]);
if (min == end) min = max;
if (min != start) Swap(&n[min], &n[start]);
start++, end--;
}
}
4.堆排序
步骤:
1.先建堆(升序建大堆,降序建小堆)
从倒数第一个非叶子节点位置开始,一直到根节点的位置,应用向下调整
2.排序(根据堆删除的思想)
将堆顶元素与堆中最后一个元素交换,将堆中有效元素个数减少一个,对堆顶元素向下调整
完全二叉树的高度:log(N+1)~log(N)
时间复杂度: O(nlogn)
空间复杂度: O(1)
稳定性:不稳定
应用场景:元素不是很多、给序列中前n个元素排序、找序列中前n个元素
void AdjustDown(int *a, int size, int parent)//向下调整
{
parent = 0;
int child = parent * 2 + 1;
while (child < size)
{
if (a[child] > a[child + 1] && child + 1 < size)
child = child + 1;
if (a[parent] > a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
else return;
}
}
void HeapSort(int *a, int size)//
{
//我建的小堆,你也可以建大堆
for (int root = (size - 2) >> 1; root >= 0; root--)
AdjustDown(a, size, root);
//排序
int end = size - 1;
while (end>0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, size, 0);
end--;//
}
}
5.冒泡排序
https://blog.csdn.net/weixin_43219708/article/details/103044871
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定
应用场景:如果用户需要稳定的排序算法、越接近有序,效率越高
void BubbleSort(int *a, int size)
{
for (int i = 0; i < size-1; i++)
{
int ischange = 0;//检查进入循环是否排序
for (int j = 1; j <= size - 1 - i; j++)
{
if (a[j - 1]>a[j])
{
Swap(&a[j - 1], &a[j]);
ischange = 1;
}
}
if (!ischange) return;//已经有序就不用继续进行后面的冒泡了
}
}
6.快速排序
面试期间常考的排序
原理:
1.从区间中任取一个元素作为基准值(一般情况取的都是区间最左/最右侧元素)
2.按照基准值对区间的元素进行划分为两部分
3.假如我排的是升序(区间左侧比基准值小,右侧比基准值大)
4.递归分别排基准值左侧,右侧(与二叉数排序规则像)
时间复杂度:O(nlogn)
空间复杂度: O(logN)
稳定性:不稳定
- 左右指针法
//左右指针法(hoare版本)
int div(int *a, int left, int right)
{
int k =right;
while (left < right)
{
while (a[left] <= a[k] && left < right)
left++;
while (a[right] >= a[k] && left < right)
right--;
if (left<right)
Swap(&a[left], &a[right]);
}
if (left != k)//在同一位置就不用交换了
Swap(&a[left], &a[k]);
return left;
}
void QuickSort(int *a, int left, int right)
{
if (left > right)
return;
int ret = div(a, left, right);//与前序遍历类似
QuickSort(a, left, ret-1);
QuickSort(a, ret + 1, right);
}
- 挖坑法
int div1(int *a, int begin, int end)
{
int k = a[end];
while (begin < end)
{
while (a[begin] <= k&&begin < end)
{
begin++;
}
a[end] = a[begin];
while(a[end] >= k&&begin < end)
{
end--;
}
a[begin] = a[end];
}
a[begin] = k;
return begin;
}
void QuickSort(int *a, int left, int right)
{
if (left > right)
return;
int ret = div1(a, left, right);
QuickSort(a, left, ret-1);
QuickSort(a, ret + 1, right);
}
- 前后指针法
int div2(int *a, int begin, int end)
{
int k = begin;
int pre = begin;
int cur = begin + 1;//找比pre小的
while (cur<=end)
{
if (a[cur] <= a[k]&&++pre != cur)
{
Swap(&a[cur], &a[pre]);
}
cur++;
}
Swap(&a[k], &a[pre]);
return pre;
}
void QuickSort(int *a, int left, int right)
{
if (left > right)
return;
int ret = div2(a, left, right);
QuickSort(a, left, ret-1);
QuickSort(a, ret + 1, right);
}