数组元素循环移动

数组元素循环移动



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


1. 目的

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

2. 效果

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

3. 示例

现有整型数组

A = [ 1 , 2 , 3 , 4 , 5 , 6 ] \color{DeepPink}{A = [ 1,2,3,4,5,6 ]} A=[123456]

循环向右移动两次,得到的结果应该是

A = [ 5 , 6 , 1 , 2 , 3 , 4 ] \color{DeepPink}{A = [ 5,6,1,2,3,4 ]} A=[561234]

3.1 算法Ⅰ:循环单次右移一位

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

下标012345
元素123456

一个整型变量 temp 保存最后一个位置的元素 t e m p = A [ l e n g t h − 1 ] \color{DeepPink}{temp = A [length-1]} temp=A[length1]
然后将其余元素从下标 length-2 到下标 0 的元素右移一个位置;

下标012345
元素112345

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

下标012345
元素612345

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;
	}

3.2 算法Ⅱ:下标关系交换元素

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

下标012345
元素561234

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

下标012345
元素123456

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

下标012345
元素432156
下标012345
元素432165

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

下标012345
元素561234

此时得到了正确的结果。
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上运行成功。


4. 结论:

第一种算法由于要移动大量(数组很大的时候)的元素,效率不如第二种高。第二种算法更高效,时间复杂度低,优先考虑。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值