左旋字符

问题描述:

实现一个函数,可以左旋字符串中的k个字符。
ABCD左旋一个字符得到BCDA ,
ABCD左旋两个字符得到CDAB 。
该问题有三种解法:

1. 第一种方法是通过左移字符串,

例如当左旋2个字符时,可以先左旋一个字符,要左旋一个字符,可以先将第二个以及它后面的全部字符全部向左移动一位,之后再将之前的第一个字符赋值给最后一个字符,但再赋值之前要先保存第一个字符;之后要左旋多个字符的话,就是将左旋一个字符的动作重复多次就OK。话不多说,上干货;

void left_move_1(char *arr,int n, int k)//arr是传来的字符数组,n是字符长度,k是左旋字符的个数
{
    int i = 0, j = 0;
    char a;
    assert(arr);
    k = k%n;//这条语句是减少循环次数,当左移字符个数大于字符串本身大小,左移动作就会重复
    for (j = 1; j <= k; j++)//外层循环控制左旋字符的个数
    {
        a = arr[0];//将第一个字符保存
        for (i = 1; i < n; i++)
        {

            arr[i - 1] = arr[i];//将第二个以及起后面的字符左移一位
        }
        arr[i-1] = a;//将第一个字符赋值给最后一个字符
    }
}

很明显,这种方法的时间复杂度为O(n^2);还可以继续优化。

2 . 第二种方法是通过逆转字符串实现。

例如要将ABCD左移两个字符得到CDAB,可以先将AB逆转得到BA,再将CD逆转得到DC,所以此时字符串是BADC,之后再逆转,就得到CDAB。实现左移两个字符。来看代码;

static void swap(char *x, char *y)//交换两个字符
{
    assert(x);
    assert(y);
    (*x) ^= (*y);
    (*y) ^= (*x);
    (*x) ^= (*y);
}
static void reverse_string(char *start, char *end)//逆转函数,将字符串逆转
{
    assert(start);
    assert(end);
    while (start < end)
    {
        swap(start, end);
        start++; end--;
    }
}
void left_move_2(char *arr,int n,int k)
{
        assert(arr);
        k = k%n;
        reverse_string(arr, arr + k-1);//逆转前面部分字符串
        reverse_string(arr+k, arr + n-1);//逆转后面部分字符串
        reverse_string(arr, arr + n-1);//整体逆转字符串

}
3.还有第三种方法,第三种方法是两倍空间法(我自己起的名字),方法如下:

先开辟两倍字符串大小的内存空间,在将原来的字符串拷贝到新开辟的空间上两次,但是拷贝两次的相同字符串之间没有’\0’;之后左旋几n个字符直接在新的长字符串的第n个字符的下一个字符中读取,读取的长度和之前字符串长度相等,将这些读取的字符串又再赋值给原来字符数组,就得到想要的结果了。代码如下:

void left_move_3(char *arr, int n, int k)
{

    char *str = (char *)malloc(n * 2 + 1);//先开辟空间
    char *p = str;//保存头指针,用于最后的释放
    strcpy(str, arr); //先拷贝
    strcat(str, arr);//在链接上原来字符串,就等于拷贝两次
    k = k%n;
    while(n--)//循环,控制赋值的次数
    {
        *arr = *(str + k );
        arr++; str++;//两个字符指针都往后移,逐个赋值。
    }
    free(p);//释放掉不用的空间
}

很明显,第三中方法的时间复杂度为O(n),优于第一种方法。这里附上源码;

#define _CRT_SECURE_NO_WARNINGS  1
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
void left_move_1(char *arr,int n, int k)//arr是传来的字符数组,n是字符长度,k是左旋字符的个数
{
    int i = 0, j = 0;
    char a;
    assert(arr);
    k = k%n;//这条语句是减少循环次数,当左移字符个数大于字符串本身大小,左移动作就会重复
    for (j = 1; j <= k; j++)//外层循环控制左旋字符的个数
    {
        a = arr[0];//将第一个字符保存
        for (i = 1; i < n; i++)
        {

            arr[i - 1] = arr[i];//将第二个以及起后面的字符左移一位
        }
        arr[i-1] = a;//将第一个字符赋值给最后一个字符
    }
}
static void swap(char *x, char *y)//交换两个字符
{
    assert(x);
    assert(y);
    (*x) ^= (*y);
    (*y) ^= (*x);
    (*x) ^= (*y);
}
static void reverse_string(char *start, char *end)//逆转函数,将字符串逆转
{
    assert(start);
    assert(end);
    while (start < end)
    {
        swap(start, end);
        start++; end--;
    }
}
void left_move_2(char *arr,int n,int k)
{
        assert(arr);
        k = k%n;
        reverse_string(arr, arr + k-1);//逆转前面部分字符串
        reverse_string(arr+k, arr + n-1);//逆转后面部分字符串
        reverse_string(arr, arr + n-1);//整体逆转字符串

}
void left_move_3(char *arr, int n, int k)
{

    char *str = (char *)malloc(n * 2 + 1);//先开辟空间
    char *p = str;//保存头指针,用于最后的释放
    strcpy(str, arr); //先拷贝
    strcat(str, arr);//在链接上原来字符串,就等于拷贝两次
    k = k%n;
    while(n--)//循环,控制赋值的次数
    {
        *arr = *(str + k );
        arr++; str++;//两个字符指针都往后移,逐个赋值。
    }
    free(p);//释放掉不用的空间
}
int main()
{
    int k = 0;
    char arr[] = "1234abcd";
    printf("左旋前的字符串是:%s\n", arr);
    int n = strlen(arr);
    printf("输入左旋字符的个数:");
    scanf("%d", &k);
    left_move_1(arr, n, k);
    printf("方法1:左旋后的字符串是:%s\n", arr);
    left_move_2(arr, n, k);
    printf("方法2:左旋后的字符串是:%s\n", arr);
    left_move_3(arr, n,k);
    printf("方法3:左旋后的字符串是:%s\n", arr);
    system("pause");
    return 0;
}

这里写图片描述
三种方法测试如图,测试环境VS2017,测试结果正确。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值