liuxuhelloworld's notebook

题目链接

https://leetcode-cn.com/problems/gray-code/

解法1

一开始面对这个题目,没有什么思路,只想到比较暴力的深度优先。维持一个list存储当前累加的结果,一个当前处理对象。下一个对象怎么得到呢?既然相邻元素只能有1位二进制位不同,那就从低位到高位遍历,每次改1个二进制位,新值如果已经在list中,就尝试下一个位置,如果不在list中,就作为下一个对象,进行下一次递归。当list中元素个数已经满了,直接返回。

public List<Integer> grayCodeV1(int n) {
		assert n >= 1 && n <= 16;

		List<Integer> grayCodes = new ArrayList<>();

		int limit = 1;
		for (int i = 1; i <= n; i++) {
			limit *= 2;
		}

		Integer cur = 0;
		grayCodes.add(cur);

		dfs(grayCodes, cur, n, limit);

		return grayCodes;
	}

	private void dfs(List<Integer> bits, Integer cur, int n, int limit) {
		if (bits.size() == limit) {
			return;
		}

		for (int i = 0; i < n; i++) {
			Integer oneBitChange = cur ^ (1 << i);

			if (!bits.contains(oneBitChange)
				&& bits.size() < limit) {
				bits.add(oneBitChange);
				dfs(bits, oneBitChange, n, limit);
			}
		}
	}

这种解法从正确性上应该是没问题的,感觉也比较容易理解,但是问题是效率较差,有太多重复计算,提交后果然超时。

解法2

超时报错后研究了下代码,感觉也没什么地方可以再优化了。耐心耗尽,直接看了题解,才意识到应该使用动态规划。那么从S(n)怎么得到S(n+1)呢?S(n)顺序挨个高位补1位二进制0,结果和S(n)一样,接着,倒序挨个高位补1位二进制1,并追加到S(n)末尾,这样就得到了S(n+1)

public List<Integer> grayCodeV2(int n) {
		assert n >= 1 && n <= 16;

		List<Integer> res = new ArrayList<>();
		res.add(0);

		for (int i = 0; i < n; i++) {
			int op = 1 << i;
			int len = res.size();
			for (int j = len-1; j >= 0; j--) {
				res.add(res.get(j) | op);
			}
		}

		return res;
}

倒序遍历高位补1这个思路,我觉得还是有点难度的,不太容易一下想到。