左旋数组中的元素 之 递归&左右反复回荡

通过递归转换,缩小问题之规模

    本文最初发布时,网友留言bluesmic说:楼主,谢谢你提出的研讨主题,很有学术和实践价值。关于思路二,本人提一个建议:思路二的代码,如果用递归的思想去简化,无论代码还是逻辑都会更加简单明了。

    就是说,把一个规模为N的问题化解为规模为M(M<N)的问题。
    举例来说,设字符串总长度为L,左侧要旋转的部分长度为s1,那么当从左向右循环交换长度为s1的小段,直到最后,由于剩余的部分长度为s2(s2==L%s1)而不能直接交换。

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

     举例解释一下:
    设原始问题为:将“123abcdefg”左旋转为“abcdefg123”,即总长度为10,旋转部("123")长度为3的左旋转。按照思路二的运算,演变过程为“123abcdefg”->"abc123defg"->"abcdef123g"。这时,"123"无法和"g"作对调,该问题递归转化为:将“123g”右旋转为"g123",即总长度为4,旋转部("g")长度为1的右旋转。

updated:

Ys:

Bluesmic的思路没有问题,他的思路以前很少有人提出。思路是通过递归将问题规模变小。当字符串总长度为n,左侧要旋转的部分长度为m,那么当从左向右循环交换长度为m的小段直到剩余部分为m’(n % m),此时m’ < m,已不能直接交换了。

此后,我们换一个思路,把该问题递归转化成规模大小为m’ +m,方向相反的同一问题。随着递归的进行,直到满足结束条件n % m==0。

 

  举个具体事例说明,如下:

1、对于字符串abc def ghi gk,

将abc右移到def ghi gk后面,此时n = 11,m = 3,m’ = n % m = 2;

abc def ghi gk -> def ghi abc gk

2、问题变成gk左移到abc前面,此时n = m’ + m = 5,m = 2,m’ = n % m 1;

abc gk -> a gk bc

3、问题变成a右移到gk后面,此时n = m’ + m = 3,m = 1,m’ = n % m = 0;

a gk bc-> gk a bc。 由于此刻,n % m = 0,满足结束条件,返回结果。

 

    即从左至右,后从右至左,再从左至右,如此反反复复,直到满足条件,返回退出。

#include <iostream>   
using namespace std;  
  
void rotate(string &str, int n, int m, int head, int tail, bool flag)  
{  
    //n 待处理部分的字符串长度,m:待处理部分的旋转长度   
    //head:待处理部分的头指针,tail:待处理部分的尾指针   
    //flag = true进行左旋,flag = false进行右旋   
      
    // 返回条件   
    if (head == tail || m <= 0)  
        return;  
      
    if (flag == true)  
    {  
        int p1 = head;  
        int p2 = head + m;  //初始化p1,p2   
          
        //1、左旋:对于字符串abc def ghi gk,   
        //将abc右移到def ghi gk后面,此时n = 11,m = 3,m’ = n % m = 2;   
        //abc def ghi gk -> def ghi abc gk   
        //(相信,经过上文中那么多繁杂的叙述,此类的转换过程,你应该是了如指掌了。)   
          
        int k = (n - m) - n % m;   //p1,p2移动距离,向右移六步   
  
        /*--------------------- 
        解释下上面的k = (n - m) - n % m的由来: 
        yansha: 
        以p2为移动的参照系: 
        n-m 是开始时p2到末尾的长度,n%m是尾巴长度 
        (n-m)-n%m就是p2移动的距离 
        比如 abc def efg hi 
        开始时p2->d,那么n-m 为def efg hi的长度8, 
        n%m 为尾巴hi的长度2, 
        因为我知道abc要移动到hi的前面,所以移动长度是 
        (n-m)-n%m = 8-2 = 6。 
        */  
          
        for (int i = 0; i < k; i++, p1++, p2++)  
            swap(str[p1], str[p2]);  
          
        rotate(str, n - k, n % m, p1, tail, false);  //flag标志变为false,结束左旋,下面,进入右旋   
    }  
    else  
    {  
        //2、右旋:问题变成gk左移到abc前面,此时n = m’ + m = 5,m = 2,m’ = n % m 1;   
        //abc gk -> a gk bc   
          
        int p1 = tail;  
        int p2 = tail - m;  
          
        // p1,p2移动距离,向左移俩步   
        int k = (n - m) - n % m;  
          
        for (int i = 0; i < k; i++, p1--, p2--)  
            swap(str[p1], str[p2]);  
          
        rotate(str, n - k, n % m, head, p1, true);  //再次进入上面的左旋部分,   
        //3、左旋:问题变成a右移到gk后面,此时n = m’ + m = 3,m = 1,m’ = n % m = 0;   
        //a gk bc-> gk a bc。 由于此刻,n % m = 0,满足结束条件,返回结果。   
  
    }  
}  


 

转自:http://blog.csdn.net/v_july_v/article/details/6322882

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值