C语言入门——第十七课

一、二分查询

1.概念

二分查询又被称为二分查找,是一种在有序数组或序列中快速查找到对应元素的一种方法。每次查找范围缩小至原来的一半。

①前提条件

数组和列表必须有序,否则无法进行二分查找。

②初始化

确定查找数组和列表的左边界(通常为数组或列表第一个元素)和右边界(通常为数组和列表最后一个元素)。

③循环

  • 计算中间元素的序列
  • 比较中间元素和目标元素
    • 如果中间元素等于目标元素,那么查询结束,返回中间元素的索引。
    • 如果目标元素大于中间元素,说明目标元素在中间元素的右半部分,那么左边界移到中间元素的右边。
    • 如果目标元素小于中间元素,说明目标元素在中间元素的左半部分,那么右边界移至中间元素的左边。

④结束条件

如果查找到目标元素的索引,退出。

或者如果左边元素的索引大于右边元素,说明目标元素不在数组内,那么退出。 

2.代码

int BinaryFindValue(const int* arr, int n, int value)
{
	int pos = -1;
	if (n < 1 || arr == NULL)	return pos;
	int left = 0;
	int right = n-1;
	while (left<=right)
	{
		int mid = (right - left +1 ) / 2 + left;
		if (value < arr[mid])
		{
			right = mid - 1;
		}
		else if (value > arr[mid])
		{
			left = mid + 1;
		}
		else
		{
			pos = mid;
			break;
		}
	} 
		return pos;
}
int main()
{
	const int n = 5;
	int arr[n] = { 10,11,12,13,14 };
	int val = 0;
	scanf("%d", &val);
	int m = BinaryFindValue(arr, n, val);
	printf("%d", m);
}

C语言入门——第十七课_第1张图片

也可以将这一行写成这样

int mid = (right - left +1 ) / 2 + left;

修改后: 

int mid = (right - left + 1) >> 1 + left;

 >> 是右移位操作符,在计算机中执行二进制位的右移。>> 1 表示将二进制位向右移动一位,相当于除以2。

注意这里有个问题:

int mid =((right - left + 1) >> 1) + left;

加号大于右移操作符,这样写的话是1+left然后右移,所以要给前面加上括号。

最终正确代码

int BinaryFindValue(const int* arr, int n, int value)
{
	int pos = -1;
	if (n < 1 || arr == NULL)	return pos;
	int left = 0;
	int right = n-1;
	while (left<=right)
	{
		int mid = ((right - left + 1) >> 1) + left;
		if (value < arr[mid])
		{
			right = mid - 1;
		}
		else if (value > arr[mid])
		{
			left = mid + 1;
		}
		else
		{
			pos = mid;
			break;
		}
	} 
		return pos;
}
int main()
{
	const int n = 5;
	int arr[n] = { 10,11,12,13,14 };
	int val = 0;
	scanf("%d", &val);
	int m = BinaryFindValue(arr, n, val);
	printf("%d", m);
}

3. Q&A

Q:下面这个代码有问题,请详细说明原因

A:

问题一:是left

C语言入门——第十七课_第2张图片

问题二:是使用(left+right)/2,如果left和right足够大,他们的mid很可能超出最大整数的范围,容易溢出。

修改:

① 修改判断条件,让left<=right的时候退出循环,这样就可以查找到边界的目标元素。

②修改查找中间元素的计算方式,所以使用下面的方式只对区域进行取半。

C语言入门——第十七课_第3张图片

4.优化一

如果我的序列中有多个重复的数字,我想要找到最左端数字的下标,你需要怎么做?

添加一个判断

while (arr[mid - 1] == value)
			{
				mid--;
			}
			pos = mid;
			break;

判断arr[mid]和value相等的时候,它的前一个值是否也相等,但是这样写是错误的,因为如图,在下面的示例中,要找到12最左边元素的下标值,mid-1判断到mid=0下标的时候,再-1会造成越界,所以我们要对于判断条件增加。

C语言入门——第十七课_第4张图片

增加一个条件mid-1>=0,防止越界行为,老师在这里增加的条件是mid>left,也是相同的道理,两种写法都可以。 

//while ((mid>left) && (arr[mid - 1] == value))
while ((mid-1 >= 0) && (arr[mid - 1] == value))
			{
				mid--;
			}
			pos = mid;
			break;
int BinaryFindValue(const int* arr, int n, int value)
{
	int pos = -1;
	if (n < 1 || arr == NULL)	return pos;
	int left = 0;
	int right = n-1;
	while (left<=right)
	{
		int mid =((right - left + 1) >> 1) + left;
		if (value < arr[mid])
		{
			right = mid - 1;
		}
		else if (value > arr[mid])
		{
			left = mid + 1;
		}
		else
		{
			while ((mid-1 >= 0) && (arr[mid - 1] == value))
			{
				mid--;
			}
			pos = mid;
			break;
		}
	} 
		return pos;
}
int main()
{
	const int n = 13;
	int arr[n] = { 12,12,12,13,13,13,13,13,24,24,24,25,26};
	int val = 0;
	scanf("%d", &val);
	int m = BinaryFindValue(arr, n, val);
	printf("%d", m);
}

C语言入门——第十七课_第5张图片5.优化二

在这里查找最左端元素的时候,你使用的是递归查询,请问怎么样使用二分法继续进行最左端元素的查找。

使用递归方法继续查找呢?

暂无思路,待补充……

二、结构体

1.定义

结构体是由我们自己设计的一种类型。

结构体和数组的区别:数组中所有元素的类型一致,但是结构体中元素的类型不需要一致。

结构体的定义

struct Student //结构体名
{
    //成员列表
};

这里结束的时候记得写分号哦!

注意: 在C语言中,这里的Student是结构体名,struct Student是结构体类型名。在进行创建结构体变量时,需要使用结构体类型名创建。但是在C++中结构体类型名和结构体名没有区别。

Student sx;//错误
struct Student sx;//正确 

2.初始化 

结构体的在声明的时候不需要初始化,因为它此时还是类型,不会开辟空间,所以不能赋初值。在定义结构体变量时,系统才会给结构体分配空间。

初始化顺序需要与结构体声明次序保持一致。

①使用花括号初始化

struct Student
{
	char s_name[20];
	int age;
};
int main()
{
	struct Student sx = { "lizeyu",23 };
	printf("%s %d\n", sx.s_name, sx.age);
}

结构体嵌套结构体初始化,也是使用花括号初始化。

struct Date
{
	int year;
	int month;
	int day;
};
struct Student
{
	char s_name[20];
	Date birthday;
	int age;
};
int main()
{
	struct Student sx = { "lizeyu",{2000,8,21},23 };
	printf("%s %d\n", sx.s_name, sx.age);
}

或者写成这个样子也是可以的:

struct Student sx = { "lizeyu",2000,8,21,23 };

3.结构体在内存中的存储

如果结构体内的数组是按照[ ]方式声明的,定义变量的时候,栈区为数组分配对应字节的空间。

C语言入门——第十七课_第6张图片

C语言入门——第十七课_第7张图片如果声明结构体成员列表的时候使用指针的方式声明数组,那么栈区的指针指向被存储在.data区的数组。

C语言入门——第十七课_第8张图片

你可能感兴趣的:(C语言,数据结构)