C经典88案例-第三部分

151 阅读34分钟

以下88案例打包下载地址

链接: www.keketec.club/posts/c065d…

案例ex45: 将排好序的字符串保存到磁盘文件中

1 题目

功能:将排好序的字符串保存到磁盘文件中

描述:从键盘读入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存

2 知识点

2.1 处理字符串的两个函数

函数名调用形式功能返回值
fgetsfgets(str, n, fp)从fp指向的文件读入一个长度为 (n-1) 的字符串,存放到字符数组str中读成功,返回地址str,失败则返回 NULL
fputsfputs(str, fp)把str所指向的字符串写到文件指针变量 fp 所指向的文件中输出成功,返回0;否则返回非 0 值

fgets 中最后一个字母 s 表示字符串(string)。fgets 的含义是,从文件读取一个字符串

1. fgets 函数的函数原型

char * fgets(char * str, int n, fILE * fp);

其作用是从文件中读取一个字符串。调用时可以写成

fgets(str, n, fp)

其中 n 是要求得到的字符的个数,但实际上只从fp所指向的文件中读取 n-1 个字符,然后在最后加 '\0' 字符,这样得到的字符串共有 n 个字符,把它们放到数组 str 中。如果在读完 n-1 个字符之前遇到了换行符 '\n' 或者结束符EOF,读入即结束,这块遇到换行符 '\n' 也作为一个字符读入。若执行 fgets函数成功,则返回值为 str 数组元素的地址,如果一开始就遇到文件尾或者读取数据出错,则返回 NULL

2.fputs函数的函数原型

char * fputs(char * str, fILE * fp);

其作用是将str所指向的字符串输出到fp所指向的文件中。调用时可以写成

例如:

fputs("China", fp);

把字符串“China”输出到fp指向的文件中。fputs函数中第 1 个参数可以是字符串常量、字符数组名或者字符型指针。字符串末尾的 '\0' 不输出,若输出成功,函数值为 0;失败时,函数值为 EOF。

fgets 和 fgets 这两个函数的功能类似于 gets 和 puts 函数,只是 gets 和 puts 以终端为读写对象,而 fgets 和 fputs 函数以指定的文件作为读写对象。

3 思路

  1. 从键盘读入n个字符串,存放在一个二维字符数组中,每个一维数组存放一个字符串
  2. 对字符数组中的n个字符串按字母顺序排序,排好序的字符串仍存放在字符数组中
  3. 将字符数组中的字符串顺序输出

4 代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
功能:将排好序的字符串保存到磁盘文件中
描述:从键盘读入若干个字符串,对它们按字母大小的顺序排序,然后把排好序的字符串送到磁盘文件中保存
**/

int main(int argc, char const *argv[]) {
	FILE * fp;
	char str[3][20], temp[10];				// str保存存放字符串的二维数组,temp 临时数组
	int i, j, k, n = 3;
	printf("输入三个字符串: \n");				// 提示输入字符串
	for (i = 0; i < n; ++i) {
		gets(str[i]); 										// 终端输入
	}


	for(i=0; i<n-1; i++) {         			// 用选择法对字符串排序
		k=i;
		for(j=i+1;j<n;j++)
			if(strcmp(str[k], str[j]) > 0)
				k=j;
		if(k != i) {
			strcpy(temp, str[i]);
			strcpy(str[i], str[k]);
			strcpy(str[k], temp);
		}
	}

	if ((fp = fopen("save_string.dat", "w")) == NULL) {	 // 打开磁盘文件
		printf("打开文件失败\n");
	}

	printf("新的语句是:\n");
	for (i = 0; i < n; ++i) {
		fputs(str[i], fp);
		fputs("\n", fp);
		printf("%s\n", str[i]);
	}
}

示例结果:

$ gcc ex045.c -o demo
$ ./demo
输入三个字符串:
grasp
apple
banana
新的语句是:
apple
banana
grasp

案例ex46: 用二进制方式向文件读写一组数据

1 题目

函数:save()/read()

功能:用二进制方式向文件读写一组数据

描述:从键盘输入 10 个学生的有关数据,然后把他们转存到磁盘文件中去

2 知识点

在程序中不仅需要一次输入输出一个数据, 而且常常需要一次输入输出一组数据(如数组或结构体变量的值), C语言允许用fread函数从文件中读一个数据块, 用fwrite函数向文件写一个数据块。在读写时是以二进制形式进行的。向磁盘写数据时, 直接将内存中一组数据原封不动、不加转换地复制到磁盘文件上, 在读入时也是将磁盘文件中若干字节的内容一批读入内存 它们的一般调用形式为:

fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);

buffer:是一个地址

对fread来说,它是用来存放从文件读入的数据的存储区的地址

对fwrite来说,是要把此地址开始的存储区中的数据向文件输出(以上指的是起始地址)

size:要读写的字节数

count:要读写多少个数据项(每个数据项长度为size)

fp: FILE类型指针

在打开文件时指定用二进制文件, 这样就可以用 fread 和 fwrite 函数读写任何类型的信息, 例如:

fread(f, 4, 10, fp);

其中 f 是一个 float 型数组名(代表数组首元素地址)。这个函数从 fp 所指向的文件读入10个4字节的数据,存储到数组 f 中。

3 思路

定义一个有 10 个元素的结构体数据,用来存放 10 个学生的数据。

从 main() 中输入 10 个数据。

用 save() 函数实现向磁盘输出学生数据。

用 fwrite() 函数一次输出一个学生的数据

用fread() 函数进行数据的读取

  1. 在main函数中,从终端键盘输入io个学生的数据,然后调用 save 函数,将这些数据输出到以“stu.dat”命名的磁盘文件中。fwrite函数的作用是将一个长度为36节的数据块送到 ex046_stud.dat 文件中(一个struct student_type类型结构体变量的长度为它的成员长度之和,即10 + 4 + 4 + 15 = 33,实际上占36字节,是4的倍数)
  2. 在fopen函数中指定读写方式为“wb”,即二进制写方式。在向磁盘文件 ex046_stud.dat 写的时候,将内存中存放stud数组元素stud订的内存单元中的内容原样复制到磁盘文件,所建立的 ex046_stud.dat 文件是一个二进制文件。这个文件可以为其他程序所用。
  3. 在本程序中,用fopen函数打开文件时没有指定路径,只写了文件名ex046_stud.dat,系统默认其路径为当前用户所使用的子目录,在此目录下建立一个新文件ex046_stud.dat,输出的数据存放在此文件中。
  4. 程序运行时,屏幕上并无输出任何信息,只是将从键盘输入的数据送到磁盘文件上

4 代码

#include <stdio.h>
#include <stdlib.h>
#define SIZE 10

/**
函数:save()/read()
功能:用二进制方式向文件读写一组数据
描述:从键盘输入 10 个学生的有关数据,然后把他们转存到磁盘文件中去
思路:
**/

struct Student_type {
	char name[10];
	int num;
	int age;
	char addr[30];
}stud[SIZE];

// 以二进制格式进行保存
void save() {
	FILE * fp;
	if ((fp = fopen("ex046_stud.dat", "wb")) == NULL) {		// 打卡输入文件 ex046_stud.dat
		printf("写入文件打开失败!\n");
		return;
	} else {
		printf("写入成功!\n");
	}
	for (int i = 0; i < SIZE; ++i) {
		if(fwrite(&stud[i], sizeof(struct Student_type), 1, fp) != 1)
			printf("写入失败\n");
	}
	fclose(fp);
}

// 从二进制文件中读取数据
void read() {
	FILE * fp;
	if ((fp = fopen("ex046_stud.dat", "rb")) == NULL) {
		printf("读取打开文件失败!\n");
		exit(0);
	}
	for (int i = 0; i < SIZE; ++i) {
		fread(&stud[i], sizeof(struct Student_type), 1, fp); 	// 从 fp 指向的文件读入一组数据
		printf("%s\t%d\t%d\t%s\n", stud[i].name, stud[i].num, stud[i].age, stud[i].addr);
	}
	fclose(fp);
}

int main(int argc, char const *argv[]) {
	printf("输入 %d 个学生的信息:\n", SIZE);
	for (int i = 0; i < SIZE; ++i) {	// 注意 scanf 中的地址传递
		scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,&stud[i].addr);
	}
	save();
	printf("\n====== 读取写入的数据 =======\n");
	read();
	return 0;
}

示例结果:

$ gcc ex046.c -o demo
$ ./demo
输入 10 个学生的信息:
"A" 1 11 "street01"
"B" 2 12 "street02"
"C" 3 13 "street03"
"D" 4 14 "street04"
"E" 5 15 "street05"
"F" 6 16 "street06"
"G" 7 17 "street07"
"H" 8 18 "street08"
"I" 9 19 "street09"
"J" 10 110 "street010"
写入成功!

====== 读取写入的数据 =======
"A"	1	11	"street01"
"B"	2	12	"street02"
"C"	3	13	"street03"
"D"	4	14	"street04"
"E"	5	15	"street05"
"F"	6	16	"street06"
"G"	7	17	"street07"
"H"	8	18	"street08"
"I"	9	19	"street09"
"J"	10	110	"street010"

案例ex47: 对一个文件重复读取

1 题目

功能:对一个文件重复读取(利用rewind()函数)

描述:

将磁盘文件的内容进行读取

第一次将它的内容显示在屏幕上,第二次把他复制到另外一个文件中

2 知识点

对文件需要进行随机读取的原因: 对文件进行顺序读写比较容易理解,也容易操作,但有时效率不高,例如文件中有1000个数据,若只査第1000个数据,必须先逐个读入前面999个数据,才能读入第1000个数据。如果文件中存放一个城市几百万人的资料,若按此方法查某一人的情况,等待的时间可能太长了。 随机访问不是按数据在文件中的物理位置次序进行读写,而是可以对任何位置上的数 据进行访问,显然这种方法比顺序访问效率高得多。

rewind() 函数

文件位置标记定位,强制使文件位置标记指向文件开头,此函数没有返回值

3 思路

可以利用 rewind() 函数进行操作,当第一次读取完后,文件位置标记指向了文件尾,那么使用 rewind() 函数将文件位置标记重新返回到文件头部

4 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:对一个文件重复读取
描述:将磁盘文件的内容进行读取,第一次将它的内容显示在屏幕上,第二次把他复制到另外一个文件中
**/

int main(int argc, char const *argv[]) {
	FILE * fp1, * fp2;
	fp1 = fopen("ex047_1.dat", "r"); 	// 打开输入文件
	fp2 = fopen("ex047_2.dat", "w"); 	// 打开写入文件
	while(!feof(fp1)){
		putchar(fgetc(fp1)); 			// 输出到屏幕
	}
	putchar(10); 						// 换行
	rewind(fp1);						// 文件位置标记重新返回文件的开头
	while(!feof(fp1)) {
		fputc(fgetc(fp1), fp2); 			// 重头,逐个字符读取写入到ex047_2.dat
	}
	fclose(fp1);
	fclose(fp2);
	return 0;
}

执行后结果:

$ gcc ex047.c -o demo
$ ./demo
hello
world
this is my first use of wind!

ex047_2.dat 文件中内容,与ex047_1.dat一致:

hello
world
this is my first use of wind!

案例ex48: 将文件中不同位置的数据打印出来

1 题目

功能:将文件中不同位置的数据打印出来

描述:

在磁盘上也存有10个学生的数据,要求将第 1、3、5、7、9 的学生数据输入到计算机 并且在屏幕中显示出来

2 知识点

  • fseek() 函数

    fseek() 函数改变文件位置标记 fseek() 的调用形式; fseek(文件类型指针, 位移量, 起始点) 起始点用0,1或者2来替代。0 代表文件起始位置、1代表当前位置、2代表文件末尾位置

    "位移量"指的是一“起始点” 为基点,向前移动的字节数。位移量应是 long 型数据 fseek() 函数一般用于二进制文件。看以下例子:

    fseek(fp, 100L, 0) 	// 将文件位置标记向前移动到离文件开头100个字节处
    fseek(fp, 50L, 1) 	// 将文件位置标记向前移动到离当前位置50个字节处
    fseek(fp, -10L, 2) 	// 将文件位置标记向前移动到离文件末尾10个字节处
    
  • ftell() 函数 测定文件位置标记的当前位置 ftell() 的作用是得到流式文件中文件位置标记的当前位置 由于文件中的文件位置标记经常移动,人们往往不容易知道其当前的位置,所以常用 ftell() 函数得到当前位置,用相对于文件开头的位移量来表示。 如果调用函数时出错(例如不存在 fp 指向的文件), ftell() 返回 -1L。如下:

    i = ftell(fp); 	// i 存放文件当前的位置
    if (i == -1L) {
    	print("error!");
    }
    

3 思路

  1. 按照2进制只读的方式打开指定文件,准备从文件中读取学生的成绩
  2. 将文件位置标记指向文件的开头,然后从磁盘文件读取一个学生的信息,并把它显示在屏幕上
  3. 再将文件位置标记指向第 1、3、5、7、9 的学生的数据区的开头,从磁盘中读取相应的学生的信息,显示在屏幕上
  4. 关闭文件

注意:将【ex046.c 用二进制方式向文件读写一组数据】按照二进制形式写入的学生信息进行读取

4 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:将文件中不同位置的数据打印出来
描述:
在磁盘上也存有10个学生的数据,要求将第 3、5、7、9 的学生数据输入到计算机
并且在屏幕中显示出来
**/

struct student_type { 	// 学生数据信息
	char name[10];
	int num;
	int age;
	char addr[30];
}stud[10];


int main(int argc, char const *argv[]) {
	FILE * fp;
	if((fp = fopen("ex046_stud.dat", "rb")) == NULL) {
		printf("打开文件失败!\n");
		exit(0);
	}
	for (int i = 0; i < 10; i+=2) {
		fseek(fp, i*sizeof(struct student_type), 0); 			// 移动文件位置标记
		fread(&stud[i], sizeof(struct student_type), 1, fp);	// 读取一个数据块到结构体变量
		printf("%s %4d %d %s \n", stud[i].name, stud[i].num, stud[i].age, stud[i].addr);
	}
}

源文件中的数据:

"A"	1	11	"street01"
"B"	2	12	"street02"
"C"	3	13	"street03"
"D"	4	14	"street04"
"E"	5	15	"street05"
"F"	6	16	"street06"
"G"	7	17	"street07"
"H"	8	18	"street08"
"I"	9	19	"street09"
"J"	10	110	"street010"

示例结果:

"A"    1 11 "street01"
"C"    3 13 "street03"
"E"    5 15 "street05"
"G"    7 17 "street07"
"I"    9 19 "street09"

案例ex49: 文件加密

1 题目

函数:encrypt() 功能:文件加密 描述:C语言实现文件加密,执行参数有 【原文件路径及名称 密码 加密后文件路径及名称】

2 思路

register 关键字

C语言中的关键字register代表寄存器存储类型 register修饰符暗示编译程序相应dao的变量zhuan将被频繁地使用,如shu果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度。这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率

加密

ch = ch ^ *(pwd + i);

采用异或方法进行加密,当然不同的情况可以采用不同的方式进行加密

3 代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
函数:fun()
功能:文件加密
描述:C语言实现文件加密,执行参数有
【原文件路径及名称 密码 加密后文件路径及名称】
**/



void encrypt(char *s_file, char *pwd, char *c_file);			     	// 文件加密函数
void encrypt(char *s_file, char *pwd, char *c_file) {  					// 自定义函数encrypt用于加密
    int i = 0;
    FILE *fp1,  *fp2; 													// 定义fp1和fp2是指向结构体变量的指针
    register char ch;
    fp1 = fopen(s_file, "rb");
    if (fp1 == NULL) {
        printf("无法打开原文件.\n");
        exit(1); 														// 如果不能打开要加密的文件,便退出程序
    }
    fp2 = fopen(c_file, "wb");
    if (fp2 == NULL) {
        printf("无法打开被加密的文件.\n");
        exit(1); 														// 如果不能建立加密后的文件,便退出
    }
    ch = fgetc(fp1);
    while (!feof(fp1))	{ 												// 测试文件是否结束

        ch = ch ^ *(pwd + i);											// 采用异或方法进行加密,也可以采用其他方式方法就行加密
        i++;
        fputc(ch, fp2);													// 异或后写入fp2文件
        ch = fgetc(fp1);
        if (i > 9)
            i = 0;
    }
    fclose(fp1);
    fclose(fp2);
}

int main(int argc, char const *argv[]) {
    char sourcefile[50]; 												// 被加密的文件名称
    char codefile[50];                         // 加密后的文件名
    char pwd[10]; 														// 密码存储
    if (argc != 4) {													// 容错处理,不满足参数格式,需要自行输入
        printf("请输入原文件名称:\n");
        gets(sourcefile); 												// 得到要加密的文件名
        printf("请输入密码:\n");
        gets(pwd); 														// 得到密码
        printf("请输入加密后文件名称:\n");
        gets(codefile); 												// 得到加密后你要的文件名
        encrypt(sourcefile, pwd, codefile);
    }
    else {
        strcpy(sourcefile, argv[1]);
        strcpy(pwd, argv[2]);
        strcpy(codefile, argv[3]);
        encrypt(sourcefile, pwd, codefile);
    }
}

示例结果:

$ gcc ex049.c -o demo
$ ./demo
请输入原文件名称:
s_file.txt
请输入密码:
johngo_py
请输入加密后文件名称:
c_file.txt

最后就可以发现有加密后的文件产出


案例ex50: 使用共用体存放学生和老师的信息

1 题目

功能:使用共用体存放学生和老师的信息 描述:根据输入职业的标识,区分出是老师还是学生 然后根据输入的标识将对应的信息输出。 如果是学生,则输出班级信息 如果是老师,则输出职位信息

2 思路

共用体有时也被称为联合或者联合体

这也是 Union 这个单词的本意

结构体和共用体的区别:

结构体的各个成员会占用不同的内存,互相之间没有影响;

共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。

结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。

共用体也是一种自定义类型,可以通过它来创建变量,例如:

union data{
	int n;
	char ch;
	double f;
};
union data a, b, c;

上面是先定义共用体,再创建变量,也可以在定义共用体的同时创建变量:

union data{
	int n;
	char ch;
	double f;
} a, b, c;

如果不再定义新的变量,也可以将共用体的名字省略:

union{
	int n;    	
	char ch;
	double f;
} a, b, c;

另外,看看下面代码中对共用体的使用方法,可以在后面编程中参考使用

3 代码

#include <stdio.h>
#include <stdlib.h>
#define N 2

/**
函数:fun()
功能:使用共用体存放学生和老师的信息
描述:根据输入职业的标识,区分出是老师还是学生
然后根据输入的标识将对应的信息输出。
如果是学生,则输出班级信息
如果是老师,则输出职位信息
**/


struct {
	int num;
	char name[10];
	char tp;
	union {									//共用体类型
		int inclass;
		char position[10];
	}job;									  //共用体变量
}person[N];									//结构体变量

int main(int argc, char const *argv[]) {
	int i;
	printf("请输入 %d 个人信息(编号 姓名 类型(s/t) 班级/职位):\n", N);
	// 信息填写
	for(i=0; i<N; i++) {
		printf("第%d个人\n",i+1);
		scanf("%d %s %c", &person[i].num, person[i].name, &person[i].tp);		/*输入信息*/
		if(person[i].tp=='s')										/*根据类型值判断是老师还是学生*/
			scanf("%d", &person[i].job.inclass);					/*输入工作类型*/
		else if(person[i].tp=='t')
			scanf("%s", person[i].job.position);
		else
			printf("输入有误");
	}
	printf("\n编号    姓名    类型    班级/职位\n");
	
	// 信息输出
	for(i=0;i<2;i++) {
		if(person[i].tp == 's')										/*根据工作类型输出结果*/
			printf("%d\t%s\t%c\t%d", person[i].num, person[i].name, person[i].tp, person[i].job.inclass);
		else if(person[i].tp == 't')
			printf("%d\t%s\t%c\t%s", person[i].num, person[i].name, person[i].tp, person[i].job.position);
		printf("\n");			
	}
}

示例结果:

$ gcc ex050.c -o demo
$ ./demo
请输入 2 个人信息(编号 姓名 类型(s/t) 班级/职位):
第1个人
1 Johngo1 s 1
第2个人
2 Johngo2 t 数学

编号    姓名    类型    班级/职位
1	Johngo	s	1
2	Johngo2	t	数学

案例ex51: 使用共用体处理任意类型数据

1 题目

功能:使用共用体处理任意类型数据 描述:设计一个共用体类型,使其成员包含多种数据类型,根据不同的类型,输出不同的数据

2 思路

首先设定了各种数据类型的变量,由于这些变量不是全部一次性处理的,所以就采用了共用体类型 在下面例子中,通过 TypeFlag 来识别在共用体重的存储类型,执行程序的时候就会按照不同的存储方式进行存储到 union_demo 变量中

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
函数:fun()
功能:使用共用体处理任意类型数据
描述:设计一个共用体类型,使其成员包含多种数据类型,根据不同的类型,输出不同的数据
**/

union {										// 定义共用体
	int i;									// 共用体成员
	char c;
	float f;
	double d;
}union_demo;										// 声明共用体类型的变量

int main(int argc, char const *argv[]) {
	char TypeFlag;
	printf("输入成员类型:\n");
	scanf("%c",&TypeFlag);								// 输入类型符
	printf("输入数字:\n");
	switch(TypeFlag) {									// 多分支选择语句判断输入
		case 'i':scanf("%d",&union_demo.i); break;
		case 'c':scanf("%c",&union_demo.c); break;
		case 'f':scanf("%f",&union_demo.f); break;
		case 'd':scanf("%lf",&union_demo.d);
	}
	switch(TypeFlag) { 	 								// 多分支选择语句判断输出
		case 'i':printf("%d",union_demo.i); break;
		case 'c':printf("%c",union_demo.c); break;
		case 'f':printf("%f",union_demo.f); break;
		case 'd':printf("%lf",union_demo.d);
	}							
	printf("\n");
}

示例结果:

$ gcc ex051.c -o demo
$ ./demo
输入成员类型:
i
输入数字:
10
10

案例ex52: 输出今天星期几

1 题目

2 思路

举语法定义格式为:

enum 枚举名 {枚举元素1,枚举元素2,……};

举个例子,比如:一星期有 7 天,如果不用枚举,我们需要使用 #define 来为每个整数定义一个别名:

#define Monday  		1
#define Tuesday  		2
#define Wednesday  	3
#define Thursday  	4
#define Friday  		5
#define Saturday  	6
#define Sunday  		7

这个看起来代码量就比较多,接下来我们看看使用枚举的方式:

enum week{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday} ;

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
函数:week()
功能:输出今天星期几
描述:枚举类型的使用,利用枚举类型表示一周的每一天,然后通过数据来输出对应周几
**/

enum week{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday} ; //定义枚举结构

int main(int argc, char const *argv[]) {
	int day;											//定义整型变量
	printf("输入星期数(0-6):");
	scanf("%d",&day);									//输入0-6的值
	switch(day) {										//根据数值进行判断
		case Sunday: printf("今天是星期天"); break;			//根据枚举类型进行判断
		case Monday: printf("今天是星期一"); break;
		case Tuesday: printf("今天是星期二"); break;
		case Wednesday: printf("今天是星期三"); break;
		case Thursday: printf("今天是星期天四"); break;
		case Friday: printf("今天是星期五"); break;
		case Saturday: printf("今天是星期六"); break;	
	}
	printf("\n");
}

示例结果:

$ gcc ex052.c -o demo
$ ./demo
输入星期数(0-6):5
今天是星期五

案例ex53: 任意次方后的最后三位

1 题目

功能:任意次方后的最后三位 描述: 求一个整数任意次方后的最后三位数,即求 x^y 的最后三位 x 和 y 的值由键盘输入

2 思路

注意点:

为了防止计算越界,本例中不能使用直接计算一个数的任意次方。那么如何去计算并且有效防止计算越界,而且不会产生误差。

这里采用了一种方式就是在每次进行次方相乘后,取其后三位,这样就不会出现越界的现象产生。

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:任意次方后的最后三位
描述:
求一个整数任意次方后的最后三位数,即求 x^y 的最后三位
x 和 y 的值由键盘输入
**/

int main(int argc, char const *argv[]) {
	int i, x, y, z = 1;
	printf("请输入两个数,x和y(x^y):\n");
	scanf("%d%d", &x, &y); 								    // 输入底数和幂数
	for (i = 1; i <= y; i++)
		z = z * x % 1000;								    // 计算一个数任意次方的后三位
	if(z>=100)
		printf("%d^%d的最后三位是:%d\n", x, y, z); 			// 输出最终结果*/
	else
		printf("%d^%d的最后三位是:0%d\n", x, y, z);			// 输出最终结果
}

示例结果:

$ gcc ex053.c -o demo
$ ./demo
请输入两个数,x和y(x^y):
1 1
1^1的最后三位是:001

$ ./demo
请输入两个数,x和y(x^y):
2 4
2^4的最后三位是:016

$ ./demo
请输入两个数,x和y(x^y):
5 9
5^9的最后三位是:125

案例ex54: 计算某日是该年的第几天

1 题目

功能:计算某日是该年的第几天 描述:计算天数的代码,从键盘输入年、月、日,在屏幕输出此日期是改年的第几天

2 思路

判断年份是闰年还是平年,两点:要么可以直接被 400 整除,要么能被 4 整除,但是不能被 100 整除

if (a % 4 == 0 && a % 100 != 0 || a % 400 == 0)
    return 1;								
else
    return 0;	

判断该天是改年的第几天,那么需要知道每个月有多少天。另外,2 月份需要首先判断是平年还是闰年,再来进行后续的计算

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:计算某日是该年的第几天
描述:计算天数的代码,从键盘输入年、月、日,在屏幕输出此日期是改年的第几天
**/

int leap(int a)	{	 									// 判断是否为闰年
    if (a % 4 == 0 && a % 100 != 0 || a % 400 == 0)		// 闰年判定条件
        return 1;										// 是闰年返回1
    else
        return 0;										// 不是闰年返回0
}

int number(int year, int m, int d) {					// 参数 年 月 日,计算是该年的第几天

    int sum = 0, i;
    // 平年每月的天数
    int a[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    // 闰年每月的天数
    int b[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    if (leap(year) == 1)								// 判断是否为闰年
        for (i = 0; i < m - 1; i++)
            sum += b[i];								// 是闰年,累加数组b前m-1个月份天数
    else
        for (i = 0; i < m - 1; i++)
            sum += a[i];								// 不是闰年,累加数组a钱m-1个月份天数
    sum += d;														// 将前面累加的结果加上日期,求出总天数
    return sum;													// 将计算的天数返回
}

int main(int argc, char const *argv[]) {
    int year, month, day, n;										// 定义变量为基本整型
    printf("请输入年月日\n");
    scanf("%d%d%d", &year, &month, &day);				// 输入年月日
    n = number(year, month, day);								// 调用函数number
    printf("%d.%d.%d 是 %d 年的第 %d 天\n", year, month, day, year, n);
}

示例结果:

$ gcc ex054.c -o demo
$ ./demo
请输入年月日
2020 12 31
2020.12.31 是 2020 年的第 366 天

案例ex55: 婚礼上的谎言

1 题目

功能:婚礼上的谎言

描述:

3 对儿情侣参加婚礼,3 个新浪为A、B、C,3 个新娘为 X、Y、Z

有人想知道究竟谁与谁结婚,X 说他的未婚夫为C,C 说他将和 Z 结婚。这人们时候都知道他们在开玩笑,说全是假的。

那么,究竟谁与谁结婚呢?

2 思路

利用穷举法进行计算

然后再利用题目中的谎话进行逻辑判断

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:婚礼上的谎言
描述:
3 对儿情侣参加婚礼,3 个新浪为A、B、C,3 个新娘为 X、Y、Z
有人想知道究竟谁与谁结婚,X 说他的未婚夫为C,C 说他将和 Z 结婚。这人们时候都知道他们在开玩笑,说全是假的。
那么,究竟谁与谁结婚呢?
**/

int main(int argc, char const *argv[]) {
    int a, b, c;
    for (a = 1; a <= 3; a++)								// 穷举a的所有可能
        for (b = 1; b <= 3; b++)							// 穷举b的所有可能
            for (c = 1; c <= 3; c++)						// 穷举c的所有可能
                if (a != 1 && c != 1 && c != 3 && a != b && a != c && b != c) {
												// 如果表达式为真,则输出结果,否则继续下次循环
        printf("%c 将嫁给 A\n", 'X' + a - 1);
        printf("%c 将嫁给 B\n", 'X' + b - 1);
        printf("%c 将嫁给 C\n", 'X' + c - 1);
    }
}

示例结果:

$ gcc ex055.c -o demo
$ ./demo
Z 将嫁给 A
X 将嫁给 B
Y 将嫁给 C

案例ex56: 百元买百鸡

1 题目

功能:百元买百鸡

描述:

中国占代数学家张丘建在他的《算经》中提出了一个著名的“百钱买百鸡闷题”

鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁、母、雏各几何?

2 思路

要点 所买的 3 种鸡的钱数总和是 100 所买的 3 种鸡的数量总和是 100 所买的小鸡的数量必须是 3 的倍数

3 代码

# include <stdio.h>
# include <stdlib.h>

/**
功能:百元买百鸡
描述:
中国占代数学家张丘建在他的《算经》中提出了一个著名的“百钱买百鸡闷题”
鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁、母、雏各几何?
**/

int main(int argc, char const *argv[]) {
    int cock, hen, chick;										// 定义变量为基本整型
    for (cock = 0; cock <= 20; cock++)								// 公鸡范围在0到20之间
        for (hen = 0; hen <= 33; hen++)							// 母鸡范围在0到33之间
            for (chick = 3; chick <= 99; chick++)						// 小鸡范围在3到99之间
                if (5 *cock + 3 * hen + chick / 3 == 100) 				// 判断钱数是否等于100
                    if (cock + hen + chick == 100) 				// 判断购买的鸡数是否等于100
                        if (chick % 3 == 0) 						// 判断小鸡数是否能被3整除
                            printf("公鸡:%d 母鸡:%d 小鸡:%d\n", cock, hen,chick);
}

示例结果:

$ gcc ex056.c -o demo
$ ./demo
公鸡:0 母鸡:25 小鸡:75
公鸡:4 母鸡:18 小鸡:78
公鸡:8 母鸡:11 小鸡:81
公鸡:12 母鸡:4 小鸡:84

案例ex57: 判断三角形的类型

1 题目

功能:判断三角形的类型 描述:根据给定的三条边判断是否能构成三角形,并且输出它对应的面积和三角形类型

2 思路

从键盘中输入三条边后,只需判断这三条边中任意两边之和是否大于第三边

如果满足条件,可以构成三角形

再做进一步判断确定该三角形是什么三角形

若两边相等-则是等腰三角形;若三边相等,则是等边三角形;若三边满足勾股定理,则是直角三角形

另外,注意 && 和 || 的使用,以及 & 与 && 的区别和 | 与 || 的区别

3 代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/**
功能:判断三角形的类型
描述:根据给定的三条边判断是否能构成三角形,并且输出它对应的面积和三角形类型
**/

int main(int argc, char const *argv[]) {
	float a, b, c;
	float s, area;
	printf("输入三角形的三条边长(以空格分隔):");
	scanf("%f %f %f", &a, &b, &c); // 输入三条边
	printf("a = %f\n", a);
	printf("b = %f\n", b);
	printf("c = %f\n", c);
	if (a + b > c && b + c > a && a + c > b) {					// 判断两边之和是否大于第三边
		s = (a + b + c) / 2;
		area = (float)sqrt(s *(s - a)*(s - b)*(s - c)); 		// 计算三角形的面积
		printf("面积是:%f\n", area); 							// 输出三角形的面积
		if (a == b && a == c)									// 判断三条边是否相等
			printf("三条边组成的三角形是: 等边三角形\n");
			// 输出等边三角形
		else if (a == b || a == c || b == c)
			// 判断三角形中是否有两边相等
			printf("三条边组成的三角形是: 等腰三角形\n");
			// 输出等腰三角形
		else if ((a * a + b * b == c * c) || (a * a + c * c == b * b) || (b * b + c * c == a *a))
			// 判断是否有两边的平方和大于第三边的平方
			printf("三条边组成的三角形是: 直角三角形\n");
			// 输出直角三角形
		else
			printf("三条边组成的三角形是: 普通三角形");
			// 普通三角形
	}
	else
		// 如果两边之和小于第三边不能组成三角形
		printf("该三条边不能构成三角形\n");
}

示例结果:

$ gcc ex057.c -o demo
$ ./demo
输入三角形的三条边长(以空格分隔):3 4 5
a = 3.000000
b = 4.000000
c = 5.000000
面积是:6.000000
三条边组成的三角形是: 直角三角形

案例ex58: 直接插入排序

1 题目

功能:直接插入排序 描述:利用直接插入排序进行将数组序列从小到大排序

2 思路

原始顺序: 34, 12, 45, 3, 8, 23, 89, 52, 24, 10

在代码中将数组 a[0] 置为监视哨

趟数监视哨排序结果
134(12,) 34, 45, 3, 8, 23, 89, 52, 24, 10
212(12, 34,) 45, 3, 8, 23, 89, 52, 24, 10
345(12, 34, 45,) 3, 8, 23, 89, 52, 24, 10
43(3, 12, 34, 45,) 8, 23, 89, 52, 24, 10
58(3, 8, 12, 34, 45,) 23, 89, 52, 24, 10
623(3, 8, 12, 23, 34, 45,) 89, 52, 24, 10
789(3, 8, 12, 23, 34, 45, 89,) 52, 24, 10
852(3, 8, 12, 23, 34, 45, 52, 89,) 24, 10
924(3, 8, 12, 23, 24, 34, 45, 52, 89,) 10
1010(3, 8, 10, 12, 23, 24, 34, 45, 52, 89,)

以上是整个的插入排序算法的过程

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:直接插入排序
描述:利用直接插入排序进行将数组序列从小到大排序
**/

void insort(int s[], int n)	{					// 自定义函数isort

    int i, j;
    for (i = 2; i <= n; i++) {					// 数组下标从2开始,0 位置做监视哨,1位置一个数据无可比性
        s[0] = s[i];							// 给监视哨赋值
        j = i - 1;								// 确定要进行比较的元素的最右边位置
        while (s[0] < s[j]) {
            s[j + 1] = s[j];					// 数据右移
            j--;								// 移向左边一个未比较的数
        }
        s[j + 1] = s[0];						// 在确定的位置插入s[i]
    }
}

int main(int argc, char const *argv[]) {
    int a[11], i;									// 定义数组及变量为基本整型
    printf("请输入10个数据:\n");
    for (i = 1; i <= 10; i++)
        scanf("%d", &a[i]);							// 接收从键盘中输入的10个数据到数组a中
    printf("原始顺序:\n");
    for (i = 1; i < 11; i++)
        printf("%3d", a[i]);						// 将未排序前的顺序输出
    insort(a, 10);									// 调用自定义函数isort()
    printf("\n插入数据排序后顺序:\n");
    for (i = 1; i < 11; i++)
        printf("%3d", a[i]);						// 将排序后的数组输出
    printf("\n");
}

示例结果:

$ gcc ex058.c -o demo
$ ./demo
请输入10个数据:
34
12
45
3
8
23
89
52
24
10
原始顺序:
 34 12 45  3  8 23 89 52 24 10
插入数据排序后顺序:
  3  8 10 12 23 24 34 45 52 89

案例ex59: 希尔排序

1 题目

功能:希尔排序 描述:利用希尔排序进行将数组序列从小到大排序

2 思路

希尔排序是在直接插入排序的基础上做的改进,将要排序的序列按固定增量分成若干组,等距离者在同一组中,然后再程纽内进行直接插入排序。

这里面的固定增量从n/2开始,以后每次缩小到原来的一半.

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:希尔排序
描述:利用希尔排序进行将数组序列从小到大排序
**/

void shsort(int s[], int n)	{
    int i, j, d;
    d = n / 2;													// 确定固定增量值
    while (d >= 1){
        for (i = d + 1; i <= n; i++) {							// 数组下标从d+1开始进行直接插入排序
            s[0] = s[i];										// 设置监视哨
            j = i - d;											// 确定要进行比较的元素的最右边位置
            while ((j > 0) && (s[0] < s[j])) {
                s[j + d] = s[j];								// 数据右移
                j = j - d;										// 向左移d个位置
            }
            s[j + d] = s[0];									// 在确定的位置插入s[i]
        }
        d = d / 2;												// 增量变为原来的一半
    }
}

int main(int argc, char const *argv[]) {

    int a[11], i;												// 定义数组及变量为基本整型
    printf("请输入10个数据:\n");
    for (i = 1; i <= 10; i++)
        scanf("%d", &a[i]);										// 从键盘中输入10个数据
    shsort(a, 10);												// 调用shsort()函数
    printf("排序后的顺序是:\n");
    for (i = 1; i <= 10; i++)
        printf("%5d", a[i]);									// 将排好序的数组输出
	printf("\n");
}

示例结果:

$ gcc ex059.c -o demo
$ ./demo
请输入10个数据:
34
15
56
12
90
43
9
7
93
100
排序后的顺序是:
    7    9   12   15   34   43   56   90   93  100

案例ex60: 冒泡排序

1 题目

功能:冒泡排序 描述:利用冒泡排序进行将数组序列从小到大排序

2 思路

冒泡法的基本思路是

如果要对 n 个数进行冒泡排序,那么要进行 n-1 趟比较,在第1趟比较中要进行 n-1 次两两比较,在第 j 趟比较中要进行 n-j 次两两比较

从这个基本思路中就会发现,趟数决定了两两比较的次数,这样就很容易将两个 for 循环联系起来了

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:冒泡排序
描述:利用冒泡排序进行将数组序列从小到大排序
**/

int main(int argc, char const *argv[]) {

    int i, j, t, a[11];							// 定义变量及数组为基本整型
    printf("请输入10个数:\n");
    for (i = 1; i < 11; i++)
        scanf("%d", &a[i]);						// 从键盘中输入10个数
    for (i = 1; i < 10; i++)					// 变量i代表比较的趟数
        for (j = 1; j < 11-i; j++)				// 变量j代表每趟两两比较的次数
    if (a[j] > a[j + 1]) {
        t = a[j];								// 利用中间变量实现俩值互换
        a[j] = a[j + 1];	
        a[j + 1] = t;
    }
    printf("排序后的顺序是:\n");
    for (i = 1; i <= 10; i++)
        printf("%5d", a[i]);					// 将冒泡排序后的顺序输出
	printf("\n");
}

示例结果:

$ gcc ex060.c -o demo
$ ./demo
请输入10个数:
56
34
16
87
134
14
1
84
55
90
排序后的顺序是:
    1   14   16   34   55   56   84   87   90  134

案例ex61: 快速排序

1 题目

功能:快速排序 描述:利用快速排序进行将数组序列从小到大排序

2 思路

在待排序的n个数据中取第 1 个数据作为基准值,将所有记录分为 3 组 使笫一组中各数据值均小于或等于基准值,第二组做基准值的数据,第三组中各数据值均人于或等于基准值,这便实现了第一趟分割,然后再对第一组和第三组分别重复上述方法。 依次类推;直到每组中只有一个记录为止

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:快速排序
描述:利用快速排序进行将数组序列从小到大排序
**/

void qusort(int s[], int start, int end) {					// 自定义函数qusort()

    int i, j;												// 定义变量为基本整型
    i = start;												// 将每组首个元素赋给i
    j = end;												// 将每组末尾元素赋给j
    s[0] = s[start];										// 设置基准值
    while (i < j){
        while (i < j && s[0] < s[j])
            j--;											// 位置左移
        if (i < j){
            s[i] = s[j];									// 将s[j]放到s[i]的位置上
            i++;											// 位置右移
        }
        while (i < j && s[i] <= s[0])
            i++;											// 位置右移
        if (i < j){
            s[j] = s[i];									// 将大于基准值的s[j]放到s[i]位置
            j--;											// 位置右移
        }
    }
    s[i] = s[0];												// 将基准值放入指定位置
    if (start < i)
        qusort(s, start, j - 1);								// 对分割出的部分递归调用函数qusort()
    if (i < end)
        qusort(s, j + 1, end);
}

int main(int argc, char const *argv[]) {
    int a[11], i;												// 定义数组及变量为基本整型
    printf("请输入10个数:\n");
    for (i = 1; i <= 10; i++)
        scanf("%d", &a[i]);										// 从键盘中输入10个要进行排序的数
    qusort(a, 1, 10);											// 调用qusort()函数进行排序
    printf("排序后的顺序是:\n");
    for (i = 1; i <= 10; i++)
        printf("%5d", a[i]);										// 输出排好序的数组
	printf("\n");
}

示例结果:

$ gcc ex061.c -o demo
$ ./demo
请输入10个数:
45
12
76
2
45
16
34
61
24
90
排序后的顺序是:
    2   12   16   24   34   45   45   61   76   90

案例ex62: 选择排序

1 题目

功能:选择排序 描述:利用选择排序进行将数组序列从小到大排序

2 思路

选择排序的基本算法是从待排序的区间中经过选择和交换后选出最小的数值存放到a[0]中,再从剩余的未排序区间中经过选择和交换后选出最小的数值存放到a[1]中,a[1]中的数字仅大于a[0],依此类推,即可实现排序

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:选择排序
描述:利用选择排序进行将数组序列从小到大排序
**/

int main(int argc, char const *argv[]) {
    int i, j, t, a[11];						//定义变量及数组为基本整型
    printf("请输入10个数:\n");
    for (i = 1; i < 11; i++)
        scanf("%d", &a[i]);					//从键盘中输入要排序的10个数字
    for (i = 1; i <= 9; i++)
        for (j = i + 1; j <= 10; j++)
            if (a[i] > a[j]) {			//如果后一个数比前一个数大则利用中间变量t实现俩值互换
		        t = a[i];
		        a[i] = a[j];
		        a[j] = t;
    }
    printf("排序后的顺序是:\n");
    for (i = 1; i <= 10; i++)
        printf("%5d", a[i]);				//将排好序的数组输出
	    printf("\n");
}

示例结果:

$ gcc ex062.c -o demo
$ ./demo
请输入10个数:
23
51
34
15
45
72
2
5
33
90
排序后的顺序是:
    2    5   15   23   33   34   45   51   72   90

案例ex63: 归并排序

1 题目

功能:归并排序 描述:利用归并排序进行将数组序列从小到大排序

2 思路

归并排序两个或多个有序记录序列合并成一个有序序列 一次对两个有序记录序的归并称为二路归并排序,也有三路归并排序及多路归并排序 下面代码给出的是二路归并排序,基本方法 (1)将n个记录看成是n介长度为1的有序子表 (2)将两两相邻的有序壬莓4行归并 (3)垂复轨行步骤(2) 直至归并成一个长度为 L 的有序表

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:归并排序
描述:利用归并排序进行将数组序列从小到大排序
**/

void merge(int r[], int s[], int x1, int x2, int x3) { // 实现一次归并排序函数
    int i, j, k;
    i = x1; 										// 第一部分的开始位置
    j = x2 + 1; 										// 第二部分的开始位置
    k = x1;
    while ((i <= x2) && (j <= x3))				// 当i和j都在两个要合并的部分中
        if (r[i] <= r[j]){						// 筛选两部分中较小的元素放到数组s中
        s[k] = r[i];
        i++;
        k++;
    } else {
        s[k] = r[j];
        j++;
        k++;
    }
    while (i <= x2)								// 将x1~x2范围内的未比较的数顺次加到数组r中
        s[k++] = r[i++];
    while (j <= x3)								// 将x2+1~x3范围内的未比较的数顺次加到数组r中
        s[k++] = r[j++];
}

void merge_sort(int r[], int s[], int m, int n) {
    int p;
    int t[20];
    if (m == n)
        s[m] = r[m];
    else
    {
        p = (m + n) / 2;
        merge_sort(r, t, m, p);
            									// 递归调用merge_sort函数将r[m]~r[p]归并成有序的t[m]~t[p]
        merge_sort(r, t, p + 1, n);				// 递归调用merge_sort函数将r[n+1]~r[n]归并成有序的t[p+1]~t[n]*/
        merge(t, s, m, p, n); 					// 调用函数将前两部分归并到s[m]~s[n]
    }
}

int main(int argc, char const *argv[]) {
    int a[11];
    int i;
    printf("请输入10个数:\n");
    for (i = 1; i <= 10; i++)
        scanf("%d", &a[i]);
    merge_sort(a, a, 1, 10);					 // 调用merge_sort函数进行归并排序
    printf("排序后的顺序是:\n");
    for (i = 1; i <= 10; i++)
        printf("%5d", a[i]);
	    printf("\n");
}

示例结果:

$ gcc ex063.c -o demo
$ ./demo
请输入10个数:
34
12
64
23
98
45
18
52
1
7
排序后的顺序是:
    1    7   12   18   23   34   45   52   64   98

案例ex64: 二分查找

1 题目

功能:二分查找 描述: 使用二分查找特定关键字元素 用户输入有序数组的关键字后,给定要查找的关键字,看是否存在于有序数组中

2 思路

二分査找就是折半查找 其基本思想:首先选取表中间位置的记录,将其关键字与给定关键字key进行比较,若相等,则査找成功; 若key值比该关键字值大,则要找的元素一定在右子表中,则继续对右子表进行折半査找; 若key值比该关键字值小,则要找的元素一定在左子表中,继续对左子表进行折半査找; 按照上述递推,直到查找成功或查找失败。

3 代码

#include <stdio.h>
#include <stdlib.h>

/**
功能:二分查找
描述:
	使用二分查找特定关键字元素
	用户输入有序数组的关键字后,给定要查找的关键字,看是否存在于有序数组中
**/

void binary_search(int key, int a[], int n) { // 自定义函数binary_search
    int low, high, mid, count = 0, count1 = 0;
    low = 0;
    high = n - 1;
    while (low < high) {							// 当查找范围不为0时执行循环体语句

        count++;								// count记录查找次数
        mid = (low + high) / 2;						// 求出中间位置
        if (key < a[mid])							// 当key小于中间值
            high = mid - 1;						// 确定左子表范围
        else if (key > a[mid])						// 当key大于中间值
            low = mid + 1;							// 确定右子表范围
        else if (key == a[mid]) {					// 当key等于中间值证明查找成功

            printf("查找成功!\n查找 %d 次!a[%d]=%d", count, mid, key);
											// 输出查找次数及所查找元素在数组中的位置
            count1++;							// count1记录查找成功次数
            break;
        }
    }
    if (count1 == 0)								// 判断是否查找失败
        printf("查找失败!");							// 查找失败输出no found
}

int main(int argc, char const *argv[]) {
    int i, key, a[100], n;
    printf("请输入数组的长度:\n");
    scanf("%d", &n);								// 输入数组元素个数
    printf("请输入数组元素:\n");
    for (i = 0; i < n; i++)
        scanf("%d", &a[i]);							// 输入有序数列到数组a中
    printf("请输入你想查找的元素:\n");
    scanf("%d", &key);								// 输入要查找的关键字
    binary_search(key, a, n);						// 调用自定义函数
	printf("\n");
}

示例结果:

$ gcc ex064.c -o demo
$ ./demo
请输入数组的长度:
10
请输入数组元素:
1
4
6
9
12
46
78
90
102
122
请输入你想查找的元素:
102
查找成功!
查找 3 次!a[8]=102

案例ex65: 分块查找

1 题目

功能:分块查找 描述:利用分块查找的思想,将指定的数据项查找出来

2 思路

分块查找是折半查找和顺序查找的一种改进方法, 分块查找由于只要求索引表是有序的, 对块内节点没有排序要求, 因此特别适合于节点动态变化的情况。

要求将待査的元素均匀地分成块, 块间按大小排序, 块内不排序, 所以要建立一个块的最大(或最小)关键字表, 称为索引表。

本例子中将给出的15个数按关键字大小分成了3块, 这15个数的排列是一个有序序列, 也可以给出无序序列, 但必须满足分在第一块中的任意数都小于第二块中的所有数, 第二块中的所有数都小于第三块中的所有数。

当要査找关键字为key的元素时, 先用顺序查找在已建好的索引表中査出key所在的块中, 再在对应的块中顺序查找key, 若key存在, 则输出其相应位置, 否则输出提示信息。

3 代码

#include <stdio.h>
#include <stdlib.h>


/**
功能:分块查找
描述:利用分块查找的思想,将指定的数据项查找出来
**/

struct index {											// 定义块的结构
    int key;												// 存放块内最大值
    int start;											// 存放块内起始值
    int end;												// 存放块内结束值
} index_table[4]; 									// 结构体数组

int block_search(int key, int a[]) { 					// 自定义分块查找

    int i, j;
    i = 1;
    while (i <= 3 && key > index_table[i].key)			// 确定在那个块中
        i++;
    if (i > 3)																// 大于分得的块数, 则返回0
        return 0;
    j = index_table[i].start; 								// j等于块范围的起始值
    while (j <= index_table[i].end && a[j] != key)		// 在确定的块内进行查找
        j++;
    if (j > index_table[i].end)									// 如果大于块范围的结束值, 则说明没有要查找的数,j置0
        j = 0;
    return j;
}

int main(int argc, char const *argv[]) {

    int i, j = 0, k, key, a[16];
    printf("请输入15个数:\n");
    // 输入由小到大的15个数,由于是测试数据,那么必须要保证在块之间是整体有序的,块内无序
    for (i = 1; i < 16; i++)
        scanf("%d", &a[i]);								
    for (i = 1; i <= 3; i++) {
        index_table[i].start = j + 1; 					// 确定每个块范围的起始值
        j = j + 1;
        index_table[i].end = j + 4; 					// 确定每个块范围的结束值
        j = j + 4;
        index_table[i].key = a[j]; 						// 确定每个块范围中元素的最大值
    }
    printf("请输入你想查找的元素:\n");
    scanf("%d", &key); 									// 输入要查询的数值
    k = block_search(key, a); 							// 调用函数进行查找
    if (k != 0)	
        printf("查找成功, 其位置是:%d\n", k);				// 如果找到该数, 则输出其位置
    else
        printf("查找失败!");								// 若未找到则输出提示信息
}

示例结果:

$ gcc ex065.c -o demo
$ ./demo
请输入15个数:
1
4
6
8
10
14
16
19
21
25
29
30
31
34
40
请输入你想查找的元素:
29
查找成功, 其位置是:11

案例ex66: 哈希查找

1 题目

功能:哈希查找 描述: 哈希表长度 11 哈希函数 (key)=key%11 采用线性探测再散列的方法处理冲突

2 思路

哈希函数简要介绍

哈希函数的构造方法

哈希函数的构造方法常用的有5种,分别是数字分析法、平方取中法、分段叠加、伪随机数法和余数法,其中余数法比较常用。 例子中已给出哈希函数,按照给出的哈希函数进行了构造

避免哈希冲突的方法

分别有开放定址法(包括线性探测再散列和二次探测再散列入、链地址法、再哈希法和建立公共溢出区 开放定址法中的线性探测再散列比较常用,该方法的特点是在冲突发生时,顺序查看表中的下一单元,直到找出一个空单元或査遍全表。

3 代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define Max 11
#define N 8

/**
功能:哈希查找
描述:
	哈希表长度 11
	哈希函数 (key)=key%11
	采用线性探测再散列的方法处理冲突
**/

int hashtable[Max];

int func(int value) {
    return value % Max; 							// 哈希函数
}

int search(int key) { 								// 自定义函数实现哈希查询

    int pos, t;
    pos = func(key); 								// 哈希函数确定出的位置
    t = pos;										// t存放确定出的位置
    while (hashtable[t] != key && hashtable[t] !=  - 1) {
		// 如果该位置上不等于要查找的关键字且不为空
        t = (t + 1) % Max; 							// 利用线性探测求出下一个位置
        if (pos == t)
			// 如果经多次探测又回到原来用哈希函数求出的位置则说明要查找的数不存在
            return  - 1;
    }
    if (hashtable[t] ==  - 1)						// 如果探测的位置是-1则说明要查找的数不存在
        return -1;
    else
        return t;
}

void creathash(int key) {							// 自定义函数创建哈希表

    int pos, t;
    pos = func(key);								// 哈希函数确定元素的位置
    t = pos;
    while (hashtable[t] !=  - 1) {
		// 如果该位置有元素存在则进行线性探测再散列
        t = (t + 1) % Max;
        if (pos == t) {
        	// 如果冲突处理后确定的位置与原位置相同则说明哈希表已满
            printf("哈希表已满\n");
            return ;
        }
    }
    hashtable[t] = key;								// 将元素放入确定的位置
}

int main(int argc, char const *argv[]) {

    int flag[50];
    int i, j, t;
    for (i = 0; i < Max; i++)
        hashtable[i] =  - 1;				// 哈希表中初始位置全置-1
     										
    for (i = 0; i < 50; i++)
        flag[i] = 0;
     										// 50以内所有数未产生时均标志为0
    srand((unsigned long)time(0)); 			// 利用系统时间做种子产生随机数
    i = 0;
    printf("建立 Hash 表: \n");
    while (i != N) {
        t = rand() % 50; 					// 产生一个50以内的随机数赋给t
        if (flag[t] == 0) {						// 查看t是否产生过
            creathash(t);						// 调用函数创建哈希表
            printf("%2d:", t); 					// 将该元素输出
            for (j = 0; j < Max; j++)
                printf("(%2d) ", hashtable[j]);
             									// 输出哈希表中内容
            printf("\n");
            flag[t] = 1; 						// 将产生的这个数标志为1
            i++;								// i自加
        }
    }
    printf("请输入你想查找的元素:");
    scanf("%d", &t); 							// 输入要查找的元素
    if (t > 0 && t < 50) {
        i = search(t);							// 调用search进行哈希查找
        if (i !=  - 1)
            printf("查找成功!其位置是:%d\n", i);	// 若查找到该元素则输出其位置
        else
            printf("查找失败!");					// 未找到输出提示信息
    }
    else
        printf("输入有误!");
}

示例结果:

$ gcc ex066.c -o demo
$ ./demo
建立 Hash 表:
 7:(-1) (-1) (-1) (-1) (-1) (-1) (-1) ( 7) (-1) (-1) (-1)
22:(22) (-1) (-1) (-1) (-1) (-1) (-1) ( 7) (-1) (-1) (-1)
16:(22) (-1) (-1) (-1) (-1) (16) (-1) ( 7) (-1) (-1) (-1)
29:(22) (-1) (-1) (-1) (-1) (16) (-1) ( 7) (29) (-1) (-1)
44:(22) (44) (-1) (-1) (-1) (16) (-1) ( 7) (29) (-1) (-1)
37:(22) (44) (-1) (-1) (37) (16) (-1) ( 7) (29) (-1) (-1)
 3:(22) (44) (-1) ( 3) (37) (16) (-1) ( 7) (29) (-1) (-1)
 9:(22) (44) (-1) ( 3) (37) (16) (-1) ( 7) (29) ( 9) (-1)
请输入你想查找的元素:9
查找成功!其位置是:9

最后

在这里给大家准备了几百本的互联网技术类书籍,需要的来下载吧!mp.weixin.qq.com/s/KAYLiHFc5… 有任何问题,欢迎随时交流