数组元素循环移动

数组元素循环移动

数组元素循环移动分为循环左移和循环右移。由于两种情况类似。就以循环右移为例。


数组元素循环右移

操作:

将数组中的元素循环向右移动。

效果:

右移一位时,数组最后一个元素跑到了数组第一个位置,数组其余元素统一向后挪动一个位置。
右移m位时,连续执行m次“右移一位”的操作。

举例:

现有整型数组 A[] = {1,2,3,4,5,6};

第一种算法:

基本思想:将数组元素循环右移m次,每一次的操作如下图

1 2 3 4 5 6

一个整型变量 temp 保存最后一个位置的元素 temp = A[length-1];
然后将其余元素从下标 length-2 到下标 0 的元素右移一个位置;

1 1 2 3 4 5

将 temp中的值存放到数组下标 0 的位置;完成一次右移操作。

6 1 2 3 4 5

C代码如下:

/* 函数功能:从控制台读入n个整型数据,存入数组中,并将数组循环右移m位 */
// 这里假设n>0,m>=0,是合法的数字。
void cyclicShiftRight(int n, int m)
{
    // input.
    int *testArray = (int*)malloc(sizeof(int) * n);
    for (int i = 0; i < n; ++i)
    {
        // 如果报错,请尝试 scanf("%d", testArray + i);
        scanf_s("%d", testArray + i);
    }

    // process.
    /* 循环右移的结果和右移的前n(数组长度)个结果一致,所以做取余操作。*/
    m = m % n;

    // 如果取余操作之后的m等于0,则不需要移动
    if (0 == m)
    {
        // empty
    }
    // 如果取余操作之后的m不等于0,则需要右移
    else
    {
        // 循环右移m次
        for (int j = 1; j <= m; ++j)
        {
            // 先将最后一个元素(下标值为n-1的元素)保存在临时变量temp中
            int temp = testArray[n - 1];
            // 从倒数第二个元素到第一个元素,向右移动一个位置
            for (int k = n - 2; k >= 0; --k)
            {
                testArray[k + 1] = testArray[k];
            }
            // 将temp中保存的元素值放入第一个位置(下标值为0)。
            testArray[0] = temp;
        }
    }

    // output.
    for (int i = 0; i < n; ++i)
    {
        printf("%d", *(testArray + i));
        if (i != n - 1)
        {
            printf(" ");
        }
    }
    printf("\n");
    free(testArray);
    testArray = NULL;
}

int main()
{
    // N是数组的大小(数组元素的个数)
    int N = 0;
    // M是循环右移的位数
    int M = 0;
    // 如果报错,请尝试 scanf("%d %d", &N, &M);
    scanf_s("%d %d", &N, &M);
    cyclicShiftRight(N, M);
    return 0;
}

第二种算法:

假设循环右移2次(循环右移8次与其结果一致,因为8%6==2),正确结果应该如下表

5 6 1 2 3 4

观察结果发现结果数组分为2个部分,一组有2个元素 “5” 和 “6”保持有序,另一组4个元素 “1” 、“2”、“3” 和 “4” 保持有序。
于是将原数组看作两个部分,一组有2个元素保持有序,另一组4个元素保持有序,结果如下

1 2 3 4 5 6

将两个数组中的元素,每个部分数组第一个元素和自身最后一个元素交换,第二元素和倒数第二个元素交换,以此类推… …

4 3 2 1 5 6
4 3 2 1 6 5

然后整个数组看作一个整体,执行以上操作

5 6 1 2 3 4

此时得到了正确的结果。
C代码如下:

/* 函数功能:交换数组testArray中下标firstIndex和下标secondIndex的元素*/
/* 这里假设传入的参数都是正确的,即testArray不能为空,firstIndex和secondIndex下标值是合法的(不能越界)。*/
void swap(int testArray[], int firstIndex, int secondIndex)
{
    // 如果firstIndex和second指向的是同一个下标,则不做任何动作;
    if (firstIndex == secondIndex)
    {
        // empty block.
    }
    else
    {
        // 交换两个元素
        int temp = testArray[firstIndex];
        testArray[firstIndex] = testArray[secondIndex];
        testArray[secondIndex] = temp;
    }
}


/* 函数功能:将数组testArray下标从leftIndex到rightIndex,包含leftIndex和rightIndex的元素进行反转(翻转)。*/
/* 这里假设传入的参数都是正确的,即testArray不能为空,leftIndex和rightIndex下标值是合法且合理的(不能越界并且leftIndex < rightIndex)。*/
void ReverseArray(int testArray[], int leftIndex, int rightIndex)
{
    /* 双“指针”--i和j */
    /* i从leftIndex开始,往下标值增大的方向移动 */
    /* j从rightIndex开始,往下标值减小的方向移动 */
    int i = leftIndex;
    int j = rightIndex;

    // 当满足 i<j 时,循环执行“交换testArray[i]和testArray[j]”的操作
    while (i < j)
    {
        swap(testArray, i, j);
        ++i;
        --j;
    }
}

/* 函数功能:从控制台读入n个整型数据,存入数组中,并将数组循环右移m位 */
// 这里假设n>0,m>=0,是合法的数字。
void cyclicShiftRight(int n, int m)
{
    // input.
    // 这里假设都能分配成功。
    int *testArray = (int*)malloc(sizeof(int) * n);
    for (int i = 0; i < n; ++i)
    {
        // 如果报错,请尝试 scanf("%d", testArray + i);
        scanf_s("%d", testArray + i);
    }

    // process.
    /* 循环右移的结果和右移的前n(数组长度)个结果一致,所以做取余操作。*/
    m = m % n;

    // 如果取余操作之后的m等于0,则不需要移动
    if (0 == m)
    {
        // empty
    }
    // 如果取余操作之后的m不等于0,则需要右移
    else
    {
        /* 将原数组看作两部分 */
        /* 一部分下标从0到n-m-1,进行数组元素的反转 */
        ReverseArray(testArray, 0, n - m - 1);  

        /* 另一部分下标从n-m到n-1,进行数组元素的反转 */
        ReverseArray(testArray, n - m, n - 1);  

        /* 整个数组所有元素进行一次数组元素的反转 */
        ReverseArray(testArray, 0, n - 1);
    }

    // output.
    for (int i = 0; i < n; ++i)
    {
        printf("%d", *(testArray + i));
        if (i != n - 1)
        {
            printf(" ");
        }
    }
    printf("\n");
    free(testArray);
    testArray = NULL;
}

int main()
{
    // N是数组的大小(数组元素的个数)
    int N = 0;
    // M是循环右移的位数
    int M = 0;
    // 如果报错,请尝试 scanf("%d %d", &N, &M);
    scanf_s("%d %d", &N, &M);
    cyclicShiftRight(N, M);
    return 0;
}

以上两种代码在Microsoft Visual Studio 2015 以及 Microsoft Visual Studio 2017上运行成功。
运行结果:

结论:

第一种算法由于要移动大量(数组很大的时候)的元素,效率不如第二种高。第二种算法是更高效的算法,时间复杂度等分析略。

猜你喜欢

转载自blog.csdn.net/PursueLuo/article/details/79993052
今日推荐