Skip to content

Latest commit

 

History

History

PascalsTriangleII

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

Pascal's Triangle II

Given an index k, return the kth row of the Pascal's triangle.

For example, given k = 3, Return [1,3,3,1].

Note: Could you optimize your algorithm to use only O(k) extra space?

Solution

根据Pascal's Triangle分析,很容易求出第k行.

注意题目k从0开始计算,因此第k行有k + 1个数。

使用迭代的方式求:后面的一个数等于前面的数乘以一个分数,这个分数从左到右分别为n/1, (n-1)/2, ..., 2/(n-1), 1/n

比如求第5行:

  • 初始化第一项为1
  • 分数从左到右为5/1, 4/2, 3/3, 2/4, 1/5, 因此从左到右的数字为1 * 5/1 == 5, 5 * 4/2 == 10, 10 * 3/3 = 10, 10 * 2/4 == 5, 5 * 1/5 == 1, 最后结果为1,5,10,10,5,1

因此代码很容易为:

vector<int> getRow(int n) {
	vector<int> result(n + 1, 0);
	result[0] = 1;
	for (int i = 1; i <= n; ++i) {
		result[i] = result[i - 1] * (n - i + 1) / i;
	}
	return result;
}

但注意当前一个数乘以分子时可能会溢出,因此先需要约分。。

约分需要求最大公约数。如何求两个数的最大公约数?

最简单的方法是使用辗转相除法,即

int gcd(int x, int y)
{
	return (!y) ? x : gcd(y, x % y);
}

但我们使用了模运算,开销大。

我们使用类似辗转相除法分析,如果一个数能够同时整除x和y,则必能同时整除x-y和y,而能够同时整除x-y和y也能够整除x和y,即x和y的公约数与x-y和y的公约数相同。即gcd(x, y) = gcd(x - y, y).

int gcd(int x, int y)
{
	if (x < y)
		return gcd(y, x);
	if (y == 0)
		return x;
	return gcd(x - y, y);
}

但是虽然使用减法代替了除法运算,但是减法的迭代次数太多了。

从分析公约数的特定入手:

  • 对于Y和X,若Y = k * x, X = k * x, 则gcd(Y, X) = k * gcd(y, x)
  • X = p * x, 假设p是素数, 并且Y % p != 0(y不能整除p), 则gcd(X, Y) = gcd(p * x, Y) = gcd(x, Y)
  • 同理当Y = p * y的情况

注意到以上三点后,就可以大幅度对算法改进。

最简单的方法是,2是素数,同时除以2可以使用移位操作,因此我们可以取p = 2

  • 若x,y均是偶数, gcd(x, y) = gcd(x >> 1, y >> 1) << 1
  • 若x为偶数,y为奇数, gcd(x, y) = gcd(x >> 1, y)
  • 若y为偶数,x为奇数, gcd(x, y) = gcd(x, y >> 1)
  • 若x,y都是奇数, gcd(x, y) = gcd(y, x - y)

最坏情况下时间复杂度O(log(max(x, y)))

int gcd(int a, int b)
{
	if (a < b)
		return gcd(b, a);
	if (b == 0)
		return a;
	if ((a & 0x1) == 0) {
		if ((b & 0x1) == 0)
			return gcd(a >> 1, b >> 1) << 1;
		else
			return gcd(a >> 1, b);
	} else {
		if ((b & 0x1) == 0)
			return gcd(a, b >> 1);
		else
			return gcd(b, a - b);
	}
}

最后代码为:

vector<int> getRow(int n) {
	vector<int> result(n + 1, 0);
	result[0] = 1;
	for (int i = 1; i <= n; ++i) { // 必须约分, 否则溢出
		int molecular = (n - i + 1), denominator = i;
		int g = gcd(molecular, denominator);
		molecular /= g;
		denominator /= g;
		int multiplier = result[i - 1];
		g = gcd(multiplier, denominator);
		multiplier /= g;
		denominator /= g;
		int value = multiplier * molecular / denominator;
		result[i] = value;
	}
	return result;
}

扩展

关于杨辉三角性质Pascal's Triangle