写了那么久的知识点梳理,今天来写点自己觉得不错的练习题来分享,顺便来巩固自己的知识点,和加强题型的解决方法的记忆。今天给大家带来的有数组的找数字题目,以及场景找凶手的题目,下面让我们来看看今天的第一道题目。
有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。要求:时间复杂度小于O(N)。
大家看到题目是否有了自己的思路了呢?下面我展示一下答案。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int Find_Num(int p[][4], int i, int j, int key) { int x = 0; int y = j - 1; while (x < i && y < j) { if (p[x][y] > key) { y--; } if (p[x][y] < key) { x++; } if (p[x][y] == key) { return p[x][y]; } } return 0; } int main() { int ret = 0; int arr[4][4] = { {1,2,3,4},{5,6,7,8,},{11,12,13,14},{22,33,44,55} }; int key = 0; printf("请输入你想查找的数字\n"); scanf("%d", &key); ret=Find_Num(arr, 4, 4,key); if (ret != 0) { printf("数组中存在这个数字\n"); } else { printf("数组中不存在这个数字\n"); } return 0; }
首先我们需要借助图来解析我的代码思路
首先我们从题目知道,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,那我们先按照题目设一些值。首先我们不能从第一行第一列一个一个往下找吧,不然这样就太耗费时间了,就不符合要求了。那怎么办呢,那我们就要想到一个奇妙的位置那就是右上角的位置,为什么要这样呢?首先在这个位置上我们首先对右上角的值,例如图中的4与Key进行对比,因为key大于4,那么就是要找比4大的数,也就是往下一行进行查找,因为在4的左边的数比4还要小。这样我们就可以排除掉一行的值,在下来也类似,8比Key大那么往小里找,8往下的值比8还大,那么舍弃掉这一列。直到找到Key与p[x][y]相等为止。
所以代码中就有这么些代码:
if (p[x][y] > key) { y--; } if (p[x][y] < key) { x++; } if (p[x][y] == key) { return p[x][y]; }
接下来我们看第二题:
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。
编写一个函数找出这两个只出现一次的数字。
例如:
有数组的元素是:1,2,3,4,5,1,2,3,4,6
只有5和6只出现1次,要找出5和6.
看过我前面的文章的是不是觉得似曾相识呢?与前面不同的是,这个题目要找的是两个数这么一看,大家能否有思路解决这道题呢。下面先展示参考答案。
FindNum(int arr[], int* num1, int* num2,int n) { int i = 0; int tem=0; for (i = 0; i < n; i++) { tem ^= arr[i]; } int k = 0; for (i = 0; i < 32; i++) { if ((tem >> i) & 1 == 1) k = i; } for (i = 0; i < n; i++) { if (((arr[i] >> k) & 1) == 1) { *num1^= arr[i]; } else { *num2^=arr[i]; } } } int main() { int arr[10] = { 1,2,3,4,76,4,3,2,1,45 }; int sz = sizeof(arr) / sizeof(arr[2]); int num1 = 0; int num2 = 0; FindNum(arr, &num1, &num2, sz); printf("%d %d", num1, num2); return 0; }
这道题要解释起来,我们还是得画图来理解:
解析:首先我们如图解所说要将数分成两部分,然后需要异或操作符。我们前面将过两个公式不知道大家是否记得:
a^a=0;
0^a=a;
这两个公式就可以让我们区分一个单次出现过的数,但是这里如何将数组的数区分成两部分呢?首先我们将数组中的所有数进行异或,那么我们最终异或得到的值存到tem中,如上方:这时tem=76^45 ,那么大家仔细一想异或操作符,相异为1,这是不是就是两个数的区别了,那么这时我们就可以想一下能否从这个地方找到方法区分两个数和其他数两两分到同一部分呢。这里我们就可以从32位比特上找到tem中为1的那一个比特位,(可看下图理解)然后将数组中的所有数为1的分为一部分,为0的分为一部分,这样我们就可以将数组的数分为两部分,然后不断异或就可以得到两个数,这既是那两个数组中只出现一次的数。如图:
代码解释:
将tem进行异或后,用tem借助>>操作符找到为1的比特位,然后再用>>和^操作符进行分类,最后得到两个数,打印即可。
最后一道场景题:
日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。
以下为4个嫌疑犯的供词:
A说:不是我。
B说:是C。
C说:是D。
D说:C在胡说
已知3个人说了真话,1个人说的是假话。
现在请根据这些信息,写一个程序来确定到底谁是凶手。
根据这个场景各位能否想到方法来找出凶手呢?下面展示参考代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { char Killer; for (Killer = 'a'; Killer <= 'd'; Killer++) { if (((Killer != 'a') + (Killer =='c') + (Killer == 'd') + (Killer != 'd')) == 3) { break; } } printf("%c是凶手\n", Killer); return 0; }
这么一看是否发现其实代码很简洁,但是这道题却很考验对代码的熟练和思维。
从题目中我们知道四个人中有一个人是凶手,所有四个人中有三个人说的话是真的,只有凶手说假的话。那么我们就需要思考出怎么在代码中确定什么时候是3个人说真话,1个人说假话得场景。
代码中我们就运用到判断语句,我们知道在C语言中判断语句中,真为1,假为0,这时我们可以根据四个人讲的话作为条件,进行判断,最后相加起来,当为3时,就是凶手,这时我们就需要代码轮番将四个人假设为凶手,那我们也要知道字符++,也是根据ASCII码值来储存,++就会得到下一个字符了。这么一解释,相信各位就已经能看懂代码了吧。
文章已到篇尾,能看到这里也谢谢您的支持了,可以的话记得三连哟。