一、引例 - 牛客网OJ题
为了更好地说明这个问题,我们以一道牛客网的题目作引例。题目链接贴在这里
当我们拿到这样的题目时的第一反应便是,将左旋的元素看作是一个部分,而将不改变的元素又视作另一个部分,在这二者独立的两个拆分的部分建立联系,并以此大做文章。
二、代码
各位看官,容我先卖个关子,我们先来思考一下,主函数里我们应该传递哪些形参变量过去呢,
有人会想,不容置喙地肯定传数组过去,没错!数组是必须的。数组长度,数组长度也应该要有,要靠数组长度控制循环啊!是的,很中肯,数组长度不可或缺。哎!还有左移的位数k,这个肯定会有的,是的没错,不过这回只说对了一半。不妨,我们先来看一个图。
我们不难发现当以"ABCD"为例,设len 为数组长度,循环k 次,和循环k % len次的效果是一致的,因此,不妨定义一个变量time=k % len来表示循环次数。
主函数部分代码如下:
int main()
{
char str[]="ABCD";
int k=0;
scanf("%d",&k);
int len=strlen(str);
int time=k%len;
LeftRound(str,time,len);
puts(str);
return 0;
}
一、双数组法
代码如下:
void LeftRound(char* str,int time,int len)
{
char tmp[10];
strcpy(tmp,str);
for(int i=time;i<len;i++)
str[i-time]=tmp[i];
for(int i=0;i<time;i++)
str[i+len-time]=tmp[i];
}
基本思路:
用 strcpy()复制出一个临时数组 tmp[ ],这是tmp[ ]数组和str[ ]数组内的内容一致,以第 k个元素为界,然后将左旋的元素看作是一个部分,而将不改变的元素又视作另一个部分,用tmp[ ]数组分别进行循环,并赋值数组str[ ],为了方便理解我们来看一个草图。
二、逐个挪动法
代码如下:
void LeftRound(char* str,int time,int len)
{
for(int i=0;i<time;i++)
{
int j=0;
char tmp=*str;
for(j=0;j<len-1;j++)
{
str[j]=str[j+1];
}
str[j]=tmp;
}
}
基本思路:
外层循环 i 控制循环次数,内层循环 j 控制数组内字符的移动,每次循环时 ,将数组的第一个元素赋值给临时变量 tmp ,然后内层循环使.得每一个数组往前移动填补空缺。在结束循环时,将原先赋给 tmp 的元素,还给数组并处在最后一个,这每次内层循环结束就完成了,一个元素左旋的任务。不过值得注意的是,在内层循环中,因为第一个元素没有参与循环。所以,只有len - 1个元素,结束循环的条件是,j < len -1。而不是我们熟悉的 j < len,防止造成数组越界。
三、三步旋转法
不过与上面不同,在我们写函数之前要写一个用于数组逆置的方法。
代码如下:
void Reverse(char* str,int start,int end)
{
while(start<end)
{
char tmp=str[start];
str[start]=str[end];
str[end]=tmp;
start++;
end--;
}
}
基本思路:
首先,我们除了数组之外我们还需要明确从哪里开始逆置,又在哪里结束逆置。因此,我们需要形参变量,起始下标 start ,和结束下标 end。在完成一次互换之后,start++,end--,使两个下标逐渐靠近,直到 start=end 结束循环同时完成逆置。
明确这个,我们来想一想如何实现函数呢?不妨我们再来看一个图。
先将第0个到第time -1 个元素逆置,再将第time 个到 len-1逆置,再整体完成逆置,结果就是我们所想要的结果。
代码如下:
void LeftRound(char* str,int time,int len )
{
Reverse(str,0,time-1);
Reverse(str,time,len-1);
Reverse(str,0,len-1);
}
四、库函数法
代码如下:
void LeftRound(char* str,int time ,int len)
{
char tmp[10];
strcpy(tmp,str+time);
strncat(tmp,str,time);
strcpy(str,tmp);
}
要读懂这几行代码,我们先对相关库函数做一个简单的了解。
strcpy( )
在C语言函数中
原型声明:
char *strcpy(char* dest, const char *src);
头文件:#include <string.h> 和 #include <stdio.h>
功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
strnact( )
在C语言函数中
原型声明:
char * strncat(char *dest, const char *src, size_t n);
头文件:#include <string.h> 和 #include <stdio.h>
主要功能是在字符串的结尾追加n个字符。
基本思路:
明确此我们再来回看那段代码:
void LeftRound(char* str,int time ,int len)
{
char tmp[10];
strcpy(tmp,str+time);
strncat(tmp,str,time);
strcpy(str,tmp);
}
因为,strcpy( )的作用是以地址媒介开始复制到另一个数组。因此,不妨将直接将第 k 个元素的地址传过去,这样第 k 个到第 len 个就储存在tmp[ ] 数组里。然后,再使用strncat( ),将前 k 个元素连接到tmp[ ]数组上,这样就完成了左旋要求。不过这时,数据存放在 tmp[ ]数组中,最后使用strcpy( ),将元素返回str[ ]数组中,这样便完成了数组左旋的要求。