《编程珠玑(第2版)》笔记——将一个n元一维向量向左旋转i个位置(第2章)
2013-11-17 11:07
525 查看
第2章 啊哈!算法
《编程珠玑(第2版)》的第2章,一开始就给出三个问题,其中问题B很有意思:将一个n元一维向量向左旋转i个位置。例如,当n=8且i=3时,向量abcdefgh旋转为defghabc。简单的代码使用一个n元的中间向量在n步就能够完成该工作,你能否仅使用数十个额外字节的存储空间,正比于n的时间内完成向量旋转。其实就像问题中提到的,如果不考虑空间,这是个很简单的问题。但考虑到空间的时候,能否灵巧地解决该问题?
首先:在我看来没有必要写这么复杂,很简短很简单就能实现。
给我我个人方法:
#include "stdio.h"
#include "stdlib.h"
void main()
{
char *p;
int n,m,i=0;
printf("字符串长度n为:");
scanf("%d",&n);
p = (char*)malloc(n+1);
printf("输入字符串:");
scanf("%s",p);
printf("要转换的长度为:n = ");
scanf("%d",&m);
m = m % n;
printf("%s",p+m);
while (m > 0)
{
printf("%c",p[i++]);
m--;
}
printf("\n");
free(p);
}
书中给出了2个比较精巧的方法:
1.求模置换的方法:
其实我们知道有个节约空间的方案:每次向左旋转一个位置(其时间正比于n),总共需要旋转i次。这个方案会消耗过多的运行时间。而求模置换的方法则是尽量让每个数一次移动到位。总体的思想是:以i为除数对n求模,将向量遍历完并一次移动到位。看下面两个例:示例数组: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
(1)n=12且i=5:
(2)n=12且i=3:
上述两图展示了两种不同的情况,示例(1)的n=12和i=5最大公约数是1,因此不断求模能够无重复完整遍历数组;而示例(2)中n=12且i=3,有最大公约数3,因此移动3个位置就会出现重复,因此需要移动到下一位再次求模共3次:
[cpp]
view plaincopyprint?
//求公约数
unsigned int Gcd(unsigned int a, unsigned int b)
{
unsigned int temp;
while (b != 0)
{
temp = a % b;
a = b;
b = temp;
}
return a;
}
void modShift(int array[], int n, int rotdist)
{
unsigned int gcd = Gcd(n, rotdist);
for (int i = 0; i < gcd; i ++)
{
int temp = array[i];
int j = i;
int k;
while(1)
{
int k = j + rotdist;
if (k >= n)
{
k -= n;
}
if (k == i)
{
break;
}
array[j] = array[k];
j = k;
}
array[j] = temp;
}
}
//求公约数 unsigned int Gcd(unsigned int a, unsigned int b) { unsigned int temp; while (b != 0) { temp = a % b; a = b; b = temp; } return a; } void modShift(int array[], int n, int rotdist) { unsigned int gcd = Gcd(n, rotdist); for (int i = 0; i < gcd; i ++) { int temp = array[i]; int j = i; int k; while(1) { int k = j + rotdist; if (k >= n) { k -= n; } if (k == i) { break; } array[j] = array[k]; j = k; } array[j] = temp; } }
2.分段递归交换的方法:
旋转向量x其实就是交换向量ab的两段,得到ba(a代表x中的前i个元素)。假设a比b短,将b分为b1和b2两段,使b2有跟a相同的长度,然后交换a和b2,也就是ab1b2交换得到b2b1a,a的位置已经是最终的位置,现在的问题集中到交换b2b1这两段,又回到了原来的问题。不断递归下去,到b1和b2的长度长度相等交换即可。代码如下[cpp]
view plaincopyprint?
//swapShift
//swap x[a .. a+offset-1] and x[b .. b+offset-1]
void swap(int array[], int a, int b, int offset)
{
int temp;
for (int i = 0; i < offset; i++)
{
temp = array[a + i];
array[a + i] = array[b + i];
array[b + i] = temp;
}
}
void swapShift(int *array, int n, int rotdist)
{
int p = rotdist;
int i = p;
int j = n - p;
while (i != j)
{
if (i > j)
{
swap(array, p - i, p, j);
i -=j;
}
else
{
swap(array, p - i, p + j - i, i);
j -= i;
}
}
swap(array, p - i, p, i);
}
//swapShift //swap x[a .. a+offset-1] and x[b .. b+offset-1] void swap(int array[], int a, int b, int offset) { int temp; for (int i = 0; i < offset; i++) { temp = array[a + i]; array[a + i] = array[b + i]; array[b + i] = temp; } } void swapShift(int *array, int n, int rotdist) { int p = rotdist; int i = p; int j = n - p; while (i != j) { if (i > j) { swap(array, p - i, p, j); i -=j; } else { swap(array, p - i, p + j - i, i); j -= i; } } swap(array, p - i, p, i); }
3.求逆法(翻手算法)
利用向量原理:把x向量分成ab两部分,a是前i个元素,b是后n-i个元素,首先对a求逆,得到a-1b,然后对b求逆得到a-1b-1,然后对整体求逆得到(a-1b-1)-1=ba。非常容易理解,代码如下:
[cpp]
view plaincopyprint?
//Reverse
void reverse(int array[], int low, int high)
{
int temp = 0;
for(int i = low; i <= (high + low) / 2; i++)
{
temp = array[i];
array[i] = array[high - (i - low)];
array[high - (i - low)] = temp;
}
}
void reverseShift(int *array, int n, int rotdist)
{
reverse(array, 0, rotdist - 1);
reverse(array, rotdist, n - 1);
reverse(array, 0, n - 1);
}
//Reverse void reverse(int array[], int low, int high) { int temp = 0; for(int i = low; i <= (high + low) / 2; i++) { temp = array[i]; array[i] = array[high - (i - low)]; array[high - (i - low)] = temp; } } void reverseShift(int *array, int n, int rotdist) { reverse(array, 0, rotdist - 1); reverse(array, rotdist, n - 1); reverse(array, 0, n - 1); }
翻手算法代码非常简短,非常容易理解,而且针对字符串的求逆也不用自己写函数,在时间和空间上都很高效。
总结:
一个简单的问题,在不同的角度,可以找到不同的方法,主要要做到发散思维,找到高效又容易理解的方法。相关文章推荐
- 《编程珠玑(第2版)》笔记——将一个n元一维向量向左旋转i个位置(第2章)
- 《编程珠玑(第2版)》笔记——将一个n元一维向量向左旋转i个位置(第2章)
- 《编程珠玑(第2版)》笔记——将一个n元一维向量向左旋转i个位置(第2章)
- 编程珠玑(1):将一个n元一维向量向左旋转i个位置。例如当n=8且i=3时,向量abcdefgh 旋转为defghabc
- 《编程珠玑》2.3 将一个n元一维向量左旋转i个位置
- 编程珠玑2章B问题--n元一维向量向左旋转i个位置
- 问题:将一个n元一维向量向左旋转i个位置,例如,当n=8且i=3时,向量abcdefgh旋转为defghabc。
- 将一个n元一维向量向左旋转i个位置。例如,当n = 8且i = 3时,向量abcdefgh旋转为defghabc
- n元一维向量向左旋转i个位置
- 平移向量:将一个具有n个元素的一维向量向左旋转i个位置
- [置顶] 编程珠玑:n元一维向量左旋移i个位置
- 将一个n元一维向量向左翻转i个位置的算法(一)
- “《编程珠玑》(第2版)第2章”:B题(向量旋转)
- 将一个n元一维向量向左翻转i个位置的算法(二)
- 【编程珠玑】将一个n元向量左旋转i个位置
- n元一维向量旋转问题(编程珠玑--第2章--问题B )
- 编程珠玑第二章问题B: n元一维向量旋转问题之java实现
- 编程珠玑 一维向量左旋转
- 半闲居士视觉SLAM十四讲笔记(3)三维空间刚体运动 - part 3 旋转向量、欧拉角、四元数
- “《编程珠玑》(第2版)第2章”:A题(二分搜索)