实现字符串左旋和右旋的常见方法

     说起字符串的左旋和右旋问题,想必大家都不陌生,这是一个在初学C语言过程中经常遇到的一个问题,解题的思路可以说很多,每一个人的看待问题的角度都不同,所以就可以得到不同的解题思路。下面我就列举几种方法:

先从最容易想到的说起:

以字符串abcdef为例,若是左旋问题,首先我们可以拿出首个字符a,将其与后面的每一个字符交换一次,得到新的字符串bcdefa,然后进行交换得到cdefab,循环执行,一直到次数等于你给定的次数。(右旋与此类似)

代码如下所示:


void LRevolve_Words(char *Pword, int  k, int len)            //左旋,k为指定的左旋字符的个数
{
	int i = len;                                         
	char temp;
	while (k--)
	{
		for ( i = 0; i < len - 1 ; i++)              //依次交换
		{
			temp = *(Pword + i);
			*(Pword + i) = *(Pword + i + 1);
			*(Pword + i + 1) = temp;
		}
	}
}

void RRevolve_Words(char *Pword, int  k, int len)               //右旋
{
	int i = len;
	char temp;
	while (k--)
	{
		for ( i = len ; i > 1 ; i--)
		{
			temp = *(Pword + i - 1);
			*(Pword + i - 1) = *(Pword +  i - 2);
			*(Pword + i - 2) = temp;
		}
	}
}

       上面的方法是最简单的,也容易想到,实现起来简单,代码容易理解,但是最主要的缺点就是效率不高,如果不考虑时间和空间的限制,假设移动的位为k,则要循环k次,每次循环移动1位,这样的空间复杂度是0(1),时间复杂度是0(n*k),如果k大于n,则0(n^2)。因此,如果K>N,右移K - N之后的数组序列跟右移K位的结果是一样的。


       下面介绍一种比较巧妙地方法:(个人认为也是最好的方法)

采用先部分再整体的方法(或者先整体再部分,效果差不多):具体实现是先反转前k个字符,然后再反转后n - k个字符,最后再反转整个字符串。这种方法比上面一种方法高效。时间复杂度是0(n),空间复杂度为0(1)。

这种方法也叫三步反转法。(以左旋为例,右旋与之类似)

#include<stdio.h>
#include <string.h>
#include<assert.h>
#define Max_Words 20
void Reverse_String(char * Pword, char * qword)
{
	char *pa = Pword;
	char *pb = qword;
	char temp;
	assert(Pword);
	assert(qword);
	while (pa<pb)
	{
		temp = *pa;
		*pa = *pb;
		*pb = temp;
		pa++;
		pb--;
	}
}
int main()
{
	char arr[Max_Words];
	int len = 0;
	int k = 0;
	char *pStart = NULL;
	char *pEnd = NULL;
	printf("请输入您要旋转的字符串内容\n");
	scanf("%s", arr);
	len = strlen(arr) - 1;
	printf("请输入您要旋转的字符个数\n");
	scanf("%d", &k);
	pStart = &arr[0];
	pEnd = arr + len;
	Reverse_String(pStart, pStart + k - 1);             //把要左旋的k个字符先逆序翻转
	Reverse_String(pStart + k, pEnd);                    //把k+1后的字符逆序翻转
	Reverse_String(pStart, pEnd);                          //整个字符串逆序翻转        
	printf("旋转之后的字符串为:%s\n", arr);
	return 0;
}

        接着再介绍一种不是一下子可以想到的方法。就是用递归,当然不是说这方法很难,只是我们不经常用这方法,或者说如果不提醒你,你是无法一下子想到的。(一般会提示你请用递归方法解决)

当然递归也有不同的递归方式:

方式一:

        每次向右移动K位,最后递归,把一个规模为N的问题化解为规模为M(M<N)的问题。

       举例来说,设字符串总长度为L,左侧要旋转的部分长度为s1,那么当从左向右循环交换长度为s1的小段,直到最后,由于剩余的部分长度为s2(s2==L%s1)而不能直接交换。

该问题可以递归转化成规模为s1+s2的,方向相反(从右向左)的同一个问题。随着递归的进行,左右反复回荡,直到某一次满足条件L%s1==0而交换结束。可以说是第一种方法更好地展示

方式二:

        先说左旋,每次都把一个元素拿出来,然后将把一个元素先拿出来,放在一个临时变量temp之中,并把这个元素改成‘\0’,然后进行递归,直到达到指定的次数,递归结束,之后进行递归的返回,返回后依次拿出之前保存的temp赋值给最后面元素之后的一个空间,(当然这里需要额外的空间,定义的数组要比字符串内容大)。

void LRevolve_Words(char *Pword , int  k , int len)             //len为数组内容的大小
{
	char temp;
	assert(Pword);
	if (k)
	{
		 temp = *Pword;
		 *Pword = '\0';                                 //将首字符改成‘\0’
		 LRevolve_Words(Pword + 1, k - 1 , len);
		*(Pword + len  ) = temp;
	}
	*(Pword + len + k) = '\0';
}
        再说右旋,与左旋稍有不同,在递归的过程中首先要将每一个字符向后面移动一位,然后取出最后面的一个元素,并放入temp之中,然后再改成'\0',然后进行递归,递归返回的时候,将前面元素赋值成后面要旋转的元素。
void RRevolve_Words(char *Pword , int  k , int len)
{
    char temp;
    int i;
    assert(Pword);
    if (k)
    {
        temp = Pword[len - 1];
        for ( i = 0; i < len ; i++)
        {
            Pword[len - i  ] = Pword[len - i - 1];
        }
        Pword[len] = '\0';
         RRevolve_Words(Pword + 1, k - 1 , len - 1);
        *Pword  = temp;                                          
    } 
}

         其实还有许多其他的方法,总之没有最好只有更好,大家也可以多多去探索!




  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值