手撕排序算法:选择排序
文章目录
选择排序(Selection Sort)是一种简单直观的排序算法。它首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
1.算法思想
第一步、在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
第二步、再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
2.代码实现
void Selsct_Sort(int num[],int numSize) {
for (int i = 0; i < numSize - 1; i++) {
int mid = i;
for(int j = i+1; j < numSize; j++) {
if (num[mid] > num[j]) {
mid = j;
}
}
if (mid != i) {
int temp = num[i];
num[i] = num[mid];
num[mid] = temp;
}
}
}
3.算法分析
3.1 时间复杂度
选择排序中有两个嵌套循环。当有n个元素时,外循环正好运行一1次迭代。但内部循环运
行变得越来越短:
当i=0,内层循环n一1次「比较」操作。
当i=1,内层循环n一2次「比较」操作。
当i=2,内层循环n一3次「比较」操作。
当i=n-3,内层循环2次「比较」操作。
当i=n-2,内层循环1次「比较」操作。
因此,总「比较」次数如下:(n-1)+…+2+1=n(n-1)/2,总的时间复杂度为:O(n^2)。
3.2 空间复杂度
选择排序的空间复杂度为O(1)。它只使用了固定的额外空间来存储交换操作,并不依赖于输
入数组的大小。
4.算法优缺点
4.1算法的优点
1.简单易懂:选择排序是一种简单直观的排序算法,容易理解和实现。
2.原地排序:选择排序不需要额外的存储空间,只使用原始数组进行排序
3.适用于小型数组:对于小型数组,选择排序的性能表现较好。
4.2算法的缺点
1.时间复杂度高:在处理大规模数据时效率较低。
2.不稳定:选择排序在排序过程中可能会改变相同元素的相对顺序,因此它是一种不稳定的排序
算法。
5.优化方案
「选择排序」在众多排序算法中效率较低,时间复杂度为O(^2)。
想象一下,当有n=100000个数字。即使我们的计算机速度超快,并且可以在1秒内计算10^8
次操作,但选择排序仍需要大约一百秒才能完成。
考虑一下,每一个内层循环是从一个区间中找到一个最小值,并且更新这个最小值。是一个「
动态区间最值」问题,所以这一步,我们是可以通过「线段树」来优化的。这样就能将内层循环
的时间复杂度优化成O(log2n)了,总的时间复杂度就变成了O(nlog2n).
当然,常见的选择排序优化是采用堆排序来进行优化的,后续我们会学到。
6.实战
6.1力扣 75.颜色分类
给定一个包含红色、白色和蓝色、共 n
个元素的数组 nums
,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0
、 1
和 2
分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题
void sortColors(int* nums, int numsSize) {
for(int i = 0; i < numsSize - 1; i++){
int mid = i;
for(int j = i + 1; j < numsSize; j++){
if(nums[mid] > nums[j]){
mid = j;
}
}
if(mid != i){
int temp = nums[i];
nums[i] = nums[mid];
nums[mid] = temp;
}
}
}
6.2力扣4 寻找两个正序数组的中位数
给定两个大小分别为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n))
。
void Select_Sort(int num[],int numSize) {
for (int i = 0; i < numSize - 1; i++) {
int mid = i;
for(int j = i+1; j < numSize; j++) {
if (num[mid] > num[j]) {
mid = j;
}
}
if (mid != i) {
int temp = num[i];
num[i] = num[mid];
num[mid] = temp;
}
}
}
int a[2000];
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
int n = 0;
for(int i = 0; i < nums1Size; i++){
a[n++] = nums1[i];
}
for(int i = 0; i < nums2Size; i++){
a[n++] = nums2[i];
}
Select_Sort(a,n);
if(n % 2 ){
return a[n / 2];
}
return (a[n/2 - 1] + a[n / 2]) / 2.0;
}
6.3 力扣747 至少是其他数字两倍的最大数
给你一个整数数组nums,其中总是存在唯一的一个最大整数。
请你找出数组中的最大元素并检查它是否至少是数组中每个其他数字的两倍。如果是,则返回最大元素的下标,否则返回
-1。
void Select_Sort(int nums[],int numSize) {
for (int i = 0; i < numSize - 1; i++) {
int mid = i;
for(int j = i+1; j < numSize; j++) {
if (nums[mid] > nums[j]) {
mid = j;
}
}
if (mid != i) {
int temp = nums[i];
nums[i] = nums[mid];
nums[mid] = temp;
}
}
}
int dominantIndex(int* nums, int numsSize) {
int max = 0;
for(int i = 0; i < numsSize; i++){
if(nums[i] > nums[max]){
max = i;
}
}
Select_Sort(nums,numsSize);
if(nums[numsSize - 1] >= 2 * nums[numsSize - 2]){
return max;
}
return -1;
}