导言:
本文将介绍几种不同字符串左旋的方法,先给出题目概要:
实现一个字符串左旋程序,可以左旋字符串中的n个字符。
输入:输入一个字符串和要左旋的个数n。
输出:左旋后的字符串。
例如:
输入:ABCD 1
输出:BCDA
输入:ABCD 2
输出:CDAB
正文:
方法1:
总体思想就是,先让一个字符左旋到最右侧,再利用循环执行n次,即可到达目标。
具体实现:
首先,通过strlen函数获取字符数组arr的长度,保存在变量len中。
然后,使用一个外层循环,循环n次。每次循环中,将数组arr的第一个元素保存在变量temp中。
接下来,使用一个内层循环,从数组arr的第一个元素开始,将每个元素都向左移动一位,即将当前元素的下一个元素的值赋给当前元素。
最后,将之前保存的第一个元素temp赋值给数组arr的最后一个元素,完成一次循环后,数组arr中的元素向左移动了一位。
重复以上步骤,直到外层循环执行n次,即完成了将字符数组arr中的元素向左移动n位的操作。
代码如下:
#include<stdio.h>
#include<string.h>
void leftRound1(char arr[], int n) {
int len = strlen(arr);
n = n % len; // 长度为5的情况下,旋转6、11、16...次相当于1次,
//7、12、17...次相当于2次,以此类推。
for (int i = 0; i < n; i++) {
//执行n次的单次平移
char temp = arr[0];
for (int j = 0; j < len - 1; j++) {
//单次平移
arr[j] = arr[j + 1];
}
arr[len - 1] = temp;
}
}
int main() {
int n;
char arr[100];
scanf("%s%d", arr, &n);
leftRound1(arr, n);
int len = strlen(arr);
for (int i = 0; i < len; i++) {
printf("%c", arr[i]);
}
return 0;
}
这个思路当然可以,但是一次一次转毕竟太麻烦,能不能实现一次到位?
方法二:
我们可以选择拼接法,一次到位。即利用拷贝函数将要左旋的字符串后面剩下的所有字符拷贝到一个新的数组中,再利用追加函数将需要左旋的字符追加到新的数组之后,最后再全部拷贝回原数组即可完成左旋。由于主函数都一样,这里就只给出函数的实现部分。
具体实现:
首先,通过strlen函数获取字符数组src的长度,保存在变量len中。
然后,计算出断开位置的下标,即time对len取模的结果,保存在变量pos中。
接下来,创建一个临时字符数组tmp,长度为256,并将每个元素初始化为0。
然后,使用strcpy函数将src数组中从pos位置开始的所有元素拷贝到tmp数组中,实现将后面的元素全部拷贝到tmp数组中。
接着,使用strncat函数将src数组中从0位置开始的pos个元素拼接到tmp数组的末尾,实现将前面的元素接在后面。
最后,使用strcpy函数将tmp数组中的元素拷贝回src数组,完成将字符数组src中的元素向左循环移动time次的操作。
整体思路是将src数组中的元素分为两部分,后面的部分从pos位置开始拷贝到tmp数组中,前面的部分从0位置开始拼接到tmp数组的末尾,最后将tmp数组中的元素拷贝回src数组,实现循环移动的效果。
代码如下:
void leftRound2(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); //最后拷回去
}
这个方法还要用到一个数组形成的辅助空间,有没有连辅助空间都不需要的呢?
方法三:
先举个例子ABCDEFG,左旋3次后变成DEFGABC,其实我们只需先将要左旋的前三个家伙逆序(CBADEFG),然后将后半段也逆序(CBAGFED),最后整体逆序(DEFGABC)即可。由于逆序需要重复操作,我们也可以再封装一个逆序函数,这样只需要做数值交换即可,和上面一样只给出函数部分。
具体实现:
reverse_part(char* str, int start, int end)实现:
首先,定义两个变量i和j,分别初始化为start和end。
然后,使用一个循环,循环条件为i小于j。在每次循环中,递增i,递减j。
在循环中,通过一个临时变量tmp,将str[i]的值保存起来。
然后,将str[i]的值替换为str[j]的值。
最后,将str[j]的值替换为tmp,即将之前保存的str[i]的值赋给str[j]。
重复以上步骤,直到i不再小于j,即完成了将字符串str中从索引start到索引end的部分逆序的操作。
整体思路是利用两个指针i和j,从字符串的两端向中间遍历,将对应位置的字符进行交换,实现逆序操作。
void leftRound3(char* src, int time)实现:
首先,通过strlen函数获取字符数组src的长度,保存在变量len中。
然后,计算出断开位置的下标,即time对len取模的结果,保存在变量pos中。
接下来,调用reverse_part函数,将字符数组src中从索引0到索引pos-1的部分进行逆序操作,实现逆序前段。
然后,调用reverse_part函数,将字符数组src中从索引pos到索引len-1的部分进行逆序操作,实现逆序后段。
最后,调用reverse_part函数,将整个字符数组src进行逆序操作,实现整体逆序。
整体思路是将字符数组src分为三段:前段、后段和整体。先对前段进行逆序操作,然后对后段进行逆序操作,最后对整个字符串进行逆序操作。通过这样的操作,实现了将字符数组src中的元素向左循环移动time次的功能。
代码如下:
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 leftRound3(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); //整体逆序
}
总结:
字符串左旋也是一个比较经典的题目,这三种方法一步步优化,既要求了我们要通过测试,也要求了我们要有优化程序的能力。