楼主又来~(≧▽≦)/~啦啦啦,科研,就是要这么一鼓作气。额,其实楼主的老本行是推公式啊,做这些算法题,其实是楼主在偷懒。额,话不多说了,快请出我们今天的主角吧!还是关于数组的-数组循环移位。
下面我们来看下题目的要求。
题目要求:
设计一个算法,把一个含有N个元素的数组循环右移K位,要求时间复杂度为 O(N) ,且只允许使用两个附加变量。
题目解答
我们来自己给个例子,来帮助自己思考。如数组为[1, 2, 3, 4, 5, 6, 7, 8],循环移位4次。
原始序列:[1, 2, 3, 4, 5, 6, 7, 8]
右移1位:[8, 1, 2, 3, 4, 5, 6, 7]
右移2位:[7, 8, 1, 2, 3, 4, 5, 6]
右移3位:[6, 7, 8, 1, 2, 3, 4, 5]
右移4位:[5, 6, 7, 8, 1, 2, 3, 4]
解法一:时间复杂度O(N * K)
代码:
#include <iostream>
#include <vector>
using namespace std;
void getShiftArray (int *pArray, int len, int K);
void printArray(int *pArray, int len);
int main()
{
int a[] = {1, 2, 3, 4, 5, 6, 7, 8};
int len = sizeof(a) / sizeof(int);
int K = 4;
getShiftArray(a, len, K);
printArray(a, len);
system("pause");
}
void getShiftArray (int *pArray, int len, int K)
{
int endTemp = 0;
while (K--)
{
endTemp = pArray[len - 1]; //保存数组最后一个元素
for (int n = len - 1; n >= 0; n--)
{
pArray[n] = pArray[n - 1];
}
pArray[0] = endTemp;
}
}
void printArray(int *pArray, int len)
{
for (int i = 0; i < len; i++)
{
cout << pArray[i] << " ";
}
cout << endl;
}
这种解法是不符合题目的线性复杂度要求的。
但是我们在这儿会意识到一个问题,如果K > N怎么办呢?我们可以试验一下,一个数组循环移位N次,数组会回到原始的样子。那么当K> N时,只需要向右循环移位K – N次就好了。综合一下,无论K取什么值,那么都只需要向右循环移位K % N次,那么上面的代码只需要改动一点点,时间复杂度就可以变为 O(N∗N) ,虽然还是不符合要求,但是我们看下面的代码:
#include <iostream>
#include <vector>
using namespace std;
void getShiftArray (int *pArray, int len, int K);
void printArray(int *pArray, int len);
int main()
{
int a[] = {1, 2, 3, 4, 5, 6, 7, 8};
int len = sizeof(a) / sizeof(int);
int K = 12;
getShiftArray(a, len, K);
printArray(a, len);
system("pause");
}
void getShiftArray (int *pArray, int len, int K)
{
int endTemp = 0;
K = K % len;
while (K--)
{
endTemp = pArray[len - 1]; //保存数组最后一个元素
for (int n = len - 1; n >= 0; n--)
{
pArray[n] = pArray[n - 1];
}
pArray[0] = endTemp;
}
}
void printArray(int *pArray, int len)
{
for (int i = 0; i < len; i++)
{
cout << pArray[i] << " ";
}
cout << endl;
}
解法二:线性时间
其实,细心发现一下就看出来了,[1, 2, 3, 4, 5, 6, 7, 8]可以分为两部分,不加粗的为第一部分,加粗的是第二部分。如果这个数组向右循环移位4次的话,其实两部分的内部顺序都是不变的,只是两个部分交换一下就好了。那么我们可以按照下面的流程来完成循环移位(靠,真是大神,怎么想出来的?)。
原始序列:[1, 2, 3, 4, 5, 6, 7, 8]
将前半部分逆序:[4, 3, 2, 1, 5, 6, 7, 8]
将后半部分逆序:[4, 3, 2, 1, 8, 7, 6, 5]
将整个序列逆序:[5, 6, 7, 8,1, 2, 3, 4 ]
看代码,是如此的简单。
#include <iostream>
#include <vector>
using namespace std;
void getShiftArray (int *pArray, int len, int K);
void printArray(int *pArray, int len);
void reverseArray(int *pArray, int start, int end);
int main()
{
int a[] = {1, 2, 3, 4, 5, 6, 7, 8};
int len = sizeof(a) / sizeof(int);
int K = 12;
getShiftArray(a, len, K);
printArray(a, len);
system("pause");
}
void getShiftArray (int *pArray, int len, int K)
{
K = K % len;
//前半部分逆序
reverseArray(pArray, 0, len - K - 1);
//后半部分逆序
reverseArray(pArray, len - K, len - 1);
//整个序列逆序
reverseArray(pArray, 0, len - 1);
}
void reverseArray(int *pArray, int start, int end)
{
for (; start < end; start++, end--)
swap(pArray[start], pArray[end]);
}
void printArray(int *pArray, int len)
{
for (int i = 0; i < len; i++)
{
cout << pArray[i] << " ";
}
cout << endl;
}
哈哈哈哈,有没有明白呢?楼主申请专栏去啦!如果你也想学习算法,那么可以和楼主一起抱团啊,多多交流,加油加油↖(^ω^)↗