为什么选择排序几天就忘了?全是细节!

选择排序

要求:给定一数组,使用选择排序对数组排序

2910143718
int main() {
  vector<int> nums = {29, 10, 14, 37, 18};
  selectSort(nums);
  for (auto i : nums) {
    cout << i << " ";
  }
}

选择排序思想

每次选择未排序部分最小值与数组前部分已排序的末尾的下一个值交换或者每次选择未排序部分的最大值与数组后部分已排序的开始的前一个值交换
从大体思想中可以判断应该用双层循环解决此问题,外层循环控制循环的次数,内层循环负责找到未排序部分的最小值(最大值)。具体算法如下。

每次选择最小值下标

数组元素个数为5,起初数组全为未排序部分
在这里插入图片描述
当第一次循环时,因为最后需要交换值,所以需要变量minIndex记录最小值下标(所以在第二层循环前应该定义变量minIndex),第二层循环第一次遍历的最小值为10,下标为1。
在这里插入图片描述
伪代码如下

 外层循环{
 
 	int minIndex=0;
 
 	内层循环{

		找到最小值的下标;
	}
}

交换。和谁交换呢?因为是升序排序,第一次找到的最小值是全数组中最小的值,应该与数组的首位置(即下标0的位置)交换。交换的逻辑代码应该写在哪呢?需要找到最小值才能做交换,所以代码应写在第一层循环中,第二层循环结束后。
在这里插入图片描述
伪代码如下

 外层循环{
 
 	int minIndex=0;
 
 	内层循环{

		找到最小值的下标;
	}
	交换(数组首位置,最小值位置);
}

第一次循环结束,第二次循环和第一次循环的基本上过程一样,这里讨论几个细节问题。

  1. 第一次循环时的内层循环是从什么位置开始的?第二次循环的内层循环是从什么位置开始的呢?
  2. 第一次循环时的内层循环结束后,交换的两个位置下标为多少?第二次循环时的内层循环结束后,交换的两个位置下标为多少呢?在内层循环开始前定义的记录数组未排序部分最小值下标变量minIndex的初始化应怎样赋值?

答:

  1. 第一次循环时的内层循环是从什么位置开始的?如果清楚了第一次循环的过程,很容易地知道,第一次循环时的内层循环,也就是第二层循环找到最小值下标是从数组的首位置开始的,更清楚地表述是,第一次循环时的内层循环从数组未排序部分中找到最小值下标是从数组的0下标开始的。
    第二次循环呢?因为第一次循环结束后,下标0位置已经是排好序的数组了,所以第二次循环的内层循环的开始位置应该是数组未排序部分的首地址,即下标1.
    所以内层循环的开始位置不是固定的,而是根据外层循环的位置确定的,可修改伪代码如下
 for(int i=0;i<nums.size();i++){
 
 	int minIndex=0;
 
 	for(int j=i;j<nums.size();j++){

		找到最小值的下标;
	}
	交换(数组首位置,最小值位置);
}
  1. 第一次循环时的内层循环结束后,交换的两个位置下标为多少?第一次循环结束后,交换的值的下标是本次循环找到的最小值下标minIndex与已排序数组部分末尾的下一个位置,这里为0。(因为数组一开始全为未排序状态)
    第二次循环时的内层循环结束后,交换的两个位置下标为多少呢?如果第一个问题搞懂了,那这里就很容易懂了,交换的值的下标是本次循环找到的最小值下标minIndex与已排序数组部分末尾的下一个位置,所以交换的应为nums[index]和nums[1].
    这一点又可以与控制外层循环的变量有关
    另外定义记录数组未排序部分中最小值变量minIndex的赋值不应该是固定的,举例,假如minIndex始终为0,第一次循环交换时,最小值nums[minIndex](10)与nums[0]交换没有问题,当第二次循环开始后,minIndex初始化为0,此时nums[0]可是整个数组中的最小值,那么在内层循环找未排序部分的最小值下标时,minIndex始终为0,这显然不对!
    那应该怎么初始化呢?每次外层循环开始后,需要将minIndex赋值成一个数组未排序部分中的值,这一点可以与控制外层循环的变量有关,也可以赋值成数组中的最后一个下标

如果上述问题和细节明白后,不难写出选择排序的完整代码

#include <iostream>
#include <vector>
using namespace std;
void selectSort(vector<int> &nums) {

  for (int i = 0; i < nums.size(); i++) { //外层循环控制循环次数

    int minIndex = nums.size() - 1; // minIndex记录数组未排序部分最小值下标,也可初始化为i

    for (int j = i; j < nums.size(); j++) { //内层循环选择最小值下标
	      if (nums[minIndex] > nums[j]){//找到更小值下标
	          minIndex = j;
	      }
    }
    swap(nums[i], nums[minIndex]); //交换
  }
}

int main() {

  vector<int> nums = {29, 10, 14, 37, 18};
  selectSort(nums);
  for (auto i : nums) {
    cout << i << " ";
  }
}
选择排序演示过程

img

每次选择最大值下标

每次选择未排序部分的最大值与数组后部分已排序的开始的前一个值交换

逻辑与选择最小值下标近似,给出完整代码如下

#include <iostream>
#include <vector>
using namespace std;
void selectSort(vector<int> &nums) {
  for (int i = 0; i < nums.size(); i++) {
    int maxIndex = 0, j = 0;
    for (j; j < nums.size() - i; j++) {
      maxIndex = nums[j] > nums[maxIndex] ? j : maxIndex;
    }
    swap(nums[maxIndex], nums[j - 1]);
  }
}

int main() {

  vector<int> nums = {2, 4, 0, 7, 8, 3, -1, 43, 4};
  selectSort(nums);
  for (auto i : nums) {
    cout << i << " ";
  }
}

时间和空间复杂度

  • 时间复杂度:O(n2 )
  • 空间复杂度:O(1)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值