问题的遍历可以通过按顺序尝试每一个锁的所有可能,但是因为需要记录转动次数。尝试可能性的时候并不能很好的记录转动次数。
以转动次数为遍历的线,将每转动一次的所有可能进行记录,并以此为基础计算下一次转动的所有可能性。
当惯用的遍历方式不适合解题时,找到矛盾的点,从矛盾点出发考虑新的遍历方式。
当视角放在局部(一个锁一个锁的尝试可能性)不能很好地解决问题时,将视角拉到整体(转动一次整体的所有可能)。
找到矛盾点,视角的灵活转换从来都是解决问题的根本性思路。
JAVA:
public final int openLock(String[] deadends, String target) { Set<String> deadSet = new HashSet<String>(), history = new HashSet<String>(); for (int i = 0; i < deadends.length; i++) deadSet.add(deadends[i]); Queue<String> queue = new LinkedList<String>(); insert("0000", queue, deadSet, history); int currStep = 0; while (!queue.isEmpty()) { int len = queue.size(); for (int i = 0; i < len; i++) { String current = queue.poll(); if (target.equals(current)) return currStep; for (int j = 0; j < 4; j++) { String right = this.moveRight(current, j); String left = this.moveLeft(current, j); this.insert(right, queue, deadSet, history); this.insert(left, queue, deadSet, history); } } currStep++; } return -1; } private final void insert(String source, Queue<String> queue, Set<String> deads, Set<String> history) { if (deads.contains(source) || history.contains(source)) return; history.add(source); queue.offer(source); } private final String moveLeft(String source, int target) { return this.move(source, target, -1); } private final String moveRight(String source, int target) { return this.move(source, target, 1); } private final String move(String source, int target, int added) { int pre = source.charAt(target) - '0'; int next = (pre + added + 10) % 10; String re = source.substring(0, target) + next + source.substring(target + 1, source.length()); return re; }
JS:
/** * @param {string[]} deadends * @param {string} target * @return {number} */ var openLock = function (deadends, target) { if ("0000" == target) return 0; let deadSet = new Set(), his = new Set(), queue = [], re = 0; for (let i = 0; i < deadends.length; i++) deadSet.add(deadends[i]); if (deadSet.has("0000")) return -1; queue.push("0000"); his.add("0000"); while (queue.length > 0) { let currentLen = queue.length; for (let j = 0; j < currentLen; j++) { let currentVal = queue.shift(); for (let k = 0; k < 4; k++) { let left = spanLeft(currentVal, k), right = spanRight(currentVal, k); if (!his.has(left) && !deadSet.has(left)) { his.add(left); queue.push(left); if (target == left) return re + 1; } if (!his.has(right) && !deadSet.has(right)) { queue.push(right); his.add(right); if (target == right) return re + 1; } } } re++; } return -1; }; var spanRight = function (pre, point) { return span(pre, point, 1); } var spanLeft = function (pre, point) { return span(pre, point, -1); } var span = function (pre, point, added) { let curr = (parseInt(pre.charAt(point)) + added + 10) % 10; return pre.substring(0, point) + curr + pre.substring(point + 1, pre.length) }