持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
前言
冒泡排序和直接选择排序属于常见排序,本文就来简单分享一波笔者的学习经验与心得。
笔者水平有限,难免存在纰漏,欢迎指正交流。
冒泡排序
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
算法步骤
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
代码实现
冒泡排序还有一个小优化,就是立一个 flag,如果在一趟序列遍历中元素没有发生交换,则证明该序列已经有序,就不需要再进行排序了,虽然这种改进对于提升性能来说并没有什么太大作用。
void Swap(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
void BubbleSort(int* arr, int sz)
{
assert(arr);
for(int j = 0; j < sz - 1; ++j)
{
int flag = 0;
for(int i = 0; i < sz - 1 - j; ++i)
{
if(arr[i] > arr[i + 1])
{
Swap(&arr[i], &arr[i + 1]);
flag = 1;
}
}
if(flag == 0)
break;
}
}
什么时候最快
当输入的数据已经是正序时(都已经是正序了,我还要你冒泡排序有何用啊)。
什么时候最慢
当输入的数据是反序时(写一个 for 循环反序输出数据不就行了,干嘛要用你冒泡排序呢,我是闲的吗)。
所以冒泡排序挺废的...
冒泡排序的特性总结
- 冒泡排序是一种非常容易理解的排序
- 时间复杂度:O(n2)
- 空间复杂度:O(1)
- 稳定性:稳定
直接选择排序
思路与实现
将序列分为已排序序列和待排序序列,每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在已排序序列的末尾,直到全部待排序的数据元素排完 。
步骤:
- 首先在未排序序列中找到最小(大)元素,存放到已排序序列的起始位置。
- 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
- 重复第二步,直到所有元素均排序完毕。
void SelectSort(int* arr, int sz)
{
assert(arr);//检测指针是否为空,不允许传入NULL
for(int i = 0; i < sz - 1; ++i)
{
int min_i = i;
for(int j = i + 1; j < sz; ++j)
{
if(arr[j] < arr[min_i])
min_i = j;
}
Swap(&arr[i], &arr[min_i]);
}
}
我们来搞一个稍微优化一丢丢的版本:
上面那个思路是每次找出最小的放到已排序的序列中,这里考虑每次不仅找出最小的,还要把最大的找出来,假如我们要排升序,那就把最小的放到序列前面,最大的放到序列后面,然后让序列向中间缩小,直到范围左右边界相遇或相错时停止。
void SelectSortImp(int* arr, int sz)
{
assert(arr);
int left = 0;
int right = sz - 1;
while (left < right)
{
int maxi = right;
int mini = left;
for (int i = left; i <= right; ++i)
{
if (arr[i] > arr[maxi])
maxi = i;
if (arr[i] < arr[mini])
mini = i;
}
Swap(&arr[left], &arr[mini]);
Swap(&arr[right], &arr[maxi]);
++left;
--right;
}
}
这样就完成了吗?
不,实际上还有一个细节没有考虑到:如果下标left和maxi重合怎么办?
第一个Swap函数一交换就把mini对应的最小值给换到left位置上去了,这样一来maxi对应的值就不是最大值而是最小值了!!
所以我们要在下一次Swap函数交换前把maxi修正一下:maxi = mini
。
void SelectSortImp(int* arr, int sz)
{
assert(arr);
int left = 0;
int right = sz - 1;
while (left < right)
{
int maxi = right;
int mini = left;
for (int i = left; i <= right; ++i)
{
if (arr[i] > arr[maxi])
maxi = i;
if (arr[i] < arr[mini])
mini = i;
}
Swap(&arr[left], &arr[mini]);
if (left == maxi)
maxi = mini;
Swap(&arr[right], &arr[maxi]);
++left;
--right;
}
}
直接选择排序的特性总结:
- 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用。
- 时间复杂度:O(n2)。数据顺序对算法性能没有影响,无论什么数据进去都是 O(n²) 的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧(最差排序之一)。
- 空间复杂度:O(1)
- 稳定性:不稳定
以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~