问题:将一个n元一维向量向左旋转i个位置,要求使用一个n元的中间向量在n步内完成该工作,仅使用数十个额外字节的存储空间,在正比于n的时间内完成向量的旋转。
思路:
想法1:移动x[0]到临时变量t,然后移动x[i]至x[0],x[2i]至x[i],以此类推(将x中的所有下标对n取模),直至返回到取x[0]中的元素,此时改为从t取值然后终止过程。以此类推,直至把x[0]至x[i-1]个变量移动。
想法2:问题可以理解为将数组ab转换成ba。从ab开始,首先对a求逆,得到a'b,然后对b求逆,得到a'b',最后整体求逆,得到(b'a')',就是ba。
代码:
想法1
#include <stdio.h>
#define MAXSIZE 10
void print(const int num[]);
void move(int num[],int move_num);
int gcd(int num,int move_num);
/* 打印信息*/
void print(const int num[])
{
int tmp_index = 0;
printf("(num) : ");
while(tmp_index<MAXSIZE)
{
printf("%d ", num[tmp_index]);
tmp_index++;
}
printf("\n");
}
/* 向左移动*/
void move(int num[],int move_num)
{
int index = 0;//遍历次数的索引
int times = gcd(MAXSIZE,move_num);//MAXSIZE和move_num的最大公约数,即遍历次数
int tmp_value = 0;//存储当前遍历的第一个值的临时变量,表示t
int tmp_index = 0;//向后移动move_num位的临时变量,表示x[i]中的i
int tmp_index_index = 0;//向后移动k*move_num位的临时变量(k>=1),表示x[i*2]中的i*2
for(index = 0;index< times;index++)//共遍历times次
{
tmp_value = num[index];
tmp_index = index;
while(1)
{
tmp_index_index = tmp_index + move_num;
if(tmp_index_index >= MAXSIZE)//超过最大值,则减去最大值,剩余值表示新的位置
tmp_index_index -= MAXSIZE;
if(tmp_index_index == index)//与当前索引值相同,说明当前一次的遍历结束(否则,就会重复遍历相同位置),再次遍历下一个
break;
num[tmp_index] = num[tmp_index_index];//把x[2*i]的位置移动到x[i]上
tmp_index = tmp_index_index;//探针移动到i*2上
}
num[tmp_index] = tmp_value;//把t值赋予相应的位置
print(num);//打印每次遍历后的数组
}
}
/* 求最大公约数*/
int gcd(int num,int move_num)
{
return (!move_num)?num:gcd(move_num,num%move_num);
}
/* 主程序*/
int main()
{
int num[MAXSIZE] = {0,1,2,3,4,5,6,7,8,9};
int move_num = 0;
char c;
print(num);
//输入向左移动次数(大于0)
printf("Input times for moving left: ");
while(scanf("%d",&move_num) != 1 || move_num <= 0 || move_num >= MAXSIZE)
{
printf("please input times (>0 and <MAXSIZE)!\n");
while((c = getchar()) != '\n' && c != EOF);
printf("Input times for moving left: ");
}
//移动move_num
move(num,move_num);
return 0;
}
想法1的时间复杂度为O(n),空间复杂度为O(n)
想法2
#include <stdio.h>
#define MAXSIZE 10
void print(const int num[]);
void move(int num[],int move_num);
void reverse(int num[],int start_pos,int end_pos);
/* 打印信息*/
void print(const int num[])
{
int tmp_index = 0;
printf("(num) : ");
while(tmp_index<MAXSIZE)
{
printf("%d ", num[tmp_index]);
tmp_index++;
}
printf("\n");
}
/* 向左移动*/
void move(int num[],int move_num)
{
reverse(num,0,move_num-1);//翻转a,得到a'
reverse(num,move_num,MAXSIZE-1);//翻转b,得到b'
reverse(num,0,MAXSIZE-1);//翻转a'b'
}
/* 翻转
* @param
* num[]:数组
* start_pos:起始位置
* end_pos:终止位置
*/
void reverse(int num[],int start_pos,int end_pos)
{
int tmp_value = 0;
for(;start_pos<end_pos;start_pos++,end_pos--) {
tmp_value = num[start_pos]; num[start_pos] = num[end_pos];
num[end_pos] = tmp_value;
}
print(num);
}
/* 主程序
* 问题:将一个n元一维向量向左旋转i个位置,要求使用一个n元的中间向量在n步内完成该工作,
* 仅使用数十个额外字节的存储空间,在正比于n的时间内完成向量的旋转。
* 思路:问题可以理解为将数组ab转换成ba。从ab开始,首先对a求逆,得到a'b,然后对b求逆,得到a'b',最后整体求逆,得到(b'a')',就是ba。
*/
int main()
{
int num[MAXSIZE] = {0,1,2,3,4,5,6,7,8,9};
int move_num = 0;
char c;
print(num);
//输入向左移动次数(大于0)
printf("Input times for moving left: ");
while(scanf("%d",&move_num) != 1 || move_num <= 0 || move_num >= MAXSIZE)
{
printf("please input times (>0 and <MAXSIZE)!\n");
while((c = getchar()) != '\n' && c != EOF);
printf("Input times for moving left: ");
}
//移动move_num
move(num,move_num);
return 0;
}
想法2的时间复杂度为O(n),空间复杂度为O(n)