选择排序算法与示例详解(c语言)

2023-05-16

    选择排序是排序算法的一种,思想就是,每一轮寻找数组中最大的值或者最小的值,放在头部或者放入一个新的数组。这样经历一轮遍历,数组或者新数组就是排好序的,他的目的很明确,每次找最大值或者最小值。

    这个放在头部,其实头部不是固定不变的,每次都会往后移动一位,因为前面的数据都是排好序的。这种借助当前数组做排序的算法,是为了节省空间,也是一种提高效率的办法。

    以最大值为例,如何找最大值?比较嘛,默认选择第一个元素作为最大值,依次与数组中的元素比较,有比它大的,就交换,遍历完成,就找到了最大值。

#include <stdio.h>
int main()
{
	int arr[] = {6,5,4,3,2,1,7,8,9,0};
	int max = arr[0];
	for(int i=1;i<10;i++)
	{
		if(max<arr[i])
			max = arr[i];
	}
	printf("max=%d\n",max);
	return 0;
}

    这个找最大值,就是每次比较之后,如果满足条件都需要进行数组元素交换。也有一种做法,记录下标,如果换为交换下标,这样,最后遍历完成,我们取出下标对应的元素即可。

    我们来看看选择排序:

#include <stdio.h>
void selectSort(int arr[],int n)
{
	int i,j,maxIndex,tmp;
	for(i=0;i<n-1;i++)
	{
		maxIndex = i;
		for(j=i+1;j<n;j++)
		{
			if(arr[maxIndex]<arr[j])
				maxIndex = j;
		}
		tmp = arr[i];
		arr[i] = arr[maxIndex];
		arr[maxIndex] = tmp;
	}
}

int main()
{
	int arr[] = {6,5,4,3,2,1,7,8,9,0};
	selectSort(arr,10);
	for(int i=0;i<10;i++)
		printf("%d ",arr[i]);
	printf("\n");
	return 0;
}

    这种排序,我们需要额外申请一个变量来记录maxIndex或者minIndex,如果不用这个变量,我们就回到了前面提到的,每次交换数组元素。

void selectSort(int arr[], int n)
{
	int i, j, tmp;
	for (i = 0; i < n - 1; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			if (arr[i] < arr[j])
			{
				tmp = arr[i];
				arr[i] = arr[j];
				arr[j] = tmp;
			}
		}
	}
}

    这种排序,看起来,很像冒泡排序,不同的是,冒泡排序,每次比较的是相邻元素,选择排序比较的是固定位置的元素和集合中剩余的元素。

    我们通过示例,来直观感受一下两者的区别:

    冒泡排序:

    

    选择排序:

    

    冒泡排序算法:

void bubbleSort(int arr[], int n)
{
	int i, j, tmp;
	for (i = 0; i < n; i++) {
		for (j = 0; j < n - 1 - i; j++) {
			if (arr[j] < arr[j + 1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

   代码直观比较:

    

    从算法的结果和特点来看,选择排序,前面的元素是排好序的,而冒泡排序,末尾的元素的排好序的。所以在冒泡排序第二层循环里面,不用每次都遍历整个数组,而是到n-1-i的位置。他们共同特点是算法复杂度是相同的,都是O(n*n)。

    这里交换元素,我们也用了一个别的变量,其实也有取巧的算法,连别的变量也不需要的,直接交换两个元素的值:

if (arr[i] < arr[j])
{
    arr[i] = arr[i] ^ arr[j];
	arr[j] = arr[i] ^ arr[j];
	arr[i] = arr[i] ^ arr[j];
}

    这种办法是一种很巧妙的算法,是有计算依据的,大家可以看看如下的计算过程:

    有人可能马上觉着,这也太low了,这不就是:

//a=2,b=3
a = a + b;// a = 5
b = a - b;// b = 2
a = a - b; // a = 3

    乍一看,没毛病,这没什么好说的,但是你可能忽略的一个问题,就是计算机的整数是有范围的,在边界以内,这么来搞没问题,如果两个数字,都在边界Integer.max附近,相加就超出范围了,并不能达到预期的结果,所以说“异或”才是最安全的做法。 

    其实,这个算法的巧妙之处在于他没有借助第三个变量就交换了两个变量的值,这个算法经常在面试题中会出现,当你知道了,就会觉着很简单。

    上面介绍了选择排序的完整实现,以及各种取巧的办法。貌似很完美了,但是有个问题,就是这种排序,理论上只支持一种排序,要么升序,要么降序。如果要改变排序规则,我们需要修改交换的判断条件,需要硬编码。在c语言中,我们可以借助一个回调函数来作为判断的依据,我们在给排序算法传递参数的时候,将回调函数传进去,然后我们只需要修改回调函数的规则即可。

#include <stdio.h>

int compare(int a, int b)
{
	return a < b?1:0;
}

void selectSort(int arr[], int n, int (*pf)(int, int))
{
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			if (pf(arr[i], arr[j]))
			{
				arr[i] = arr[i] ^ arr[j];
				arr[j] = arr[i] ^ arr[j];
				arr[i] = arr[i] ^ arr[j];
			}
		}
	}
}

int main()
{
	int arr[] = { 6,5,4,3,2,1,7,8,9,0 };
	selectSort(arr, 10, compare);
	for (int i = 0; i < 10; i++)
		printf("%d ", arr[i]);
	printf("\n");
	return 0;
}

    其实这个算法就很接近c语言库提供的qsort算法了,看看算法的表示:

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))

   其中参数base是数组,nitems表示数组元素个数,size表示元素的长度,也就是该类型的长度,整型是4,比较函数两个参数都是const void *,表示任意类型,并不局限于int。 

    利用qsort排序示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void *a, const void *b)
{
	int* ia = (int*)a;
	int* ib = (int*)b;
	return *ia > *ib ? 1 : 0;
}
int main()
{
	int arr[] = { 6,5,4,3,2,1,7,8,9,0 };
	qsort(arr, 10, 4, compare);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

    区别在于可以对各种类型的数据进行排序,不仅局限于整型。他可以对int,char,double进行排序。

    我们需要注意的是这里对于字符串数组的排序,字符串可以表示为char *,字符串数组就是char **了。我们在设置回调函数作为判断条件的时候,需要对回调函数做正确的参数设置,void*这时候需要转为char**才是表示的字符串地址。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void* a, const void* b)
{
	return strcmp(*(char**)a, *(char**)b)>0?1:-1;
}
int main()
{
	const char * fruit[] = {"banana","apple","orange","grapefruit","strawberry"};
	int size = sizeof(fruit) / sizeof(fruit[0]);
	qsort(fruit, size , sizeof(char*), compare);
	for (int i = 0; i < size; i++)
	{
		printf("%s\n", fruit[i]);
	}
	return 0;
}

    以上是对选择排序的理解和整理,示例也通过vs2019编译通过。我这里遇到了一个问题,就是sizeof(char*)在64位机器下是8。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

选择排序算法与示例详解(c语言) 的相关文章

随机推荐

  • React Hook “useState“ is called in function “xxx“ which is neither a React function component or解决办法

    如题所示 xff0c 在React开发中 xff0c 我们会自定义一些函数 xff0c 比如hook函数 xff0c 或者组件 xff0c 当我们使用了useState useEffect等这些hook函数的时候 xff0c 可能会报错 x
  • electron+vue项目安装vue-devtools插件

    这里记录一下自己安装过程中遇到的问题 xff1a 1 首先需要安装vue devtools xff0c 遇到了源码编译构建的时候的webpack webpack cli反复提示缺失的问题 这个问题很烦 a git clone https g
  • vue+vuetify表格控件v-data-table使用自定义列渲染

    这里先说明一下vue项目中使用vuetify框架进行整合的办法 xff1a 1 加入依赖 npm install vuetify save 2 加入开发依赖 npm install sass sass loader deepmerge sa
  • vue+vuetify构建简单消息确认框

    vue框架自己好像没有弹出框 xff0c 而vuetify有弹出框v dialog xff0c 没有确认框confirm xff0c 虽然确认框本身就是弹出框 xff0c 但是弹出框的功能有个特点 xff0c 就是确定做一件事情 xff0c
  • docker-compose构建mysql服务

    docker安装mysql服务显得很快捷 xff0c 我们如果使用了docker compose那就更快了 xff0c 我们只需要按照我们的要求 xff0c 设置相应的端口映射 xff0c 如果有需求 xff0c 也可以设置数据映射 配置如
  • Nginx核心模块内置变量

    本文根据Nginx官网整理了Nginx的ngx http core module模块的内置变量 xff0c 可与Apache做对比参考 随后做了一次测试观察各变量的值 xff0c 并附上测试结果 1 变量列表 arg name 请求行中参数
  • js属性名用变量代替

    在前端中 xff0c 我们有时候需要利用变量名来设置属性名 xff0c 虽然不是很常见 xff0c 但是也是一个应用场景 这时候 xff0c 我们如果想当然的 xff0c 直接使用变量来设置 xff0c 那么可能不会达到我们想要的结果的 我
  • nodejs利用ffi库调用windows系统user32库函数获取桌面程序窗口大小

    ffi库是npm提供的操作windows系统库函数的依赖库 xff0c 安装过程会比较麻烦 xff0c 需要编译 xff0c 可能需要npm全局安装windows build tools xff0c 如何安装 xff0c 可以参照这里 这篇
  • nodejs利用ffi库调用windows系统user32函数模拟用户登录操作

    如题所示 xff0c 一般的桌面程序 xff0c 用户登录很简单 xff0c 就是找到用户名和密码输入框 xff0c 输入相应的用户名和密码 xff0c 然后点击 登录 按钮 xff0c 完成登录操作 这是人为操作的步骤 xff0c 如果这
  • 通过vue指令创建electron-vue模板项目出现一直“downloading template“问题

    今天试了一下 xff0c vue init simulatedgreg electron vue vueapp的时候 xff0c 在命令行下一直downloading template xff0c 让我很懊恼 原来vue init创建的时候
  • electron-vue与vuetify整合出现报错:If you‘re seeing “$attrs is readonly“

    如题所示 xff0c 正常情况下electron vue与vuetify的整合 xff0c 因为就是vue与vuetify的整合 xff0c 按照一般的推荐方法 xff0c 基本不会出错 xff0c 但是 xff0c 这里因为electro
  • VISA编程实例(C实现)

    今天写这个文章 xff0c 是因为自己工作中用到了ROHDE amp SCHWARZ xff08 即罗德 施瓦茨公司 xff09 的仪表设备 xff0c 需要通过编程的方式来读取仪表上功率测试结果 xff0c 本来仪表上显示了测试结果 xf
  • mac下通过gcc命令手动编译动态链接库示例

    编译动态链接库 xff0c windows linux mac平台各不相同 xff0c 从文件上来说 xff0c windows下是dll xff0c linux下是so xff0c mac下是dylib xff1b 命令上也会有区别 xf
  • c++中char[]与char*的转换以及char*与数字互转

    在c c 43 43 中 xff0c 字符串操作不可避免 xff0c 而且通常 xff0c char 或者char 就能表示字符串 xff0c 这个跟java语言有很大的差别 xff0c java中char是字符 xff0c string才
  • electron项目构建打包缺少dll文件的问题解决办法

    最近 xff0c 在做electron项目中 xff0c 使用了第三方dll xff0c 开发环境运行一切正常 xff0c 可是当我们打包 xff0c 最后生成的可执行程序再执行 xff0c 发现调用dll总是不成功 xff0c 猜测是少了
  • c/c++中的回调函数

    c c 43 43 中的回调函数是一个很奇怪的东西 xff0c 在java中 xff0c 方法调用的时候 xff0c 参数最多可以传入另一个对象实例 xff0c 然后在方法体内 xff0c 调用实例的方法 xff0c 做不到方法调用的时候
  • 在Windows下配置多个git账号

    本文记录在Windows下配置两个github账号的过程 1 生成并部署SSH key 安装好Git客户端后 xff0c 打开git bash xff0c 输入以下命令生成user1的SSH Key xff1a ssh keygen t r
  • windows命令行下通过cl命令编译动态链接库示例

    一般在windows下写一个c c 43 43 的动态链接库 xff0c 我们都是在visual studio或着visual c 43 43 这些ide里面进行编译和生成的 xff0c 今天介绍 xff0c 如何通过命令行来实现手动编译和
  • gdb命令调试c程序

    一般开发c语言程序 xff0c 都是在ide中编码 xff0c 调试也是使用集成环境 xff0c 有时候 xff0c 我们的程序是在文本编辑器中编写的 xff0c 这时候可能使用gcc编译 xff0c 然后运行可执行程序 遇到需要调试的场景
  • 选择排序算法与示例详解(c语言)

    选择排序是排序算法的一种 xff0c 思想就是 xff0c 每一轮寻找数组中最大的值或者最小的值 xff0c 放在头部或者放入一个新的数组 这样经历一轮遍历 xff0c 数组或者新数组就是排好序的 xff0c 他的目的很明确 xff0c 每