编程艺术 - 第一章 左旋转字符串

简介: 编程艺术 - 第一章 左旋转字符串

题目

定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。

若把字符串abcdef左旋转2位得到字符串cdefab。

请实现字符串左旋转的函数,要求对长度为n的字符串操作的时间复杂度为O(n),空间复杂度为O(1);

类似题目还有剑指Offer.58题


分析

三次反转

本题与2.17数组循环移位相似,这里我们用同样的方法。如果不理解,请看2.17数组循环移位

C

#include<stdio.h>
#include<string.h>
void swap(char* arr, int front, int later)//交换函数
{
  char tmp = arr[front];
  arr[front] = arr[later];
  arr[later] = tmp;
}
void Reverse(char* arr, int start, int end)//反转函数  范围[start, end);
{
  int i = 0;
  for(i = start; i < (start + end) >> 1; i++)
  {
    swap(arr, i, end - 1 - (i - start));
  }
}
char* LeftString(char* arr, int pos)//左旋转字符串
{
  int len = strlen(arr);
  pos = len - pos % len;//左转pos次相当于右转len-pos次
  Reverse(arr, 0, len);
  Reverse(arr, 0, pos);
  Reverse(arr, pos, len);
  return arr;
}
int main()
{
  char arr[] = "abcdef";
  int pos = 2;
  LeftString(arr, pos);
  printf("%s\n", arr);
  return 0;
}



双指针+交换

我们用俩个指针,前指针指向开头,后指针指向旋转的位数。具体操作看图


子字符串“cab”尾巴我们又分为三种情况。

1、abc。提前完成任务,这种情况是字符串的长度刚好是左移次数的倍数。如:abcdef,pos=3;

2、cab。这种情况是len%pos = 2。如abcdefgh,pos=3; 向右交换一轮

3、bca。这种情况是len%pos = 1。如abcdefg,pos=3;向右交换俩轮

发现 交换次数=pos-len%pos

具体实现如下

C

#include<stdio.h>
#include<string.h>
#include<assert.h>
void swap(char* arr, int front, int later)//交换函数
{
  char tmp = arr[front];
  arr[front] = arr[later];
  arr[later] = tmp;
}
char* LeftString(char* arr, int pos)//左旋转字符串
{
  assert(arr != NULL);
  assert(pos > 0);
  int len = strlen(arr);
  pos = pos % len;
  int cycle = (len - pos);//第一轮交换的次数
  int front = 0;//前指针
  int later = pos;//后指针
  //第一轮先交换到底
  while (cycle != 0)
  {
    swap(arr, front, later);
    front++;
    later++;
    cycle--;
  }
  cycle = pos - len % pos;//余数为0情况一,其他情况通通右移
  while (cycle != 0)
  {
    int i = 0;
    for (i = 0; i < pos - 1; i++)
    {
      swap(arr, front + i, front + i + 1);
    }
    cycle--;
    front++;
  }
  return arr;
}
int main()
{
  char arr[] = "abcdefgh";
  int pos = 3;
  LeftString(arr, pos);
  printf("%s\n", arr);
  return 0;
}


双指针+交换2

上面是指针一次走到底。然后再把尾巴复原。

这次我们这交换pos的整数倍的字符,剩余的单独处理。图示如下

第一轮的交换代码没有任何变化,只有循环次数有变。循环次数就是字符串总长度减去pos剩余的长度且还是pos的整数倍。cycle = (len - pos) / pos * pos;

我们来分析这次的尾巴。abcgh

因为剩余长度范围为[0,pos-1]。长度不是很大,我们可以选择一步一步的移动也可以在再来一次字符串旋转(len=5,pos=3)。这里我选取的是一步一步的移动(g、h分别依次向左移动)。


C


#include<stdio.h>
#include<string.h>
#include<assert.h>
void swap(char* arr, int front, int later)//交换函数
{
  char tmp = arr[front];
  arr[front] = arr[later];
  arr[later] = tmp;
}
char* LeftString(char* arr, int pos)//左旋转字符串
{
  assert(arr != NULL);
  assert(pos > 0);
  int len = strlen(arr);
  pos = pos % len;
  int cycle = (len - pos) / pos * pos;//第一轮交换的次数
  int front = 0;//前指针
  int later = pos;//后指针
  //第一轮先交换到底
  while (cycle != 0)
  {
    swap(arr, front, later);
    front++;
    later++;
    cycle--;
  }
  cycle = len % pos;//余数为0情况一,其他情况通通右移
  while (cycle != 0)
  {
    int i = later;
    while (i > front)
    {
      swap(arr, i, i - 1);
      i--;
    }
    cycle--;
    later++;
  }
  return arr;
}
int main()
{
  char arr[] = "abcdefgh";
  int pos = 3;
  LeftString(arr, pos);
  printf("%s\n", arr);
  return 0;
}


rotate算法

上述思想在C++中有一个相似的算法STL::rotate这里我引用程序员编程艺术书中代码,有改动。

核心:对字符串长度和旋转长度的最大公约数。作为循环次数。然后每次循环里面先把第一个元素保存下来,然后以旋转长度为单位跳着赋值。如下图

C++

#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int m, int n)
{
  int r = m % n;
  while (r != 0)
  {
    m = n;
    n = r;
    r = m % n;
  }
  return n;
}
//这里的mid相当于pos,范围是[begin,end)
void my_rotate(char* arr,int begin, int  mid, int end)
{
  int len = end - begin;
  int k = mid - begin;
  //gcd最大公约数函数是GUN编译器自带的,这里我自己实现
  int d = gcd(len, k);
  int i = 0;
  int j = 0;
  for (i = 0; i < d; i++)
  {
    int tmp = arr[i];
    int last = i;
    for (j = (i + k) % len; j != i; j = (j + k) % len)
    {
      arr[last] = arr[j];
      last = j;
    }
    arr[last] = tmp;
  }
}
int main()
{
  string str = "abcdefgh";
  int pos = 3;
  my_rotate((char*)str.c_str(), 0, pos, str.length());
  cout << str.c_str() << endl;
  return 0;
}
目录
相关文章
|
7月前
|
C语言 数据格式
C程序设计内容与例题讲解 -- 第三章第三部分(第五版)谭浩强
C程序设计内容与例题讲解 -- 第三章第三部分(第五版)谭浩强
|
7月前
|
存储 移动开发 C语言
# C程序设计内容与例题讲解 -- 第三章第一部分(第五版)谭浩强
# C程序设计内容与例题讲解 -- 第三章第一部分(第五版)谭浩强
|
7月前
|
存储 C语言
C程序设计内容与例题讲解 -- 第三章第二部分(第五版)谭浩强
C程序设计内容与例题讲解 -- 第三章第二部分(第五版)谭浩强
|
7月前
|
自然语言处理 Java C#
C程序设计内容与例题讲解 -- 第一章(第五版)谭浩强
C程序设计内容与例题讲解 -- 第一章(第五版)谭浩强
|
7月前
|
算法 调度
C程序设计内容与例题讲解 -- 第二章(第五版)谭浩强
C程序设计内容与例题讲解 -- 第二章(第五版)谭浩强
|
安全 Java 程序员
<<c和指针>>温故及问题研讨(第一章)
这个部分的分享主要是我在阅读<<C和指针>>这本书的过程发现的我以前遗漏或者没有记清楚的知识点,这本书内容很多,我只做我认为容易混淆或遗漏的部分的分享,有些我认为比较简单的地方会略掉,知识点可能比较杂,我尽量为大家理请逻辑.此书共十八章,也就代表这一部分的博客会有18篇,希望大家多多支持!
<<c和指针>>温故及问题研讨(第一章)
|
C语言 数据安全/隐私保护
课外闲谈1.谈一谈最近自己遇到的比较不错的题目(C语言)
课外闲谈1.谈一谈最近自己遇到的比较不错的题目(C语言)
115 0
课外闲谈1.谈一谈最近自己遇到的比较不错的题目(C语言)
|
算法 C语言
c语言程序设计 编程题
8.求年龄:有 5 个人坐在一起,问第 5 个人多少岁,他说比第 4 个人大 2 岁。问 第 4 个人多少岁,他说比第 3 个人大 2 岁。问第 3 个人多少岁,他说比第 2 个 人大 2 岁。问第 2 个人多少岁,他说比第 1 个人大 2 岁。最后问第 1 个人,他 说是 10 岁。请问第 5 个人多大? #include <stdio.h> int age(int n) { if (n == 1) return 10; return age(n - 1) + 2; } void main() { printf("%d", age(5)); } 9.猴子吃桃问题:猴子第一天摘下若干个
103 0
|
机器学习/深度学习 C语言
c语言程序设计 编程题
20.求数字:输出 100(含 100)-200(含 200)以内的满足以下条件的数,条件 为:这个数与 3 的和是 5 的倍数,与 3 的差是 6 的倍数,输出这样的数。 #include <stdio.h> void main() { int i; for (i = 100; i <= 200; i++) if ((i + 3) % 5 == 0 && (i - 3) % 6 == 0) printf("%d,", i); } 21.求数字:找出乘积为 399 的两个相邻奇数。 #include <stdio.h> void main() { int i = 1; whil
147 0
下一篇
DataWorks