数组的实战

在这里插入图片描述

练习1:多个字符从两端移动,向中间汇聚。

上述题干的描述内容举个例子更好理解:
在这里插入图片描述
很明显需要两个字符串才能实现,一个字符串为"welcome to C!!!!!!!!!!",另一个字符串为"######################"。因为字符串可以放在字符数组中,所以可以利用下标访问操作符访问数组中的字符。定义下标的变量为leftright,分别表示数组中最左边和最右边的数组元素的下标,数组下标从0开始,所以对left初始化为0。如图所示:
在这里插入图片描述
那么怎么求下标right呢?有三种方法:
1.int right = sizeof(arr1) / sizeof(arr1[0]) - 2;
利用sizeof计算数组元素的个数再减2。这里举个简单的例子更好理解点:字符数组arr[] = "abc"里的字符c的下标2就是right,下图所示利用VS的调试得知这个数组的大小(数组的元素个数)是4,4减去2即为下标right
其实在《初识C语言》中的字符串和\0有讲解字符串的末尾默认有一个\0字符,怎么调试其中也有讲解。
在这里插入图片描述
2.int right = sizeof(arr1) - 2;
是第一种写法的简写,因为字符数组中的每一个数据的数据类型都是字符型,只占1个字节,sizeof(arr1[0])就为1,可以省略不写。
3.int right = strlen(arr1) - 1;
strlen()- string length是用来计算字符串长度的,使用时需要包含头文件<string.h>。计算时,计算的是不包括\0的字符串长度。所以计算下标right时要在strlen()的结果上减1。

初始化后的第一步代码是数组元素赋值并打印:

arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);

在这里插入图片描述

在这里插入图片描述
下标rightleft要发生改变:

left++;
right--;

在这里插入图片描述
然后又需要赋值、打印、两个下标改变,这样就会形成循环:

while (判断条件)
{
	arr2[left] = arr1[left];
	arr2[right] = arr1[right];
	printf("%s\n", arr2);
	left++;
	right--;
}

那么这个循环的判断条件是什么呢?或者说,循环终止的条件是什么呢?
其实能明显地看出下标left是要小于下标right的,那么等于行不行呢?还是拿上述的数组arr[] = "abc"为例子,下面的两张图分别表示初始化和两个下标发生变化,可以发现当两个下标对应的元素是同一个时,其实也不影响正常赋值和打印,所以循环判断条件为left <= right
初始化时
在这里插入图片描述
完整代码如下:

#include <string.h>
#include <stdio.h>
int main()
{
	char arr1[] = "welcome to C!!!!!!!!!!";
	char arr2[] = "######################";
	int left = 0;
	//int right = sizeof(arr1) / sizeof(arr1[0]) - 2;
	//int right = sizeof(arr1) - 2;
	int right = strlen(arr1) - 1;
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		left++;
		right--;
	}
	return 0;
}

在这里插入图片描述
我们看到屏幕中打印的结果是一次性全部输出,看不到变化的过程,其实还可以对代码进行改进。
改进1: 利用Sleep()函数,间隔1000毫秒再输出下一行。

#include <string.h>
#include <stdio.h>
#include <windows.h>//Sleep()的头文件
int main()
{
	char arr1[] = "welcome to C!!!!!!!!!!";
	char arr2[] = "######################";
	int left = 0;
	//int right = sizeof(arr1) / sizeof(arr1[0]) - 2;
	//int right = sizeof(arr1) - 2;
	int right = strlen(arr1) - 1;
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		Sleep(1000);//休眠1000毫秒
		left++;
		right--;
	}
	return 0;
}

最终打印的结果还是跟上面的图一样,不过,若是能在打印下一行的同时删除这一行的内容,那么变化的过程岂不是更明显,请看改进2。
改进2: 利用system()函数实现清理操作。
在搜索栏中输入cmd打开,先随便输入几组数据(如f、sf、qw、wdf…),再输入cls后按下回车,实现清理操作,想看到更明显的清理现象可以输入dir,再输入cls后按下回车,下面几个图就是这句话的过程:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
演示完整代码:

#include <string.h>
#include <stdio.h>
#include <windows.h>//Sleep()的头文件
#include <stdlib.h>//system()的头文件
int main()
{
	char arr1[] = "welcome to C!!!!!!!!!!";
	char arr2[] = "######################";
	int left = 0;
	//int right = sizeof(arr1) / sizeof(arr1[0]) - 2;
	//int right = sizeof(arr1) - 2;
	int right = strlen(arr1) - 1;
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		Sleep(1000);//休眠1000毫秒
		system("cls");
		left++;
		right--;
	}
	printf("%s\n", arr2);
	return 0;
}

练习2:二分查找

题面:
给定一个整型的有序数组,在数组中输入一个值并找到这个指定的值。比如升序数组:1 2 3 4 5 6 7 8 9 10 ,找出7,如果找到了,打印出这个值的下标;找不到的话,就打印找不到。

有序数组与无序数组的区别,举个例子就能一目了然了:

int main()
{
	int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//有序数组,这里是升序数组
	int arr2[] = { 4, 9, 5, 6, 7, 1 ,2, 5, 7, 8 };//无序数组
	return 0;
}

在一个升序的数组中找到一个指定的值,很容易想到的方法是遍历整个数组,上述题面代码演示:

#include <stdio.h>
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//升序
	int k = 0;
	scanf("%d", &k);
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		if (arr[i] == k)
		{
			printf("找到了,下标是%d\n", i);
			break;
		}
	}
	if (i == sz)
		printf("找不到\n");
	return 0;
}

代码改进,定义变量flag并初始化为0,表示一开始没有找到。若找到了,给flag赋值为1;若没找到,则flag还是为0。演示如下:

#include <stdio.h>
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//升序
	int k = 0;
	scanf("%d", &k);
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int flag = 0;//一开始没找到
	for (i = 0; i < sz; i++)
	{
		if (arr[i] == k)
		{
			flag = 1;
			printf("找到了,下标是%d\n", i);
			break;
		}
	}
	if (flag == 0)
		printf("没找到\n");
	return 0;
}

若是有序数组中的元素个数太多了,这样的查找方式肯定太慢了,所以接下来就介绍二分查找(也叫折半查找)。
引入一个例子辅助理解:一双鞋不超过300元,请猜出它的具体价格(假设鞋子的真实价格是175元)。猜的范围是1~300,从1开始猜效率太低了,其实可以对半猜,先猜是150元,被告知猜小了,则150及以下的价格就不用考虑了。再从151到300之间对半猜225,被告知猜大了,这样225及以上的价格就不用再考虑了,以此类推…
在这里插入图片描述
像这样每次都能找到被查找范围的中间元素与指定元素进行比较的方法就是二分查找(也叫折半查找)。用这种方法查找的前提是数组必须是有序数组。
那么上述题面的代码思路其实跟猜鞋子价格的思路是一样的:假设指定的元素为k,查找时找到被查找范围的中间元素,再使用中间元素和k比较并去掉一半数据。很明显,被查找范围的中间元素可以用下标来计算,中间元素下标记为mid,则mid = (left + right) / 2,再用下标mid的数组元素arr[mid]k比较在这里插入图片描述
若与指定元素相比小了,则arr[mid]及以下的元素不用考虑了,此时left = mid + 1
在这里插入图片描述
若与指定元素相比大了,则arr[mid]及以上的元素不用考虑了,此时right = mid - 1
在这里插入图片描述
以上的比较情形可能比较一次会找不到的,这样就需要while循环,那就要考虑判断条件了,left < right是一定的,那么left = right可不可以呢?如下图所示,若被查找的元素是10,执行到最后下标leftright一样,还要进行最后一次查找,所以循环的判断条件是left <= right
在这里插入图片描述
完整的代码演示:

#include <stdio.h>
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int left = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int right = sz - 1;
	int k = 0;
	scanf("%d", &k);
	int flag = 0;//一开始没找到
	while (left <= right)
	{
		int mid = (left + right) / 2;
		if (arr[mid] < k)
			left = mid + 1;
		else if (arr[mid] > k)
			right = mid - 1;
		else
		{
			printf("找到了,下标是%d\n", mid);
			flag = 1;
			break;
		}
	}
	if (flag == 0)
		printf("没找到\n");
	return 0;
}

计算mid的时候,mid = (left + right) / 2这种写法大部分情况下没什么问题,但当leftright的值太大了,相加一起会大于整数的最大值,就会有溢出的风险,下图辅助理解:
在这里插入图片描述
若把rightleft多的那一部分的一半和left的值相加,也就是mid的值。即mid = left + (right - left) / 2,下图辅助理解:
在这里插入图片描述

#include <stdio.h>
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int left = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int right = sz - 1;
	int k = 0;
	scanf("%d", &k);
	int flag = 0;//一开始没找到
	while (left <= right)
	{
		int mid = left + (right - left) / 2;
		if (arr[mid] > k)
			right = mid - 1;
		else if (arr[mid] < k)
			left = mid + 1;
		else
		{
			flag = 1;
			printf("找到了,下标是%d\n", mid);
			break;
		}
	}
	if (flag == 0)
		printf("没找到\n");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值