嵌入式学习day07:排序、二分查找(折半查找)

  排序是将一组数据按照特定的顺序(如升序或降序)进行重新排列的过程。

时间复杂度:表示算法运行所需的时间与输入规模之间的关系。

  通常使用大 O 记号来表示,例如 O(n)、O(n^2) 等。其中 n 表示输入的规模(例如数组的长度、节点的数量等)。如果一个算法的时间复杂度为 O(n),意味着算法的执行时间与输入规模 n 呈线性关系;如果是 O(n^2),则执行时间与 n 的平方成正比,随着 n 的增大,算法的运行时间增长得更快。
空间复杂度:是衡量算法在运行过程中所需额外存储空间的度量。
   它同样使用大 O 记号表示,例如 O(n)、O(1) 等。
如果空间复杂度为 O(n),表示算法需要与输入规模 n 成正比的额外空间来存储数据或进行计算;如果是 O(1),表示算法所需的额外空间是固定的,不随输入规模的变化而变化。

(一)选择排序

思想:在合适的位置选择合适的数。

例:数组a[]:
①当下标为0时,遍历除自己以外的所有在数组中的数,如果有比自己小的数,就交换过来,放在a[0]的空间中,直至比较完所有的数,这样就找到了数组中最小的数,并将它放在了a[0]的位置。
②当下标1时,从自己的后一位数开始遍历,如果有比自己小的数,就交换过来,放在a[1]的空间中,直至比较完所有的数。
  以此类推,确定下标为2、3、4、······· len-2位置上的值,排序结束,最后数组中的数为升序排列。

例:

#include <stdio.h>

int main(void)
{
	//选择排序
	int i, j, temp;
	int a[] = {3,1,7,2,8,4,5,6};
	int len = sizeof(a)/sizeof(a[0]);

	for(i=0; i<len-1; ++i)//要确定的位置,len-1=7,是下标的最大位置
	{
		for(j=i+1; j<len; ++j)//内层循环给对应位置找合适的数。
		{                       i=0时循环一次,遍历除自己以外的所有数
			if(a[j]<a[i])
			{
				temp = a[i];
				a[i] = a[j];
				a[j] = temp;
			}
		}
	}

	for(i=0; i<len; ++i)
	{
		printf("a[%d] = %d\n",i,a[i]);
	}

    return 0;
}

(二)冒泡排序

思想:一次冒出一个数,相邻两个数,小的放前,大的放后。

(1)外层循环:

       控制排序的轮数,轮数为 (len-1) ,len 为数组长度。

(2)内层循环:

        用于每一轮比较和交换相邻的元素。重复地比较要排序的数列的相邻两个数,如果顺序不对则进行交换,并一直重复这样的比较操作,直到没有要交换的数据元素为止。每一轮过后,最大的元素就“浮”到了数组的末尾。经过( len- 1) 轮排序后,数组就变成了有序的。

       冒泡排序和选择排序的平均时间复杂度和最坏时间复杂度均为 O(n²),其中 n 是待排序数组的长度。

例:

#include <stdio.h>

int main(void)
{
    int i,j,temp;
	int a[] = {3,1,7,2,8,4,5,6};
	int len = sizeof(a)/sizeof(a[0]);

	for(i=len-1; i>0; --i)
	{
		for(j=0; j<i; ++j)
		{
			if(a[j]>a[j+1])
			{
				temp = a[j];
				a[j] = a[j+1];
				a[j+1] = temp;
			}
		}
	}

	for(i=0; i<len; ++i)
	{
		printf("%d ",a[i]);
	}
	putchar('\n');

    return 0;
}

(三)插入排序

思想:在有序序列中,找到合适的位置插入。

(1)非原地插入排序:

        每次从未排序的数组 a[] 中选择一个元素,创建一个新的数组 b[] 来放置从没有排序的数组中取出的元素,每次从 a[] 中新取出的元素都要与数组 b[] 相比较,直到未排序部分为空。

(2)原地插入排序:

        先把数组 b[] 的数据构建为有序序列,对于未排序数组 a[] ,每次从 a[] 中取出的元素时,都要在数组 b[] 中从后向前扫描,找到相应位置并插入,直到整个数组有序。

原地插入排序比非原地插入排序的空间复杂度低。

非原地插入排序:

#include<stdio.h>

int main(void)
{
    int i,j,temp;
	int a[] = {3,2,5,1,4};
	int len = sizeof(a)/sizeof(a[0]);
	int b[len];

	for(i=0; i<len; ++i)
	{
		temp = a[i];
		j = i;
		while(j>0 && temp<b[j-1])
		{
			b[j] =b[j-1];
			--j;
		}
		b[j] = temp;
	}
	for(i=0; i<len; ++i)
		printf("%d ",b[i]);
	putchar('\n'); 
    return 0;
}

原地插入排序:

#include<stdio.h>

int main(void)
{
    int i,j,temp;
	int a[] = {3,2,5,1,4};
	int len = sizeof(a)/sizeof(a[0]);

	for(i=1; i<len; ++i)
	{
		temp = a[i];
		j = i;

		while(j>0 && temp<a[j-1])
		{
			a[j] = a[j-1];
			--j;
		}
		a[j] = temp;
	}
	for(i=0; i<len; ++i)
		printf("%d ",a[i]);

	putchar('\n');
    return 0;
}

(四)二分查找(折半查找)

    思想:将待查找的区间不断地对半分割,每次比较中间元素与目标值的大小,然后根据比较结果缩小查找区间,直到找到目标值或者确定目标值不存在。

二分查找的前提是数组必须是有序的。

具体的查找思路:
(1)首先,定义查找区间的左右边界 left 和 right,初始时 left = 0,right = len- 1,其中 len 是数组的长度。
(2)计算中间位置 mid = (left + right) / 2 。
(3)将中间元素 arr[mid] 与目标值 target 进行比较:

①如果 arr[mid] == target,则查找成功,返回中间位置 mid 。

② 如果 arr[mid] > target,则说明目标值在左半区间,将右边界更新为 right = mid - 1 ,然后     继续在左半区间查找。

③如果 arr[mid] < target,则说明目标值在右半区间,将左边界更新为 left = mid + 1 ,然后       继续在右半区间查找。
(4)重复步骤 2 和 3,直到找到目标值或者 left > right,表示目标值不存在,查找结束。


二分查找的效率较高。

例:

#include<stdio.h>

int mian(void)
{
    int begin, mid, end,m;
	scanf("%d",&m);
	int a[] = {1,2,3,4,5,6,7,8};
	int len = sizeof(a)/sizeof(a[0]);

	begin = 0;
	end = len -1;

	while(begin <= end)
	{
		mid = (begin+end)/2;
		if (a[mid] > m)
		{
			end = mid - 1;
		}
		else if (a[mid] < m)
		{
			begin = mid + 1;
		}
		else
		{
			break;
		}
	}
	if(begin <= end)
		printf("yes\n");
	else
		printf("no\n");

    return 0;
}

  • 14
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值