原作者:v_JULY_v:http://blog.csdn.net/v_july_v/article/details/6322882
何海涛--《剑指offer》
感谢上述作者;
我仅仅是将其一些思路重新自己实现了一遍,稍作整理,以便经常温习用。
题目描述:
字符串左旋操作:将一个字符串前面的若干个字符移动到字符串尾部;例如字符串“abcdefgi” ,左旋3位,结果为“defgiabc”;
请实现字符串左旋转的函数,要求对长度为n的字符串操作时间复杂度为O(n) , 空间复杂度为O(1);
假设字符串长度:n
左旋移位:m
思路一:蛮力移位
每一移位一位:利用temp保存字符串的第一位string[0],并将后面的字符赋值给前一位,即string[i - 1] = string[i];再将temp赋值给string[n-1];
重复操作m次:
辅助空间:temp
思路二:利用指针左旋
字符串string[0] ------string[n-1] ; 指针p1 指向string[0] , p2指向string[m] ,即 相隔m位;(n > m)
交换swap(string[p1] , string[p2]) ,然后p1++ ,p2++ ,循环操作m次;
若 n % m == 0:即 n 整除 m时:
例如"123456789" ,左旋3位--》变成“456789123” ;此时 9 % 3 == 0;最终 p1 指向9 , p2指向3;
若当(n % m != 0)时:
"12345678967" -->"45678912367"时, 尾部段落“12367” , p1指向1 , p2指向6;此时无法正常的完成全部m位左旋;
我们知道string[i] ,0 <= i <= n - 1;p1指向1时,其代表下标值为6,p2指向6,其下标值为9;9 + (m - 1) = 9 + (3 - 1) = 11 > n - 1 = 10 出界;
对于尾部段落“12367” ,我们只要将“6" ,“7”一步步左移到"123"前面即可。且移动元素个数= n - p2 = 11 - 9 = 2;
算法步骤:
1、首先让p1=string[0],p2=string[m],即让p1,p2相隔m的距离;
2、判断p2+m-1是否越界,如果没有越界转到3,否则转到4;
3、不断交换*p1与*p2,然后p1++,p2++,循环m次,然后转到2。
4、此时p2+m-1 已经越界,在此只需处理尾巴。过程如下:
4.1 通过n-p2得到p2与尾部之间元素个数r,即我们要前移的元素个数。
4.2 以下过程执行r次:
string[p2]<->string[p2-1],string[p2-1]<->string[p2-2],....,string[p1+1]<->string[p1];p1++;p2++;
其中 k = (n - m) - n % m;k为总的正常左旋移动次数;
r = n - p2 ; 越界时,处理尾巴需要移动的元素个数
p2 + m - 1 用于判断是否越界(n - 1);
思路三:递归转换法
就是说,把一个规模为N的问题化解为规模为M(M<N)的问题。
举例来说,设字符串总长度为L,左侧要旋转的部分长度为s1,那么当从左向右循环交换长度为s1的小段,直到最后,由于剩余的部分长度为s2(s2==L%s1)而不能直接交换。
该问题可以递归转化成规模为s1+s2的,方向相反(从右向左)的同一个问题。随着递归的进行,左右反复回荡,直到某一次满足条件L%s1==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,满足结束条件,返回结果。
即从左至右,后从右至左,再从左至右,如此反反复复,直到满足条件,返回退出
思路四:三步翻转法
举例:“abcdefgij”,左旋3位;
(1)
我们将其分为两部分:“abc” ,“defgij”;
先对子部分翻转:“abc”-->"cba" ; “defgij”-->"jigfed";
两者结合:“cbajigfed”;再对其翻转"cbajigfed" ----->"defgijabc";
(2)
也可以先对总的字符串翻转:“abcdefgij”---->"jigfedcba"
再对子部分翻转,不过这次分割子部分时:从最右边选择m位为一部分--》“cba”,左端剩余n-m位为一部分--》"jigfed"
翻转:“jigfed”-->"defgij" ,“cba”--"abc"
最终“defgijabc"
代码实现:
#include<iostream>
#include<string>
using namespace std;
//---------------------------------------------左旋字符串------------------------------------------------------------
void leftRotateOne(char* pStr , int length);//暴力
void Roate(char *pHead, char *pTail);
//-----------------------------------------------方法一暴力移位------------------------------------------------------
void leftRoate_Soulation1(char* pStr , int length ,int m)
{
if(pStr == NULL || m < 0)
return;
while(m--)
{
leftRotateOne(pStr , length);
}
}
void leftRotateOne(char* pStr , int length)
{
char temp = pStr[0];
for(int i = 1; i < length; i++)
pStr[i-1] = pStr[i];
pStr[length - 1] = temp;
}
//-----------------------------------------------方法二指针翻转-----------------------------------------------------
void leftRoate_Soulation2(string &pStr , int m)
{
if(pStr.length() <= 0 || m <= 0)
return;
int n = pStr.length();
//m >= n
if(m % n <= 0)
return;
int p1 = 0;
int p2 = m;
int k = (n - m) - n%m;
for(int i = 0; i < k; i++)
{
swap(pStr[p1] , pStr[p2]);
p1++;
p2++;
}
int r = n - p2;
while(r--)
{
int i = p2;
while(i > p1)
{
swap(pStr[i] , pStr[i - 1]);
i--;
}
p2++;
p1++;
}
}
//-----------------------------------------------方法三 递归法--------------------------------------------------------
void leftRoate_Soulation3(string &pStr , int length , int m , int head , int tail , bool flag)
{
if(length <= 0 ||head == tail || m <= 0)
return;
//左旋
if(flag == true)
{
int p1 = head;
int p2 = head + m;
int n = length;
int k = (n - m) - (n%m);
for(int i = 0; i < k; i++)
{
swap(pStr[p1] , pStr[p2]);
p1++;
p2++;
}
leftRoate_Soulation3(pStr , n - k , n%m , p1 , tail , false);
}
else
{
int p1 = tail;
int p2 = tail - m;
int n = length;
int k = (n - m) - n%m;
for(int i = 0; i < k; i++)
{
swap(pStr[p1] , pStr[p2]);
p1--;
p2--;
}
leftRoate_Soulation3(pStr , n - k , n%m , head , p1 , true);
}
}
//------------------------------------------------方法四三步旋转法------------------------------------------------
char* leftRoate_Soulation4(char* pStr, int m)
{
if(pStr != NULL)
{
int nLength = static_cast<int>(strlen(pStr));
if(nLength > 0 && m > 0 && m < nLength)
{
char* pFirstStart = pStr;
char* pFirstEnd = pStr + m - 1;
char* pSecondStart = pStr + m;
char* pSecondEnd = pStr + nLength - 1;
// 翻转字符串的前面n个字符
Roate(pFirstStart, pFirstEnd);
// 翻转字符串的后面部分
Roate(pSecondStart, pSecondEnd);
// 翻转整个字符串
Roate(pFirstStart, pSecondEnd);
}
}
return pStr;
}
void Roate(char *pHead, char *pTail)
{
if(pHead == NULL || pTail == NULL)
return;
while(pHead < pTail)
{
char temp = *pHead;
*pHead = *pTail;
*pTail = temp;
pHead ++, pTail --;
}
}