作业标题
字符串左旋
作业内容
实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
2.解题思路
存储字符串需要用数组,最大的问题是如何左旋?
1.把要第一个元素先存起来,把后面的依次前移,第一个元素放原来最后面。左旋几次,就执行几次。
2.利用复制函数,先把后面的字符串元素存起来,再把需要左旋的前面的元素存到后面。
3.如果是左旋3位:先将要左旋的前三个家伙逆序(CBADEFG),然后将后半段也逆序(CBAGFED),最后整体逆序(DEFGABC)即可。
3.具体代码
#include <stdio.h>
#include <assert.h>
void reverse_arr(char *src, int k)// 函数用于反转字符串数组src中的前k个字符
{
int tmp;
assert(src != NULL);// 断言src不为空指针
if(k<=0){
return 0;
}
while (k--)// 保存当前字符
{
char *cur = src; // 创建临时指针指向字符串的开始
tmp = *cur; // 保存当前字符
while (*(cur+1) != '\0')// 循环移动字符串中的字符,直到到达字符串的末尾
{
*cur = *(cur+1);// 将下一个字符移动到当前位置
cur ++;// 移动指针到下一个字符
}
*cur = tmp;// 将保存的字符放到字符串的末尾
}
}
int main()
{
char arr[] = "ABCDE";
int k = 0;
scanf("%d",&k);
reverse_arr(arr, k);
printf("%s\n", arr);
return 0;
}
打印:
代码讲解:
-
先进入main函数,定义arr字符数组,并存入需要左旋的字符串。
-
定义一个整型常量k
-
输入这个整型常量,得到需要左旋的位数
-
进入
reverse_arr()
函数,将arr数组首地址和 左旋位数传给这个函数 -
定义一个整型变量tmp。
-
assert(src != NULL);
断言src不为空指针 -
if(k<=0){ return 0; }
如果输入的是非正数,那么就停止
reverse_arr()
函数。 -
进入while循环,循环进行k次。
-
char *cur = src;
创建临时指针指向字符串的开始 -
tmp = *cur;
保存当前字符 -
while (*(cur+1) != '\0')// 循环移动字符串中的字符,直到到达字符串的末尾 { *cur = *(cur+1);// 将下一个字符移动到当前位置 cur ++;// 移动指针到下一个字符 }
进入while循环,循环次数为
k-1
次。目的是将arr[1]到arr[4]的元素往前挪一位。 -
*cur = tmp;
将保存的字符放到字符串的末尾,也就是把arr[0]的元素给arr[4]。 -
然后继续循环,每次循环都会左旋一次。直到循环结束,返回main函数。
-
打印左旋后的arr数组。
改进一:
这个思路当然可以,但是一次一次转毕竟太麻烦,就不能一次到位么?
我们可以选择拼接法,一次到位:
void leftRound(char * src, int time)
{
int len = strlen(src);
int pos = time % len; //断开位置的下标
char tmp[256] = { 0 }; //更准确的话可以选择malloc len + 1个字节的空间来做这个tmp
strcpy(tmp, src + pos); //先将后面的全部拷过来
strncat(tmp, src, pos); //然后将前面几个接上
strcpy(src, tmp); //最后拷回去
}
代码讲解:
-
进入
leftRound
函数int len = strlen(src);
定义整型变量len来存放字符数组arr的长度。 -
int pos = time % len;
定义整型变量pos来存放断开位置的下标。如果我们想左旋2个字符,那么这里的pos就是2。
-
char tmp[256] = { 0 };
定义一个字符数组tmp来存放左旋的数组元素。 -
strcpy(tmp, src + pos);
先将后面的全部拷过来char *strcpy(char *dest, const char *src);
参数说明:
dest
:目标字符串指针,指向用于存储复制内容的内存空间。
src
:源字符串指针,指向要复制的字符串。返回值:
返回一个指向目标字符串 dest 的指针。
工作原理:
strcpy 函数的是将
src
指向的字符串复制到dest
指向的内存空间中,直到遇到src
字符串的结束符\0
。复制完成后,dest
指向的字符串将与src
指向的字符串相同。例如,我们想左旋2个字符,那么这里的strcpy函数是把arr[2]往后的字符串复制到tmp数组里面。
-
strncat(tmp, src, pos);
将前面几个接上char * strncpy(char *dest, const char *src, size_t n);
参数说明:
dest
:目标字符串指针,指向要追加内容的字符串。
src
:源字符串指针,指向要追加的字符串。
n
:要追加的最大字符数。返回值:
返回一个指向目标字符串 dest 的指针。
工作原理:
strncat 函数的工作原理是将
src
指向的字符串的前n
个字符追加到dest
指向的字符串的末尾。如果src
字符串的长度小于n
,则追加src
字符串的全部内容;如果src
字符串的长度大于或等于n
,则只追加前n
个字符。例如,我们想左旋2个字符,那么这里的strncpy函数是把arr[2]往前的字符串:
arr[0]
和arr[1]
复制到tmp数组后面。(反正左旋后就是在后面的) -
strcpy(src, tmp);
最后把tmp里的元素拷到src里面去。
改进二:
这个方法要用到一个数组形成的辅助空间,让人觉得有点不爽,还可以有更好的选择,例如ABCDEFG,左旋3次后变成DEFGABC,有一个特殊的操作方式:
先将要左旋的前三个家伙逆序(CBADEFG),然后将后半段也逆序(CBAGFED),最后整体逆序(DEFGABC)即可。这样只需要做数值交换即可,可以写一个函数帮我们完成局部逆序,代码如下:
void reverse_part(char *str, int start, int end) //将字符串从start到end这一段逆序
{
int i, j;
char tmp;
for (i = start, j = end; i < j; i++, j--)
{
tmp = str[i];
str[i] = str[j];
str[j] = tmp;
}
}
void leftRound(char * src, int time)
{
int len = strlen(src);
int pos = time % len;
reverse_part(src, 0, pos - 1); //逆序前段
reverse_part(src, pos, len - 1); //逆序后段
reverse_part(src, 0, len - 1); //整体逆序
}
代码讲解:
-
进入
leftRound
函数,int len = strlen(src);
定义整型变量len来存放字符数组arr的长度。 -
int pos = time % len;
定义整型变量pos来存放断开位置的下标。如果我们想左旋2个字符,那么这里的pos就是2。
-
reverse_part(src, 0, pos - 1);
进入reverse_part
函数。逆序前段。 -
定义整型变量i和j,定义字符变量tmp。
-
进入for循环,
i=0;
,j=pos-1;
。这个for循环干的其实就是先把数组第一个元素和(前端)最后一个元素互换位置,然后把第二个元素和(前端)最后第二个元素互换位置,依此类推。
-
reverse_part(src, pos, len - 1);
逆序后段。 -
进入for循环,
i=pos;
,j=len-1;
。这个for循环干的其实就是先把数组(后端)第一个元素和最后一个元素互换位置,然后把(后端)第二个元素和最后第二个元素互换位置,依此类推。
-
reverse_part(src, 0, len - 1);
整体逆序 -
进入for循环,
i=0;
,j=len-1;
。这个for循环干的其实就是先把数组第一个元素和最后一个元素互换位置,然后把第二个元素和最后第二个元素互换位置,依此类推。
环,i=pos;
,j=len-1;
。这个for循环干的其实就是先把数组(后端)第一个元素和最后一个元素互换位置,然后把(后端)第二个元素和最后第二个元素互换位置,依此类推。
-
reverse_part(src, 0, len - 1);
整体逆序 -
进入for循环,
i=0;
,j=len-1;
。这个for循环干的其实就是先把数组第一个元素和最后一个元素互换位置,然后把第二个元素和最后第二个元素互换位置,依此类推。