排序算法:
八种排序算法总结:
冒泡排序:
描述:
-
比较相邻的元素,如果第一个比第二个大就交换
-
对每一对相邻的元素进行同样的操作,这步做完之后最后的元素是最大的数
-
每次对越来越少的元素执行相同的步骤
//冒泡排序 时间复杂度(平均):O(n^2) 空间复杂度:O(1) 稳定性:稳定 void BabbleSort(int n, int arr[]) { //安全检查 if (arr == nullptr || n <= 0) { return; } for (int i = 0; i < n; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { Swap(j, j + 1, arr); } } } }
快速排序:
描述:
- 从列表中选择一个作为基准 base
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
//快排 时间复杂度(平均):O(nlog 2 n) 空间复杂度:O(nlog 2 n) 稳定性:不稳定
void QuickSort(int left, int right, int arr[])
{
//安全检查
if (left > right || left < 0 || right < 0) return;
int i, j, temp, base;
i = left;
j = right;
base = arr[left];
while (i < j)
{
while (i < j && arr[j] >= base) j--;
while (i < j && arr[i] <= base) i++;
if (i < j)
{
Swap(i, j, arr);
}
}
//基数归位
arr[left] = arr[i];
arr[i] = base;
QuickSort(left, i - 1, arr);
QuickSort(i + 1, right, arr);
}
直接选择排序
描述:
- 首先在未排序的序列中记录第一位为最小值的位置
- 再从剩余的元素中寻找比最小值大的并交换位置
//直接选择排序 时间复杂度(平均):O(n^2) 空间复杂度:O(1) 稳定性:不稳定
void SelectSort(int n, int arr[])
{
//安全检查
if (n <= 0) return;
int min;
for (int i = 0; i < n - 1; i++)
{
//记录最小值的位置
min = i;
for (int j = i + 1; j < n; j++)
{
if (arr[min] > arr[j])
{
min = j;
}
}
if (i != min)
{
Swap(i, min, arr);
}
}
}
直接插入排序:
描述:
- 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
- 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
//直接插入排序 时间复杂度(平均):O(n^2) 空间复杂度:O(1) 稳定性:稳定
void InsertSort(int n, int arr[])
{
//安全检查
if (n <= 0) return;
for (int i = 1; i < n; i++)
{
int temp = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > temp)
{
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
希尔排序:
描述:
- 设置增量序列
- 控制分组
- 直接插入排序
- 增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
//希尔排序 时间复杂度(平均):O(n^1.3) 空间复杂度:O(1) 稳定性:不稳定
void ShellSort(int n, int arr[])
{
//控制增量
for (int gap = n / 2; gap >= 1; gap = gap / 2)
{
//控制分组
for (int k = 0; k < gap; k++)
{
//直接插入排序
for (int i = k + gap; i < n; i += gap)
{
//默认第一个元素有序,每次从最后一个有序增量单位开始
int tamp = arr[i];
//游标指向有序序列最后一个元素
int j = i - gap;
while (j >= 0 && tamp < arr[j])
{
//移动增量个单位
arr[j + gap] = arr[j];
j -= gap;
}
arr[j + gap] = tamp;
}
}
}
}
堆排序:
描述:
- 创建一个堆
- 把堆首(最大值)和堆尾交换
- 递归调整其他不满足堆性质的部分
//堆排序 时间复杂度(平均):O(nlog 2 n) 空间复杂度:O(1) 稳定性:不稳定
void Adjust(int arr[], int len, int index)
{
int left = 2 * index + 1;
int right = 2 * index + 2;
int maxId = index;
if (left<len && arr[left]>arr[maxId]) maxId = left;
if (right<len && arr[right]>arr[maxId]) maxId = right;// 到此maxIdx是3个数中最大数的下标
if (maxId != index)// 如果maxIdx的值有更新
{
Swap(maxId, index, arr);
Adjust(arr, len, maxId);// 递归调整其他不满足堆性质的部分
}
}
void HeadSort(int n, int arr[])
{
for (int i = n / 2; i >= 0; i--) // 对每一个非叶结点进行堆调整(从最后一个非叶结点开始)
Adjust(arr, n, i);
for (int i = n - 1; i >= 1; i--)
{
Swap(i, 0, arr); // 将当前最大的放置到数组末尾
Adjust(arr, i, 0); // 将未完成排序的部分继续进行堆排序
}
}
归并排序:
描述:
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;result
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置; start end
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
- 重复步骤 3 直到某一指针达到序列尾;
- 将另一序列剩下的所有元素直接复制到合并序列尾。
//归并排序 时间复杂度(平均):O(nlog 2 n) 空间复杂度:O(1) 稳定性:稳定
void Merge(int* data, int start, int end, int* result)
{
int left_length = (end - start + 1) / 2 + 1;//左部分区间元素的个数
int left_index = start;
int right_index = start + left_length;
int result_index = start;
while (left_index < start + left_length && right_index < end + 1)
{
//对分别已经拍好的左区间和右区间进行合并
if (data[left_index] <= data[right_index])
result[result_index++] = data[left_index++];
else
result[result_index++] = data[right_index++];
}
while (left_index < start + left_length)
result[result_index++] = data[left_index++];
while (right_index < end + 1)
result[result_index++] = data[right_index++];
}
void MegeSort(int* data, int start, int end, int* result)
{
//如果区间中只有两个元素 对这两个元素进行排序
if (end - start == 1)
{
if (data[start] > data[end])
{
Swap(start, end, data);
}
return;
}
if (end - start == 0)
return;//如果只有一个元素 则不用排序
else
{
//继续划分子区间,分别对左右子区间进行排序
MegeSort(data, start, (end - start + 1) / 2 + start, result);
MegeSort(data, (end - start + 1) / 2 + start + 1, end, result);
//开始归并已经排好序的start到end之间的数据
Merge(data, start, end, result);
//把排序后的区间数据复制到原始数据中去
for (int i = start; i <= end; ++i)
data[i] = result[i];
}
}
基数排序:
描述:
基数排序(以整形为例),将整形10进制按每位拆分,然后从低位到高位依次比较各个位。每次比较完进行排序,直到整个数组有序
主要分为两个过程:
(1)分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)
(2)收集,再将放置在0~9号桶中的数据按顺序放到数组中
重复(1)(2)过程,从个位到最高位,直到排好序为止(比如32位无符号整形最大数4294967296,最高位10位)