关于旋转数组问题的环状替换解法的数学推导
目录
- 关于模运算的推导
- 对旋转数组的数学分析
关于模运算的推导
一数与别数之商的余,有:
5/3 = 1… 2
5 mod 3 = 2 (5%3 = 2)
一数在一模下与别数同余,有:
5 mod 3 = 2
8 mod 3 = 2
5 ≡ 8 (mod 3)
当N ≡ M (mod n),P ≡ L (mod n),c属Z有:
N+c ≡ M+c (mod n)
Nc ≡ Mc (mod n)
N+P ≡ M+L (mod n)
对旋转数组的数学分析
原题
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
解法
将一数直接移动至最终位置,并用一变量储存原数,反复进行直到移动全部完成。
数学分析
设一数在数组中的初始指针为x,其末位置指针为y,a为任意数,b为移动次数,数组长度为n,
则有以下方程:
(x+k*b) mod n = y
因y < n,又有y mod n = y,故:
x+k*b ≡ y (mod n)
进而:
k*b ≡ y-x (mod n)
当x = y时,k*b ≡ 0 (mod n),此时指针回到初始位置,又有:
k*b = a*n
又因,当k与n已知时可变化为a关于b的线性函数:
b = a*(n/k)
a为任意数,n与k为正整数,函数在a≧ 0时连续单调递增。
这意味着指针始终可能回到初始位置。
因为二数共除其最大公约数后互质(其共同因数只有1),故:
k*b/gcd(n,k) = a*n/gcd(n,k),或k*b/gcd(a,k) = a*n/gcd(a,k)
但后者不便计算,所求得:
b = a*t/gcd(a,k)
当t=1, b = a/gcd(a,k)
故用前者,又有贝祖定理为:
当a与b互素时,方程a*x+b*y = m有解(m=dt,t属Z,d=gcd(a,b))
当x = 0时,by = m, d = b
//greatest common divisor(最大公约数)
得:b = a/gcd(n,k)
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
void swap(int* a, int* b) {
int t = *a;
*a = *b, *b = t;
}
void rotate(int* nums, int numsSize, int k) {
k = k % numsSize;
int count = gcd(k, numsSize);
for (int start = 0; start < count; ++start) {
int current = start;
int prev = nums[start];
do {
int next = (current + k) % numsSize;
swap(&nums[next], &prev);
current = next;
} while (start != current);
}
}