排序与查找

        算法效率分为空间复杂度和时间复杂度。时间复杂度:主要衡量的是一个算法的运行速度,而空间复杂度是衡量一个算法所需要的额外空间。

void fun1(int N){
    for(int i=0;i<N;i++){
        for(int j=0;i<=i;j++)
            printf("*");
    }

}
void fun2(){
    int a[5]={1,2,3,4,5},b[5];
        for (int i=1;i<5;i++){
            int j=i,temp=a[i];
		    while (j>0 && temp<b[j-1]){
		    	b[j] = b[j-1];
		    	--j;
		    }
		b[j]=temp;
	}
}

        循环种执行的语句次数则称为时间复杂度。比如fun1中的执行次数为(N+1)*N/2,时间复杂度看最高项即可即O(n^2);空间复杂度如fun2该算法需要生成一个空间进行运算。假如数据很大的话导致该函数执行的很慢。

1.选择排序

        基本思路是每次从未排序的部分中选出最小(或最大)的元素,放到已排序部分的末尾。时间复杂度O(n^2)、具体步骤如下:

  1. 初始状态:假设数组为 a,长度为 5。
  2. 选择最小(大)值:从数组的第一个元素开始,找到最小(大)的元素,并将其与第一个元素交换位置。
  3. 重复步骤:从剩下的 4 个元素中继续选择最小的元素,将其与第二个元素交换位置。
  4. 继续选择:重复上述步骤,直到整个数组排序完成。
#include <stdio.h>
int main(void)
{
	int a[5] = {3,12,0,30,-4};
	for (int i=0;i<4;i++) {
		for(int j=i+1;j<5;j++){
			if (a[i] > a[j]){
				int temp = a[i];
				a[i] = a[j];
				a[j] = temp;
			}
		}
    }
    return 0;
}

2.冒泡排序

        通过多次遍历待排序的数组,每次比较相邻的两个元素,如果它们的顺序错误就交换它们的位置。这样,较大的元素会逐渐“冒泡”到数组的末尾。时间复杂度O(n^2)、以下是具体步骤:

  1. 初始状态:假设数组为 a,长度为 5(n)。
  2. 外层循环:从第一个元素开始,进行4 (n-1) 轮排序。
  3. 内层循环:在每一轮排序中,从第一个元素开始,依次比较相邻的两个元素。如果前一个元素大于后一个元素,则交换它们的位置。
  4. 重复过程:每一轮排序结束后,最大的元素会被移动到数组的末尾。重复上述过程,直到整个数组有序。
int a[5] = {3,12,0,30,-4};	
for (int i=5-1;i>0;i--){//比较次数
		for (int j=0;j<=i-1;j++){
			if(a[j]>a[j+1]){
				int temp = a[j+1];
				a[j+1]=a[j];
				a[j]=temp;
			}
		}
	}

3.插入排序

        插入排序是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。以下是具体步骤:

  1. 初始状态:假设数组为 a,长度为 5。
  2. 外层循环:从数组的第二个元素开始,依次将每个元素插入到前面已经排好序的部分。
  3. 内层循环:对当前元素,在已排序部分从后向前扫描,找到合适的位置插入。
  4. 重复过程:重复上述步骤,直到整个数组有序。
int a[5] = {3,12,0,30,-4};
#if 0
//插入排序,非原地插入
	for (int i=0;i<5;i++){
		int j=i,temp=a[i];
		while (j>0 && temp<b[j-1]){
			b[j] = b[j-1];
			--j;
		}
		b[j]=temp;
	}
#endif

//插入排序,原地插入占用空间小
#if 0
	for (int i=1;i<5;i++){
		int j=i,temp=a[i];
		while (j>0 && temp<a[j-1]){
			a[j] = a[j-1];
			--j;
		}
		a[j]=temp;
	}
#endif

非原地插入的空间复杂度比原地插入的空间复杂度高些,时间复杂度都是O(n^2)

4.二叉法查找

        二叉查找法是一种高效的查找算法,适用于有序数组。其基本思想是通过逐步缩小查找范围,将查找目标锁定在一个较小的范围内。以下是具体步骤:

  1. 初始状态:假设数组为a,长度为n,查找目标为num。
  2. 确定中间点:计算中间点mid 的索引,mid =(end+begin)/2,其中begin为数组的起始索引,end为数组的结束索引。
  3. 比较中间点:将 num 与 a[mid] 进行比较:
    • 如果num等于 a[mid],则查找成功,break。
    • 如果num小于 a[mid],则在左半部分继续查找,即更新end = mid - 1
    • 如果num大于 a[mid],则在右半部分继续查找,即更新begin= mid + 1
  4. 重复过程:重复上述步骤,直到找到目标元素或查找范围为空(即 begin >end)。
int a[5]={1,2,34,39,90};	
int num,begin = 0,end = 4;
	printf("input a number:");
	scanf("%d",&num);
	while(begin<=end){
		int mid = (end+begin)/2;
		if (a[mid]>num){
			end = mid-1;
		}else if (a[mid]<num){
			begin = mid+1;
		}else
			break;
	}

三、数组

        数组是由同一种类型变量构成的集合 表达式 类型说明符 数组名[常量表达式],在编译中数组下标越界也不会报错。

数组的特点:

        连续性:数组空间是一片连续的空间

        有序性:数组元素挨个存放

        单一性:同一种类型

 数组初始化:部分初始)化(没有被初始化的元素默认补0),全部初始化,不初始化;

{0}与{}都是初始化为0;{}这个叫做初始化器。

       C99之后可以声明可变数组,数组长度用变量表示,但是该数组不能初始化,并且变量的长度要在声明该数组之前确定下来。

字符串数组

        char a[10]="hello 或 char a[]="hello" 或者 char a[10]={'h','e','c','l','l','o'};这两种声明字符串数组。第二种中实际长度小于数组长度的话默认补’\0‘;char a[]="hello" 系统编译时根据字符串长度自己会补全[]里的长度。

字符串数组在最后一位系统会添加'\0',做为结束标志位。

字符串可以使用gets()与puts()

gets(),输入字符串,已被弃用,容易修改到其它数据的内容。        

                                        s1                                                s

123456(h)7(e)\0(l)lo\0

              如上图简化后s1,s地址(地址连续)。输入s1时 输入“1234567”长度超过给定的长度,导致将字符串“67”写入到s所在的地址中导致“hel”数据被gets()修改了,所以输出才为67,输入字符串时会默认补’\0‘

        puts()函数:以识别到’\0‘做为结束符,如果识别不到则会一直输出下去知道遇到’\0‘

        如图,第一个图s字符串后面会自动补'\0',但是s1是系统根据实际长度自动补的也就是说s1[4]。s1的存储地址会在s的周围进行存储的,这里是在s前面存储,存储位置要看系统决定的。所以打印s1时先打印s1的字符,因为没有'\0'结束标志进行往下查找所以到s的地址上,输出完s遇到'\0'结束了语句。为了避免出现这种情况可以在s1[10]大于实际长度的长度,因为部分赋值后面会默认补'\0'.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值