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

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

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

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

#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。

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
基于最小堆的C语言堆排序算法可以通过以下步骤实现: 1. 首先,将无序序列构建成一个最小堆。可以使用一个循环遍历无序序列,从最后一个非叶子节点开始,依次向上调整每个子树,使得根节点的值小于等于其子节点的值。这个过程可以使用一个称为"shift_down"的函数来实现,它将根节点与其较小的子节点进行交换,然后继续向下调整交换后的子树。 2. 接下来,取出堆顶元素(即最小值),将其与最后一个元素交换位置,并将堆的大小减一。这样,最小值就被放置在了有序序列的末尾。 3. 此时,堆被破坏了,需要重新调整剩余元素构建一个新的最小堆。可以使用一个称为"shift_up"的函数来实现,它将新加入的元素与其父节点进行比较,如果父节点的值大于新加入的元素,则将它们交换位置,然后继续向上调整交换后的子树。 4. 重复步骤2和步骤3,直到堆的大小为1,此时整个序列就被排序完成了。 通过以上步骤,我们可以实现基于最小堆的堆排序算法。这个算法的时间复杂度仍为O(nlogn),并且相对于其他排序算法,堆排序只需要一个辅助存储空间,使其运行空间更小。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C语言实现基于最大堆和最小堆的堆排序算法示例](https://download.csdn.net/download/weixin_38500664/12796677)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C语言堆排序算法详解](https://blog.csdn.net/weixin_58045538/article/details/121537268)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luffy5459

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值