排序是经常会考到的算法。各大排序算法总结:
排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 数据对象稳定性 |
---|---|---|---|---|
冒泡排序 | O(n2) | O(n2) | O(1) | 稳定 |
选择排序 | O(n2) | O(n2) | O(1) | 数组不稳定、链表稳定 |
插入排序 | O(n2) | O(n2) | O(1) | 稳定 |
快速排序 | O(n*log2n) | O(n2) | O(log2n) | 不稳定 |
堆排序 | O(n*log2n) | O(n*log2n) | O(1) | 不稳定 |
归并排序 | O(n*log2n) | O(n*log2n) | O(n) | 稳定 |
希尔排序 | O(n*log2n) | O(n2) | O(1) | 不稳定 |
1. 冒泡排序
冒泡排序的主要思想是一趟比较,相邻元素之间进行对比,遍历一遍,可以将一个最值元素沉底。 时间复杂度为O(n^2),最好最坏都是如此 空间复杂度为O(1) 比较稳定void BubbleSort(int arr[], int size)
{
checkParam(arr, size);
for (int i = 0; i < size; i++)
{
/*相邻元素进行比较,每次都找一个最值,放到队尾*/
for (int j = 0; j < size - 1 - i; j++)
{
if (arr[j] < arr[j + 1])
swapElement(arr[j], arr[j + 1]);
}
}
printArray(arr, size);
}
2. 选择排序
选择排序:与冒泡排序对比,交换次数很少,走一趟只需要一次交换
因为选择排序走一趟的目的是为了找到一个最值,然后进行交换。
时间复杂度为O(n^2),最好最坏都是如此
空间复杂度为O(1)
数组不稳定(涉及插入),链表稳定
void SelectSort(int arr[], int size)
{
checkParam(arr, size);
for (int i = 0; i < size; i++)
{
/*找到最值的下标*/
int min = i;
for (int j = i + 1; j < size; j++)
{
if (arr[j] < arr[min])
{
min = j;
}
}
/*最后才交换位置*/
swapElement(arr[min], arr[i]);
}
printArray(arr, size);
}
3. 插入排序
插入排序,与冒泡和选择相比,插入排序不交换值,只是选择合适的位置。
维护前面的顺序数组
时间复杂度为O(n^2),最好最坏都是如此
空间复杂度为O(1)
链表稳定
void InsertSort(int arr[], int size)
{
checkParam(arr, size);
for (int i = 1; i < size; i++)
{
int cur = arr[i];
int j = i - 1;
/*维护已经排好序的数组*/
while (j >= 0 && arr[j] > cur)
{
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = cur;
}
printArray(arr, size);
}
4. 快速排序
快速排序:找到一个标志位,使得左边的数据都小于这个标志数据,右边的数据都大于这个标志数据。
然后对左边的数据,和右边的数据进行处理。
时间复杂度,在最坏情况下:O(n^2),平均时间复杂度:O(NlogN,2为底),平均复杂度,1.3NlogN
空间复杂度,在最坏情况下:O(n),平均时间复杂度:O(logN,2为底)
不稳定
void QuickHelp(int arr[], int begin, int end)
{
if (begin < end)
{
int flag = begin;
int right = end;
int x = arr[begin];
/*找到标志位flag,使得左边的都小于x,右边的都大于x*/
while (flag < right)
{
while (flag < right && arr[right] >= x)
right--;
/*把小于X的放在左边*/
arr[flag] = arr[right];
while (flag < right && arr[flag] < x)
flag++;
/*把大于X的放在右边*/
arr[right] = arr[flag];
}
arr[flag] = x;
QuickHelp(arr, begin, flag - 1);
QuickHelp(arr, flag + 1, end);
}
return;
}
void QuickSort(int arr[], int size)
{
checkParam(arr, size);
QuickHelp(arr, 0, size - 1);
printArray(arr, size);
}
5. 堆排序
堆排序:大顶堆(升序,小到大),小顶堆(降序,大到小)。
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
然后对左边的数据,和右边的数据进行处理。
时间复杂度:平均 O(NlogN,2为底)
空间复杂度:O(1)
不稳定
void create_heap(int array[], int begin, int end)
{
int son = begin * 2 + 1;
int dad = begin;
while (son <= end)
{
if (son + 1 <= end && array[son] < array[son + 1])
{
son++;
}
if (array[dad] > array[son])
return;
else
{
std::swap(array[dad], array[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
void heap_sort(int array[], int length)
{
//参数判断
//1.构建大顶堆
for (int i = length / 2 - 1; i >= 0; i--)
{
//从最后一个非叶子结点从下至上,从右至左调整结构
create_heap(array, i, length - 1);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for (int i = length - 1; i > 0; i--)
{
std::swap(array[0], array[i]); //将堆顶元素与末尾元素进行交换
create_heap(array, 0, i - 1); //重新对堆进行调整
}
printArray(array, length);
}
6. 归并排序
归并排序:采用分治法。先分,再合,合的过程中进行比较。
归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差
归并排序的最好,最坏,平均时间复杂度均为O(nlogn)
空间复杂度为O(N)
稳定
void merge_sort_recursive(int *arr, int *reg, int start, int end)
{
if (start >= end)
{
return;
}
int len = end - start, mid = (len >> 1) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
/*左边递归*/
merge_sort_recursive(arr, reg, start1, end1);
/*右边递归*/
merge_sort_recursive(arr, reg, start2, end2);
/*合并*/
int k = start;
while (start1 <= end1 && start2 <= end2)
{
reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
}
while (start1 <= end1)
{
reg[k++] = arr[start1++];
}
while (start2 <= end2)
{
reg[k++] = arr[start2++];
}
/*赋值操作*/
for (k = start; k <= end; k++)
{
arr[k] = reg[k];
}
}
void merge_sort(int *arr, const int length)
{
/*避免在递归中,频繁开辟空间*/
int *reg = new int[length];
merge_sort_recursive(arr, reg, 0, length - 1);
printArray(arr, length);
delete[] reg;
}
7. 希尔排序
希尔排序:插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
设定分组,组内进行插入排序,逐渐减少分组。
时间复杂度,在最坏情况下:O(n^2),平均时间复杂度:O(N logN logN,2为底)
空间复杂度:O(1)
不稳定
void shell_sort(int array[], int length)
{
/*步长设定为gap , 每次除以2,逐渐缩小分组*/
for (int gap = length >> 1; gap > 0; gap >>= 1)
{
cout << "gap : " << gap << endl;
/*分组,插入排序*/
for (int i = gap; i < length; i++)
{
int temp = array[i];
int j = 0;
for (j = i - gap; j >= 0 && array[j] > temp; j -= gap)
{
array[j + gap] = array[j];
}
array[j + gap] = temp;
}
}
printArray(array, length);
}