C语言实现数组元素循环右移

C语言实现数组元素循环右移

算法设计要求

试设计一个算法,将数组 A n A_n An中的元素全部循环向右移动k位,并要求只用一个元素大小的附加存储,元素移动或者交换次数为 O ( n ) O(n) O(n)

算法分析思想

将数组分为两部分,前n-k个元素和后k个元素,分别将这两部分反转,然后再将整个数组反转。这样可以实现循环右移的效果,而且不需要使用递归。

数学分析

首先我们来看优化算法的实现过程:
首先对k进行取模,以处理k大于n的情况。
然后反转前n-k个元素,将前n-k个元素从[0, n-k-1]反转为[n-k-1, 0],此时数组变为了[An-k, An-k+1, …, An-1, A0, A1, …, An-k-1]。
接着反转后k个元素,将后k个元素从[n-k, n-1]反转为[n-1, n-k],此时数组变为了[An-k, An-k+1, …, An-1, An-k, An-k+1, …, An-1-k]。
最后反转整个数组,将整个数组从[0, n-1]反转为[n-1, 0],此时数组变为了[An-k, An-k+1, …, An-1-k, A0, A1, …, An-k-1],即将所有元素循环右移了k位。

接下来我们用数学方法来分析为什么这样可以。
设原数组为A,将A循环右移k位后得到的新数组为B。
根据循环右移的定义,可以将B表示为:
B[i] = A[(i+k) mod n] (0 <= i < n)
我们将B中的元素拆分成两部分:前n-k个元素和后k个元素,分别为B1和B2。
B1[i] = A[(i+k) mod n] (0 <= i < n-k)
B2[i] = A[(i+k) mod n] (n-k <= i < n)
将B1和B2反转后得到的新数组为C1和C2。
C1[i] = A[(n-k-1-i+k) mod n] (0 <= i < n-k)
C2[i] = A[(n-1-i+k) mod n] (n-k <= i < n)
合并C1和C2得到新数组C。
C[i] = A[(n-k-1-i+k) mod n] (0 <= i < n-k)
C[i] = A[(n-1-i+k) mod n] (n-k <= i < n)
将C反转后得到的新数组D即为循环右移k位后的数组。
D[i] = A[(n-k-1-i+k+k) mod n] (0 <= i < n)
化简上式可得:
D[i] = A[(n-k-1-i) mod n] (0 <= i < n)
可见,通过将数组分为前n-k个元素和后k个元素,分别反转,然后再反转整个数组,可以实现将数组循环右移k位的效果。

代码实现

#include <stdio.h>

void reverse(int a[], int start, int end);
void MoveRight(int a[], int n, int k);

int main() {
    int a[5] = {1, 2, 3, 4, 5};
    MoveRight(a, 5, 3);
    for (int i = 0; i < 5; i++) {
        printf("%d ", a[i]);
    }
    return 0;
}

/// @brief 将start和end中的元素进行反转(包含start和end这两个元素)
/// @param a 
/// @param start 
/// @param end 
void reverse(int a[], int start, int end) {
    while (start < end){
        int temp = a[start];
        a[start] = a[end];
        a[end] = temp;
        start++;
        end--;
    }
}

/// @brief 将数组中的所有元素全部循环右移k位
/// @param a 
/// @param n 数组元素的总数
/// @param k 右移的距离
void MoveRight(int a[], int n, int k) {
    k %= n;
    reverse(a, 0, n - k - 1);
    reverse(a, n - k, n - 1);
    reverse(a, 0, n - 1);
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值