题目描述:
定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。
如把字符串abcdef左旋转2位得到字符串cdefab。
在这里都假设K 〈 N,如果不然的话,K = k%N;另外K 为正整数,如果不然的话就相当于向右移动K位.左移和右移在本质上是一样的。
思想一:
假设数组为:char arr[ ] ="abcd1234"。我们先试验简单的办法,可以每次将数组中的元素右移一位,循环K次。
abcd1234→4abcd123→34abcd12→234abcd1→1234abcd。代码如下:
RightShift(char* arr, int N, int K)
{
while(K--)
{
char t = arr[N - 1];
for(int i = N - 1; i > 0; i --)
arr[i] = arr[i - 1];
arr[0] = t;
}}时间复杂度为:O(n*n);思想二:假设原数组序列为abcd1234,要求变换成的数组序列为1234abcd,即循环右移了4位。比较之后,不难看出,其中有两段的顺序是不变的:1234和abcd,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。
变换的过程通过以下步骤完成:
逆序排列abcd:abcd1234 → dcba1234;
逆序排列1234:dcba1234 → dcba4321;
全部逆序:dcba4321 → 1234abcd。代码如下:
Reverse(char* arr, int b, int e)
{
for(; b < e; b++, e--)
{
char temp = arr[e];
arr[e] = arr[b];
arr[b] = temp;
}
}
RightShift(char* arr, int k)
{ int N = strlen(arr);
Reverse(arr, 0, N – K - 1);
Reverse(arr, N - K, N - 1);
Reverse(arr, 0, N - 1);
}
时间复杂度为:O(n);
思想三:两指针逐步翻转
假设数组为:abcdefghi,要abc移动至最后
abc defghi->def abcghi->def ghiabc
定义俩指针,p1指向ch[0],p2指向ch[m];
一下过程循环m次,交换p1和p2所指元素,然后p1++, p2++;。
第一步,交换abc 和def ,
abc defghi->def abcghi
第二步,交换abc 和 ghi,
def abcghi->def ghiabc
整个过程,看起来,就是abc 一步一步 向后移动
abc defghi
def abcghi
def ghi abc
//最后的 复杂度是O(m+n)图解如下:
如果是要左旋十个元素的序列:abcdefghij,如果abcdef ghij要变成defghij abc:
abcdef ghij
1. def abc ghij
2. def ghi abc j //接下来,j 步步前移
3. def ghi ab jc
4. def ghi a j bc
5. def ghi j abc 或者:1. def abc ghij
2. def ghi abc j
3. def ghi j bc a
4. def ghi j abc 上面两方法思想都差不多,前两步都是一样的,只是后面有点不一样而已。
思想四:通过递归转换,缩小问题之规模
所用方法和思想三一样,但是把思想三用递归的方式实现出来。
举个具体事例说明,如下:
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,满足结束条件,返回结果。
即从左至右,后从右至左,再从左至右,如此反反复复,直到满足条件,返回退出。代码如下:
- 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,满足结束条件,返回结果。
- }
-
}
思想五:利用STL里面的库函数进行。(留待学习)
157

被折叠的 条评论
为什么被折叠?



