目录
1.单身狗1
1.1 题目
1.2 分析推理
1.3 代码实现
2.单身狗2
2.1 题目
2.2 分析推理
2.3 代码实现
3.字符串左旋
3.1 题目
3.2 分析推理
3.3 代码实现
3.3.1 方法一
3.3.2 优化一
3.3.2.1 思路分析
3.3.2.2 strcpy函数和strncat函数
3.3.2.3 代码实现
3.3.3 优化二
3.3.3.1 思路分析
3.3.3.2 代码实现
4.字符串旋转结果
4.1 题目
4.2 分析推理
4.3 代码实现
1.单身狗1
1.1 题目
在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。
例如:
数组中有:1 2 3 4 5 1 2 3 4,只有5出现一次,其他数字都出现2次,找出5
1.2 分析推理
这里我们借助一个操作符:^ ----按位异或--->对应的二进制位上相同则为0,相异则为1
满足交换律;eg:1^2 ^1=2
类似消消乐一样
那我们顺着这个思路往下,就可以将整个数组的元素都^ ,最后的结果就是我们要找的单身狗
1.3 代码实现
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = 0;
for (int i = 0; i < sz; i++)
{
ret ^= arr[i];
}
printf("单身狗是%d", ret);
return 0;
}
2.单身狗2
2.1 题目
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。编写一个函数找出这两个只出现一次的数字。
例如:
有数组的元素是:1,2,3,4,5,1,2,3,4,6
只有5和6只出现1次,要找出5和6.
2.2 分析推理
我们发现刚刚把里面所有的数字都异或一遍,利用异或的方法不成立了。如果有两个数字都只出现了一次,那么如此得到的应该是两个数异或的结果。
例子中异或的结果也就是5^ 6=0011
在单身狗1的基础上,我们再来想想办法:
首先这个结果肯定不是0(要不然就全都配对了),所以里面一定至少一位是一。找出值为1的一位,以这一位的值将结果分为两组。这样的话,就可以延用单身狗1的方法了。
例如1 2 3 4 1 2,异或完的结果应该是3^4得到的111,那么随便找一位就行了。例如找最低位,那么这最低位是1的有1 3 1,最低位是0的有2 4 2,由于是利用异或结果为1的某一位分的组,所以两个待查询数字一定分别在两组中。所以再找两个变量,分别异或两组数,即可找到这两个数。
2.3 代码实现
#include<stdio.h>
void findnum(int arr[], int sz, int* ret1, int* ret2)
{
//整体异或
int ret = 0;
for (int i = 0; i < sz; i++)
{
ret ^= arr[i];
}
//找到何处为1
int pos = -1;//位置上的值不是0就是1,-1可以避免影响
//从右往左,遇到1,就记录1的位置
for (int i = 0; i < 32; i++)
{
if ((ret & 1) == 1)//&---按位与--->全1则为1,有0则0
{
pos = i;//记录1所在位置
break;
}
}
//分组分别异或
for (int i = 0; i < sz; i++)
{
if ((arr[i] >> pos) & 1)//右移pos位找到pos位上的值按位与
{
*ret1 ^= arr[i];
}
else
{
*ret2 ^= arr[i];
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
//返回值
int ret1 = 0;
int ret2 = 0;
findnum(arr, sz, &ret1, &ret2);//传址
printf("单身狗是%d,%d\n", ret1, ret2);
return 0;
}
3.字符串左旋
3.1 题目
实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
3.2 分析推理
设计循环使其可以旋1次,然后让他执行n次是一个最简单的思路:一个一个慢慢挪
我们按照下标顺序一个一个将第一个下标对应的字符现存放到tmp空间,再将tmp中存放的字符放到数组最后一个下标对应的位置,以此类推:
但是,值得注意的是多次左旋结果可能相同
例如:ABCD---4个字符--->左旋1,5,9......等次数的结果一样--->BCDA
3.3 代码实现
3.3.1 方法一
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
void leftround(char* arr, int len, int time)
{
int tmp,i,j;
//左旋次数
time %= len;
//左旋循环趟数
for (i = 0; i < time; i++)
{
tmp = arr[0];//将第一个坐标对应的字符放到tmp空间
for (j = 0; j < len - 1; j++)//j+1<4-->j<3
{
//后面的覆盖到前面
arr[j] = arr[j + 1];
}
arr[j] = tmp;
}
}
int main()
{
char arr[] = "ABCD";
int time = 0;
scanf("%d\n", &time);
int len = strlen(arr);
leftround((char*)arr, len, time);
printf("%s\n", arr);
return 0;
}
3.3.2 优化一
3.3.2.1 思路分析
改进一:
这个思路当然可以,但是一次一次转毕竟太麻烦,就不能一次到位么?
当然可以,我们可以选择拼接法(需要借助库函数),一次到位:
我们先将移动k次之后的剩余的字符挪到tmp空间存放,再将移动k次涉及到的字符拷到tmp空间中刚刚字符的后面,最后拷到原数组中
3.3.2.2 strcpy函数和strncat函数
3.3.2.3 代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
void leftround(char* arr, int len, int time)
{
//左旋次数
time %= len;
char tmp[200] = {0};
strcpy(tmp, arr + time);//将不涉及左旋的字符全部拷到tmp
strncat(tmp, arr, time);//将涉及到左旋的字符拼接到前面
strcpy(arr, tmp);//最后拷回去原数组
}
int main()
{
char arr[] = "ABCD";
int time = 0;
scanf("%d\n", &time);
int len = strlen(arr);
leftround((char*)arr, len, time);
printf("%s\n", arr);
return 0;
}
3.3.3 优化二
3.3.3.1 思路分析
改进二:
方法二要用到一个数组形成的辅助空间,让人觉得有点不爽,还可以有更好的选择,例如ABCDEFG,左旋3次后变成DEFGABC,有一个特殊的操作方式:局部翻转
先将要左旋的前三个家伙逆序(翻转)ABCDEFG--->CBADEFG,然后将后半段也逆序(翻转)CBADEFG--->CBAGFED,最后整体逆序(翻转)CBAGEFD-->DEFGABC即可。这样只需要做数值交换即可,可以写一个函数帮我们完成局部逆序
3.3.3.2 代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
void resrve_part(char* arr, int start, int end)
{
//交换位置---翻转倒序
while (start < end)
{
char tmp = arr[start];
arr[start] = arr[end];
arr[end] = tmp;
start++;
end--;
}
}
void leftround(char* arr, int len, int time)
{
//左旋次数
time %= len;
resrve_part(arr, 0, time - 1);//翻转前半部分
resrve_part(arr, time, len-1);//翻转后半部分
resrve_part(arr, 0, len-1);//全部翻转
}
int main()
{
char arr[] = "ABCD";
int time = 0;
scanf("%d\n", &time);
int len = strlen(arr);
leftround((char*)arr, len, time);
printf("%s\n", arr);
return 0;
}
4.字符串旋转结果
4.1 题目
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:给定s1 =AABCD和s2 = BCDAA,返回1
给定s1=abcd和s2=ACBD,返回0.
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
4.2 分析推理
本题最简单的思路就是可以将所有旋转后的结果放到一个数组里,然后进行查找:利用左旋的方法每左旋一次就去对比是否一样(共左旋len次,之后左旋结果重复)
这里我们需要介绍一个用于比较不同的函数---strcmp函数
4.3 代码实现
4.3.1 方法一
#include<stdio.h>
#include<string.h>
int findnum(char* arr1, char* arr2, int len)
{
int i, j, tmp;
for (i = 0; i < len; i++)
{
tmp = arr1[0];
for (j = 0; j < len - 1; j++)
{
arr1[j] = arr1[j + 1];
}
arr1[j] = tmp;
if (strcmp(arr1, arr2) == 0)
{
return 1;
}
}
return 0;
}
int main()
{
char arr1[] = "AABCD";
char arr2[] = "ABDCA";
int len = strlen(arr1);
int ret = findnum((char*)arr1, (char*)arr2, len);
printf("%d", ret);
return 0;
}
4.3.2 优化
4.3.2.1 思路分析
但是方法一这种做法既不好操作,也太费事,但是这题有一个很简单的做法---拼接法(借助库函数):
其实AABCD无论怎么旋,旋转后的所有结果,都包含在了AABCDAABCD这个字符串里了。
所以做法很简单,只需要将原字符串再来一遍接在后面,然后找一找待查找的字符串是不是两倍原字符串的子集即可。
但是我们需要借助库函数实现
4.3.2.2 strcat函数和strstr函数
4.3.2.3 代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int findnum(char* arr1, char* arr2)
{
char tmp[200] = { 0 };
strcpy(tmp, arr1);//初始化字符串拷到tmp
strcat(tmp, arr1);//自己拼接自己
return strstr(tmp, arr2) != NULL;//能否找到
}
int main()
{
char arr1[] = "AABCD";
char arr2[] = "DCBAA";
int ret = findnum((char*)arr1, (char*)arr2);
printf("%d", ret);
return 0;
}
到这里,第二弹练习就结束了
PS:小江目前只是个新手小白。欢迎大家在评论区讨论哦!有问题也可以讨论的!
如果对你有帮助的话,记得点赞👍+收藏⭐️+关注➕