第8-9周:C语言数组算法练习 在这一周我学习了在数组中求最值求和基础和升级打乱数组的数据反转数组查找其中分基本查找二分查找分块查找哈希查找还学习了排序冒泡排序和选择排序除此之外我还练习了题目结合三大结构函数数组包括下列查找排序首先先回顾一下数组是一个什么东西。数组是一种容器可以用来存储同个数据类型的多个值。求最值1.需要定义数组2.定义变量maxmax的默认值一定是数组中存在的数据一般将索引0当作默认值3.然后利用循环遍历数组遍历数组得到的每一个元素与max比较。//1.定义数组 int num[] { 33,5,22,44,55 }; int len sizeof(num) / sizeof(int); //2.定义变量表示max int max num[0]; //3.利用循环遍历数组遍历数组得到每一个元素拿着遍历得到的元素与max比较 for (int i 1;i len;i) { if (num[i] max) { max num[i]; } } //4.输出打印 printf(%d\n, max);求和求和分为基础和升级二者的区别在于基础版可以数据重复但是升级版不可以所以应保证每一个元素只出现一次。无论是基础还是升级我们都用到随机数。如何得到随机数我们应设置种子再生成随机数。既然升级版数据不可以重复那在代码上二者的区别升级多了一步判断随机数存在数组中之前应判断刚生成的随机数是否已存在于数组中我们可以利用函数判断随机数是否在数组中存在。这里的代码只展示判断num与数组中的数据是否重复//设置种子生成10个 1到10 之间的随机数 srand(time(NULL)); //生成随机数 此时for后面的i在下面这意味着循环变量i不会每次循环都自动加1 for (int i 0;i len;) //i0时给arr[0]-arr[9]赋值 { int num rand() % 101 ; //把随机数num存入数组中但存入之前需要判断判断刚刚生成的随机数是否已经存在于当前的数组中 int flag contains(arr, len, num); if (!flag) //!flag: 如果flag为假则flag0,也就是不存在的意思 { arr[i] num; //把目前num存入数组第i个位置 i; //只有符合if里面的语句才会加1 }//判断num在函数中是否存在 //存在返回1 //不存在返回0 int contains(int arr[],int len,int num) { for (int i 0;i len;i) //i:依次表示数组中每一个索引 { //arr[i];依次表示数组中每一个数据 if (arr[i]num) { return 1; } } return 0; }打乱数组中的数据核心原理是遍历数组将当前遍历到的元素与数组中随机位置的一个元素进行交换。其中int index rand() % len;作用是生成随机下标。rand() 会生成一个很大的随机数通过 % len取余数可以将这个随机数限制在 0 到 len-1 的范围内即 0 到 4得到的就是一个随机的数组下标。//1.定义数组 int arr[] { 1,2,3,4,5, }; int len sizeof(arr) / sizeof(int); //2.遍历数组(用于打乱数据得到数组每一个元素让这个元素跟随机索引进行交换 //设置种子 srand(time(NULL)); for (int i 0;i len;i) { //获取一个随机索引随机数 //min;0 max;4 int index rand() % len; //rand是一个函数作用是生成一个很大的随机数所以我们要控制随机数的范围 //拿着i指向的元素跟index指向的元素进行交换 int temp arr[i]; arr[i] arr[index]; arr[index] temp; } //3.遍历数组用于打印输出省略...反转数组核心原理是双指针法首尾指针向中间靠拢。在反转数组中用到的循环结构是while。其中while (i j括号中是条件判断语句循环条件只要左指针还在右指针的左边就说明还没有相遇需要继续交换。一旦 i j相遇或者交叉说明整个数组已经反转完毕循环结束。如何判断用for还是while1.已知循环次数或循环范围用 for 循环更清晰。2. 如果是不知道循环次数或循环范围只知道结束条件用 while 循环更合适。//4.反转数组 int i 0; int j len-1; while (i j) //用for循环过于繁琐即使我们知道需要循环多少次len/2次 //但这种“左右两边同时移动”的逻辑中什么时候停取决于两边的相遇而不是单纯的要循环多少次 //并且如何选择for还是while //1.当知道循环次数或范围用for //2.不知道循环次数或范围用while / 而循环的终止条件依赖于多个变量动态变化时while比for更适合 { int temp num[i]; num[i] num[j]; num[j] temp; i; // 双指针向中间靠拢,如果不打这两步i和j不懂变成死循环还是在同样的位置互换 j--; } //5.遍历数组 printArr(num, len);查找基本查找二分查找插值查找分块查找哈希查找①基本查找顺序查找从头到尾遍历数组。核心思路从数组0索引开始依次往后查找。如果找到了就会返回数组中的索引。如果没找到就会返回-1。//1.定义数组 int arr[] { 23,52,34,89,16 }; int len sizeof(arr) / sizeof(int); //2.定义一个变量表示要查找的数据 int num 34; //3.调用函数查找数据 int indexorder(arr, len, num); //4.输出索引 printf(%d\n, index); return 0; } //函数作用查找数组中的数据 //返回值;数据所在的索引 int order(int arr[], int len, int num) { for(int i 0;i len;i) { if (arr[i] num) { return i; } } return -1; }②二分查找折半查找使用前提条件数组中的数据必须是有序可以是从小到大也可以是哦从大到小就是不可以是乱的。核心思路每次排除一半的查找范围1.定义min和max表示当前的查找范围2.定义midmid在min和max中间midminmax/23.如果要查找的数据在mid左边则min不变maxmid-1如果要查找的数据在mid右边则max不变minmid1//1.定义数组 int arr[] { 12,25,37,58,60,67,82,92 }; int len sizeof(arr) / sizeof(int); //2.定义一个变量表示要查找的数据 int num 82; //3.调用函数查找数据 int index binaryserach(arr, len, num); //4.输出索引 printf(%d\n, index); return 0; } //函数作用利用二分查找查找数据 //返回值数据在数组中的索引 //找到了, 就会返回数据对应的索引 //没找到就会返回-1 int binaryserach(int arr[], int len, int num) { //1.确定查找范围 int min 0; int max len - 1; //2.利用循环不断进行查找 while (min max) { //确定中间位置 int mid (minmax)/ 2; //进行比较此时min、mid、max都表示数组的索引 //而此时的num表示要查找的元素所以一定要拿arr[mid]跟num比较 //判断 if (arr[mid] num) //要查找的num在右边则max不变min为mid1 { min mid 1; } else if (arr[mid] num) //要查找的num在左边则min不变max为mid-1 { max mid - 1; } else { return mid; } //3.如果min大于max了表示数据不存在返回-1 return -1; }③插值查找使用前提条件数据有序且数据分布尽可能均匀一点它与二分查找操作差不多只要把mid的表达式换一下就好了midminkey-arr【min】/arr【max】-arr【min】*max-min它对于二分查找的优势效率比二分查找快但如果前提条件不符还会适得其反代码不展示与二分查找一样只需把mid的表达式换一下即可//确定中间位置 int mid min (key - arr[min]) / (arr[max] - arr[min]) * (max - min); //可以直接用num没必要多余定义key④分块查找核心思路先确定要查找的元素在哪一块然后在其块内挨个查找。如何分块分块原则• 块内无序每一个小块内部的数据可以是乱序的不需要排序。• 块间有序第 1 块中的最大值必须小于第 2 块中的最小值第 2 块的最大值必须小于第 3 块的最小值……以此类推。即整体趋势是递增的。一般块数的数量等于数据个数的开根号eg16个数据一般分为4块且需要注意分块中每一块的数据范围不可以有交集⑤哈希查找核心思路把数据存入时通过公式计算出一个位置哈希值查找时用同样的公式算出位置直接去那个位置取数据。它和另外四个查找不一样它不找数据而是直接算出数据的位置。排序算法冒泡排序选择排序①冒泡排序核心思路1.相邻的数据两两比较小的放前面大的放后面。大的放右小的放左2.第一轮循环结束最大的已经在最右边放在数组最右边。3.剩余数据继续循环排序。如果数组中有n个数据总共我们只要执行 n-1 轮代码即可。//2.利用冒泡排序将数组中的数据进行升序排序 //我们可以发现四轮下来只有条件判断语句那一部分变动了所以我们可以在套一个循环使它变得简便 一 for (int i 0;i len -1;i) // i:范围0,1,2,3 此时条件判断语句要写len-1防止索引越界 //外循环控制比较的轮数 //随着i的增加内循环范围越来越小 { //内循环控制每一轮的次数 //j:表示进行比较的元素下标 //每过一轮就会有一个最大的数排到末尾所以后面的数就不用比较了则-i for (int j 0;j len - 1-i;j) { //相邻元素二二比较小的在前大的在后 //j与j1比较 if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; } } }利用冒泡排序解决方法双重循环双重循环本质指内循环重复执行n次。内循环找到本次循环的较大值再把较大值放到最右边。外循环把这个动作执行n次。②选择排序核心思路1.索引0开始拿着每一个索引上的元素跟后面元素依次比较小的放前面大的放后面。2.第一轮循环结束最小值已经找到放在数组最左边3.第二轮循环从索引1开始依次比较。依次类推…//2.利用选择排序将数组中的数据进行升序排序 //第一轮 i0j1,2,3,4 // 第二轮i1j2,3,4 // 第三轮i2j3,4 // 第四轮i3j4 //i依次表示数组中每一个索引 for (int i 0;i len - 1;i) //ilen-1,防止索引越界 { //j依次表示i索引后面的每一个索引 for (int j i 1;j len;j) //假如第一轮比较结束它会直接将比索引0小的互换位置 { //判断的代码逻辑是 拿着 arr[i] 去和后面所有元素相比只要遇到比它小的当场就换 一轮下来最小的那个必定会通过多次交换“顶”到 i 的位置上。 if (arr[i] arr[j]) //如果成立则互换位置 { int temp arr[i]; arr[i] arr[j]; arr[j] temp; } } }冒泡排序VS选择排序核心动作相邻两两比较频繁交换 找最小/最大值一轮只换一次适用场景数据量极少 数据量极少算术练习回文数斐波那契数列水仙花数综合练习1.2.3.