排序:
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作
稳定性:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次 序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排 序算法是稳定的;否则称为不稳定的
常见的排序
当时间复杂度为nlogn基本都不稳定, 除了归并排序
注: 以下传值n为元素个数-1
1.插入排序
基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一 个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
1.1 直接插入排序
void InsertSort(int* src, int n) //越有序,速度越快
{
int i, j;
int tmp;
for (i = 1; i < n; i++)
{
tmp = src[i];
for (j = i; j > 0 && src[j - 1] > tmp; j--)
{
src[j] = src[j - 1];
}
src[j] = tmp;
}
}
1.2 希尔排序
void ShellSort(int* src, int n)
{
int gap, tmp;
int i, j, k;
for (gap = n / 2; gap; gap /= 2)
{
for (k = 0; k < gap; k++)
{
for (i = gap + k; i < n; i += gap)
{
tmp = src[i];
j = i;
for (j = i; j >= gap && src[j - gap] > tmp; j -= gap)
{
src[j] = src[j - gap];
}
src[j] = tmp;
}
}
}
}
2.选择排序
基本思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的 数据元素排完
2.1 选择排序
void SelectSort(int* src, int n)
{
int i, j;
int min;
for (i = 0; i < n; i++)
{
min = i;
for (j = i; j < n; j++)
{
if (src[min] > src[j])
{
min = j;
}
}
swapArgs(src + min, src + i);
}
}
2.2 堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是 通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆
堆栈的详细代码参见
超链接: github_堆栈实现
网站:https://github.com/Zhangddy/Project_t0/tree/master/19_8_17_堆/19_8_17_堆
部分代码如下
void adjustDown(Heap* hp, int m) //向下调整算法
{
int cur = m;
int n;
while (cur * 2 + 1 < hp->size)
{
if (cur * 2 + 2 >= hp->size)
{
n = cur * 2 + 1;
}
else
{
if (hp->data[cur * 2 + 1] > hp->data[cur * 2 + 2])
{
n = cur * 2 + 1;
}
else
{
n = cur * 2 + 2;
}
}
if (hp->data[cur] < hp->data[n])
{
int tmp = hp->data[cur];
hp->data[cur] = hp->data[n];
hp->data[n] = tmp;
cur = n;
}
else
{
break;
}
}
}
void HeapPop(Heap* hp)
{
if (hp->size == 0)
{
return;
}
hp->size--;
int tmp = hp->data[0];
hp->data[0] = hp->data[hp->size];
hp->data[hp->size] = tmp;
adjustDown(hp, 0);
}
void HeapSort(Heap* hp) //pop函数会调用向下调整算法
{
int temp = hp->size;
while (hp->size > 1)
{
HeapPop(hp);
}
hp->size = temp;
}
3. 交换排序
基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排 序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
3.1 冒泡排序
void BubbleSort(int *src, int n)
{
int i, j, tmp;
for (i = 0; i < n - 1; i++)
{
for (j=0; j<n-1-i ;j++)
{
if (src[j] > src[j + 1])
{
int tmp = src[j];
src[j] = src[j + 1];
src[j + 1]=tmp;
}
}
}
}
3.2 快速排序
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
将区间按照基准值划分为左右两半部分的常见方式有:
- hoare版本
- 挖坑法
- 前后指针版本
快速排序优化方法:
- 当元素个数少时采用插入排序,当元素个数多时采用快速排序
void dealQuickSort(int* src,int start, int end)
{
int mid;
if (start + 8 < end)
{
mid = hoareWay(src, start, end);
dealQuickSort(src, start, mid - 1);
dealQuickSort(src, mid + 1, end);
}
else
{
InsertSort(src + start, end - start + 1);
}
}
void QuickSort(int* src, int n)
{
dealQuickSort(src, 0, n - 1);
}
1.双指针法
int doublePointerWay(int* src, int start, int end)
{
int a = start, b = end;
int flag = 0;
while (src[b] > src[a])
{
b--;
}
while (a < b)
{
swapArgs(src + b, src + a);
flag = !flag;
while (b >= 0 && a <= end && src[b] >= src[a] ) // b >= 0 && a <= end &&
{
flag ? a++ : b--;
}
}
return flag ? b : a;
}
- 挖坑法
int digWay(int* src, int start, int end)
{
//挖坑法
//在1上优化,直接覆盖,不交换
//先tmp = a, b找比tmp小的,覆盖a的,a找比tmp大的覆盖b的
//减少交换次数,提升效率
int a = start, b = end ;
int tmp = src[a];
while (1)
{
while (b > start && src[b] >= tmp)
{
b--;
}
if (a < b)
{
src[a] = src[b];
}
else
{
src[a] = tmp;
return a;
}
while (a < end && src[a] <= tmp)
{
a++;
}
if (a < b)
{
src[b] = src[a];
}
else
{
src[b] = tmp;
return b;
}
}
}
- hoare法
int hoareWay(int* src, int start, int end)
{
//三数取中法
int a = start + 1, b = end - 2;
int mid = (start + end) / 2;
//相当于冒泡排序三个数
if (src[start] > src[mid])
{
swapArgs(src + start, src + mid);
}
if (src[mid] > src[end])
{
swapArgs(src + mid, src + end);
}
if (src[start] > src[mid])
{
swapArgs(src + start, src + mid);
}
if (end - start <= 2)
{
return mid;
}
//找到中值
swapArgs(src + mid, src + end - 1);//将中值保护起来
while (a <= b)
{
while (a < end - 1 && src[a] <= src[end - 1])
{
a++;
}
while (b > start && src[b] >= src[end - 1]) //start 改 0
{
b--;
}
if (a == b && (a == end - 2 || a == start)) // - 1
{
break;
}
if (a < b)
{
swapArgs(src + a, src + b);
}
}
swapArgs(src + a, src + end - 1);
return a;
}
4. 归并排序
基本思想:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并
void dealMergeSort(int* src, int* tmp, int start, int end)
{
if (start >= end)
{
return;
}
int mid = (start + end) / 2;
dealMergeSort(src, tmp, start, mid);
dealMergeSort(src, tmp, mid + 1, end);
int a = start;
int b = mid + 1;
int c = start;
while (a <= mid && b <= end)
{
if (src[a] < src[b])
{
tmp[c] = src[a];
a++;
}
else
{
tmp[c] = src[b];
b++;
}
c++;
}
for (; a <= mid; a++, c++)
{
tmp[c] = src[a];
}
for (; b <= end; b++, c++)
{
tmp[c] = src[b];
}
int i;
for (i = start; i <= end; i++)
{
src[i] = tmp[i];
}
}
void MergeSort(int* src, int n) //归并排序
{
int* tmp = (int*)malloc(n * sizeof(int));
dealMergeSort(src, tmp, 0, n - 1); //入口
free(tmp);
}