一、复杂度比较
类别 | 排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
平均情况 | 最好情况 | 最坏情况 | ||||
插入 排序 |
直接插入 | 稳定 | ||||
shell排序 | 不稳定 | |||||
选择 排序 |
简单选择 | 不稳定 | ||||
堆排序 | 不稳定 | |||||
交换 排序 |
冒泡排序 | 稳定 | ||||
快速排序 | ~ |
不稳定 | ||||
归并排序 | 稳定 |
二、几种排序的C++实现
1、冒泡排序
基本思想是大的数依次沉底,设置两个位置指针i和j,i从首位开始,j从末尾开始,依次比较j到i之间的数的大小,依次交换即可。设置交换标志位flag,若发生过交换,则认为已经有序,不再发生交换,代码如下:
void BubbleSort(vector &array)//O(n^2)
{
int len = array.size();
int flag = 1;//是否发生交换标志,默认无序即发生了交换
for (int i = 0; (i < len) && flag; i++)
{
flag = 0;
for (int j = len - 2; j >= i; j--)
{
if (array[j] > array[j + 1])
{
swap(array[j], array[j + 1]);
flag = 1;
}
}
}
}
2、简单选择排序
基本思想:从首位开始,将该位置之后最小的元素与之比较,若存在比当前位置还小的元素,则交换,直至遍历完整个数组,代码如下:
void Simple_SlectSort(vector &array)//O(n^2)
{
//依次取出最小的放在开始无序的位置
int len = array.size();
int i = 0, j = 0, min = 0;
for (i = 0; i < len; i++)
{
min = i;
for (j = i + 1; j < len; j++)
{
if (array[min] > array[j])
min = j;
}
if (i != min)
swap(array[min], array[i]);
}
}
3、直接插入排序
基本思想:将一个记录插到有序数组里面,开始时,将第一个元素视为有序数组。i从下标1开始(i初始下标0),取出第i位置的元素,插到前面合适的位置,使得新数组有序,代码如下:
void InsertSort(vector &array)//O(n^2)
{
int tmp = 0;
int len = array.size();
for (int i = 1; i < len; i++)
{
int k = i;//需要插入的位置
tmp = array[i];
for (int j = i - 1; (j >= 0) && (array[j] > tmp); j--)
{
array[j + 1] = array[j];
k = j;
}
array[k] = tmp;
}
}
4、希尔排序
基本思想:先分组,再进行插入排序。根据经验,分组间隔大小gap = gap/3 + 1,代码如下:
void ShellSort(vector &array)//O(n^1.3),不稳定
{
int tmp = 0;
int len = array.size();
int gap = len;
do
{
gap = gap / 3 + 1;
for (int i = gap; i < len; i += gap)
{
int k = i;//需要插入的位置
tmp = array[i];
for (int j = i - gap; (j >= 0) && (array[j] > tmp); j -= gap)
{
array[j + gap] = array[j];
k = j;
}
array[k] = tmp;
}
} while (gap > 1);
}
5、快速排序
基本思想:通过⼀趟排序将待排记录分割成独⽴的两部分,其中⼀部分记录的关键字均⽐另⼀部分记录的关键字⼩,则可分别对这两部分记录继续进⾏排序,以达到整个序列有序的⽬的,代码如下:
void QuickSort(vector &array, int left, int right)//O(nlog2n),不稳定
{
if (left < right)
{
int i = left, j = right, x = array[left];
while (i < j && array[j] >= x)
j--;//从右到左找到第一个小于X的数
if (i < j)
array[i++] = array[j];
while (i < j && array[i] <= x)
i++;//从左到右找到第一个大于X的数
if (i < j)
array[j--] = array[i];
array[i] = x;//x放在中间
QuickSort(array, left, i - 1);
QuickSort(array, i + 1, right);
}
}
6、归并排序
基本思想:是假设初始序列含有n个记录,则可以看成是n个有序的⼦序列,每个⼦序列的⻓度为1,然后两两归并,得到|n/2|(|x|表示不⼩于x的最⼩整数)个⻓度为2或1的有序⼦序列;再两两归并,如此重复,直⾄得到⼀个⻓度为n的有序序列为⽌,这种排序⽅法称为2路归并排序,代码如下:
//合并有序数组
void merge(vector &array, int left, int right, vector &res)
{
int l_index = left;
int l_len = (right - left + 1) / 2 + 1;
int r_index = l_index + l_len;
int res_index = left;
while (l_index < left + l_len && r_index < right + 1)
{
if (array[l_index] <= array[r_index])
res[res_index++] = array[l_index++];
else
res[res_index++] = array[r_index++];
}
while (l_index < left + l_len)//如果左边还有剩余
{
res[res_index++] = array[l_index++];
}
while (r_index < right + 1)//如果右边还有剩余
{
res[res_index++] = array[r_index++];
}
}
void Merge_sort(vector &array, int left, int right, vector &res)
{
if (right - left == 1)//如果区间只有两个元素则进行排序
{
if (array[left] > array[right])
{
swap(array[left], array[right]);
}
return;
}
else if (right - left == 0)//只有一个元素则不用排序
return;
else
{
//继续划分左右子区间
Merge_sort(array, left, (right - left + 1) / 2 + left, res);
Merge_sort(array, (right - left + 1) / 2 + left + 1, right, res);
merge(array, left, right, res);
for (int i = left; i <= right; i++)
array[i] = res[i];
}
}
7、堆排序
基本思想:堆排序是基于完全二叉树的排序方法,将待排序的序列构造成⼀个⼤顶堆。此时,整个序列的最⼤值就是堆顶的根结点。将它移⾛(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最⼤值),然后将剩余的n-1个序列重新构造成⼀个
堆,这样就会得到n个元素中的次⼤值。如此反复执⾏,便能得到⼀个有序序列,代码如下:
// 递归方式构建大根堆(len是arr的长度,index是第一个非叶子节点的下标)
void adjust(vector &arr, int len, int index)
{
int left = 2 * index + 1; // index的左子节点
int right = 2 * index + 2;// index的右子节点
int maxIdx = index;
if (left arr[maxIdx])
maxIdx = left;
if (right arr[maxIdx])
maxIdx = right;
if (maxIdx != index)
{
swap(arr[maxIdx], arr[index]);
adjust(arr, len, maxIdx);
}
}
// 堆排序
void heapSort(vector &arr, int size)
{
// 构建大根堆(从最后一个非叶子节点向上)
for (int i = size / 2 - 1; i >= 0; i--)
{
adjust(arr, size, i);
}
// 调整大根堆
for (int i = size - 1; i >= 1; i--)
{
swap(arr[0], arr[i]); // 将当前最大的放置到数组末尾
adjust(arr, i, 0); // 将未完成排序的部分继续进行堆排序
}
}
8、基数排序
基本思想:按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。
//寻找数组中最大数的位数作为基数排序循环次数
int KeySize(int a[], int n){
int key = 1;
for(int i=0;i0){
temp++;
r*=10;
}
key = (temp>key)?temp:key;
}
return key;
}
//基数排序
void RadixSort(int a[], int n){
int key = KeySize(a,n);
int bucket[10][10]={0};
int order[10]={0};
for(int r = 1;key>0;key--,r*=10){
for(int i=0;i
参考资料:
1、https://www.cnblogs.com/wuxiangli/p/6399266.html
2、https://www.cnblogs.com/wanglei5205/p/8733524.html
3、https://www.cnblogs.com/cityflickr/p/3896109.html
4、《大话数据结构》——程杰