所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。
1、直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
直接插入排序的特性总结:
1.1元素集合越接近有序,直接插入排序算法的时间效率越高
1.2 时间复杂度:O(N^2)
1.3空间复杂度:O(1),它是一种稳定的排序算法
1.4 稳定性:稳定
从前往后比较插入数据(这种不太好理解)
void InsertSort(int*a, int n)
{
//将第一个数据看作是有序的
for (int i = 1; i < n; i++)
{
int temp = a[i];//插入区间的下一个数据
for (int j = 0; j < i; j++)
{
if (a[i] < a[j])
{
//将j到i-1的数据往后移动一个位置
int end = i - 1;
while (end - j >= 0)
{
a[end + 1] = a[end];
--end;
}
a[j] = temp;
}
}
}
}
后向比较直接插入方法(较为好理解,可以与希尔排序放在一起理解)
void InsertSort(int*a, int n)
{
for (int end = 0; end < n - 1; end++)
{
int temp = a[end + 1];
while (end >= 0)
{
if (temp < a[end])
{
a[end + 1] = a[end];
--end;
}
else
{
break;
}
}
a[end + 1] = temp;
}
}
int main()
{
int a[] = { 9,1,2,5,7,4,8,6,3,5};
int n = sizeof(a) / sizeof(a[0]);
Print(a, n);
InsertSort(a, n);
Print(a, n);
return 0;
}
程序运行结果:
2、希尔排序
希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。
希尔排序总结:
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
- 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3 - N ^2)。
- 稳定性:不稳定。
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int end = 0; end < n - gap; end++)
{
int temp = a[end + gap];
while (end >= 0)
{
if (temp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = temp;
}
}
}
3、选择排序
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
直接选择排序的特性总结:
- 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 稳定性:不稳定
void Swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void SelectSort(int* a, int n)
{
for (int i = 0; i < n; i++)
{
int minidex = i;
for (int j = i; j < n; j++)
{
if (a[minidex] > a[j])
{
minidex = j;
}
}
Swap(&a[i], &a[minidex]);
}
}
4、堆排序:
是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
堆排的特性总结:
- 堆排序使用堆来选数,效率就高了很多。
- 时间复杂度:O(N*logN)
- 空间复杂度:O(1)
- 稳定性:不稳定
void Swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void AdjustDown(int* a, int n, int parent)
{
int child = 2 * parent + 1;
while (child < n)
{
if (child + 1 < n&&a[child] < a[child + 1])
{
++child;
}
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void HeapSort(int*a, int n)
{
//建堆
for (int end = (n - 1 - 1) / 2; end >= 0; end--)
{
AdjustDown(a, n, end);
}
int end = n - 1;
while (end >= 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
end--;
}
}
5、快速排序:在数据序列中任选一个数据作为基准,,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右
子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
时间复杂度:N*logN;
空间复杂度:O(1);
前后指针法
int PartSort(int* a, int left, int right)
{
int Key = left;
while (left < right)
{
while (left < right&&a[right] >= a[Key])
right--;
while (left < right&&a[left] <= a[Key])
left++;
Swap(&a[left], &a[right]);
}
Swap(&a[Key], &a[left]);
return left;
}
void QuickSort(int* a, int begin, int end)
{
if (begin > end)
{
return;
}
int min = PartSort(a, begin, end);
QuickSort(a, begin, min - 1);
QuickSort(a, min + 1, end);
}
6、归并排序:是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
时间复杂度:N*logN;
空间复杂度:O(N);
void _MergeSort(int* a, int left, int right, int* temp)
{
if (left>=right)
return;
int mid = (left+right) / 2;
_MergeSort(a, left, mid, temp);
_MergeSort(a, mid + 1, right, temp);
int begin1 = left;
int end1 = mid;
int begin2 = mid + 1;
int end2 = right;
int i = left;
while (begin1 <= end1 && begin2 <= end2)
{
if(a[begin1] < a[begin2])
{
temp[i++] = a[begin1++];
}
else
{
temp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
temp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
temp[i++] = a[begin2++];
}
for (int i = left; i <= right; i++)
{
a[i] = temp[i];
}
}
void MergeSort(int* a, int n)
{
int left = 0;
int right = n-1;
//int* temp = (int*)malloc(sizeof(int)*n);
int* temp = new int[n];
_MergeSort(a, left, right, temp);
delete[] temp;
temp = nullptr;
}