第四章
/** * 面试题27 输入一颗二叉树,输出一颗对称的二叉树 * 思路:从根结点开始,交换两个子结点 * * @param root */ public void mirror(TreeNode root) { if (root == null) return; TreeNode temp = root.left; root.left = root.right; root.right = temp; mirror(root.left); mirror(root.right); }
/** * 面试题28:判断输入二叉树是否是对称二叉树 * 思路:将前序遍历和前序对称遍历获取的结点值进行比较 * * @param root * @return */ public boolean isSym(TreeNode root) { //invalid input if (root == null) return false; return isSymCore(root, root); } private boolean isSymCore(TreeNode root1, TreeNode root2) { //递归结束条件 if (root1 == null && root2 == null) return true; if (root1 == null || root2 == null) return false; if (root1.val != root2.val) return false; //root1 and root2 是对称遍历 return isSymCore(root1.left, root2.right) && isSymCore(root1.right, root2.left); }
/** * 面试题30:含有O(1)min方法的栈 * 思路:建立一个辅助栈,该栈中栈顶元素始终是当前数据栈中的最小元素。 */ private Stack<Integer> dateStack = new Stack<>(); private Stack<Integer> minStack = new Stack<>(); public void push(int date) { //入栈的数据是当前最小值,注意minStack为空的情况 if (date < minStack.peek() || minStack.empty()) { minStack.push(date); } else minStack.push(minStack.peek()); dateStack.push(date); } public int pop() { minStack.pop();//栈为空时抛出异常 return dateStack.pop(); } public int min() { return minStack.peek(); }
/** * 面试题31:判断压入栈和弹出栈是否匹配 * 思路:建立一个辅助栈 * 当辅助栈顶元素值为空,或者与出栈序列不同时,入栈序列入栈 * 相同时,出栈序列出栈,辅助栈出栈 * * @param pushA * @param popA * @return */ public boolean IsPopOrder(int[] pushA, int[] popA) { //invalid input if (pushA == null || popA == null) return false; int pushALen = pushA.length; int popALen = popA.length; if (pushALen != popALen) return false; //匹配 int pushAptr = 0; int popAptr = 0; Stack<Integer> stack = new Stack<>(); while (popAptr != popALen) { if (stack.empty() || stack.peek() != popA[popAptr]) { if (pushAptr == pushALen) return false; else stack.push(pushA[pushAptr++]); } else { stack.pop(); ++popAptr; } } return true; }
/** * 面试题32: 从上到下打印二叉树 * 思路:用队列保存下一层要打印的结点 * 从队列中出一个结点,打印后将该结点的左右子结点加入队列中,直到队列为空结束 * <p> * 面试题32(题目二): 从上到下分行打印二叉树 * 思路:在队列中添加分行标志位 * * @param root * @return */ public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { ArrayList<Integer> result = new ArrayList<>(); //save result //invalid input if (root == null) return result; LinkedList<TreeNode> queue = new LinkedList<>();//assist queue queue.offer(root); while (!queue.isEmpty()) { TreeNode temp = queue.poll(); result.add(temp.val); if (temp.left != null) queue.offer(temp.left); if (temp.right != null) queue.offer(temp.right); } return result; }
/** * 面试题33:判断数组是否为搜索二叉树的后序遍历序列 * 思路: * 1 数组最后一个元素为该树的根节点 * 2 根据该根节点将数组前面的元素划分为左子树部分和右子树部分 * 3 递归调用,判断是否是后序遍历 * * @param sequence * @return */ public boolean VerifySquenceOfBST(int[] sequence) { //invalid input if (sequence == null) return false; int length = sequence.length; if (length == 0) return false; return VerifySquenceOfBSTCore(sequence, 0, length - 1); } private boolean VerifySquenceOfBSTCore(int[] sequence, int lo, int hi) { if (lo < 0 || hi < 0 || lo >= hi) return true; int rootVal = sequence[hi]; //根结点值 int leftroot = -1; int rightroot = -1; if (sequence[lo] < rootVal) leftroot = lo; int i = -1;//迭代变量 for (i = lo; i < hi; i++) { //找第一个大于根节点的值 if (sequence[i] > rootVal) { rightroot = i; break; } } for (i = i + 1; i < hi; i++) {//判断后面的元素是否均大于根结点值 if (sequence[i] <= rootVal) return false; } return VerifySquenceOfBSTCore(sequence, leftroot, rightroot - 1) && VerifySquenceOfBSTCore(sequence, rightroot, hi - 1); }
/** * 面试题34:二叉树中和为某一值的路径 * 技巧,可以用三层树型结构来代测试 * 思路: * 1 递归的寻找路径,在遍历过程中记录经过的路劲 * 2 递归到叶结点时判断该路径是否符合条件,并添加到结果数组中 * * @param root * @param target * @return */ public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) { ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); if (root == null) return result; ArrayList<Integer> path = new ArrayList<>(); findPath(root, target, path, result); return result; } private void findPath(TreeNode root, int target, ArrayList<Integer> path, ArrayList<ArrayList<Integer>> result) { if (root == null) return; if (root.left == null && root.right == null) { //叶结点 path.add(root.val); if (root.val == target) result.add(new ArrayList<Integer>(path)); path.remove(path.size() - 1); return; } path.add(root.val); findPath(root.left, target - root.val, path, result); findPath(root.right, target - root.val, path, result); path.remove(path.size() - 1); }
/** * 面试题35:复杂链表的复制 * 思路: * 1 在更复杂链表中插入相同的结点 * 2 连接自由结点 * 3 分离两条链表 * * @param pHead * @return */ public RandomListNode Clone(RandomListNode pHead) { if (pHead == null) return null; // * 1 在更复杂链表中插入相同的结点 RandomListNode copyNode = pHead; while (copyNode != null) { RandomListNode node = new RandomListNode(copyNode.label); node.next = copyNode.next; copyNode.next = node; copyNode = copyNode.next.next; } // * 2 连接自由结点 copyNode = pHead; while (copyNode != null) { copyNode.next.random = copyNode.random == null ? null : copyNode.random.next; copyNode = copyNode.next.next; } // * 3 分离两条链表 copyNode = pHead; RandomListNode node = pHead.next; RandomListNode result = pHead.next; while (copyNode.next.next != null) { copyNode.next = copyNode.next.next; node.next = node.next.next; copyNode = copyNode.next; node = node.next; } copyNode.next = null; //容易忽视 return result; }
/** * 面试题36:将二叉搜索树转化成一个排序的双向链表 * 思路 * 1 中序遍历递归地构造双向链表,并返回双向链表的尾部 * * @param pRootOfTree * @return */ private TreeNode tail = null; //链表的尾部 private TreeNode head = null; //链表的头部 public TreeNode Convert(TreeNode pRootOfTree) { ConvertCore(pRootOfTree); return head; } private void ConvertCore(TreeNode node) { if (node == null) return; ConvertCore(node.left); if (tail == null) { tail = node; head = node; } else { tail.right = node; node.left = tail; tail = node; } ConvertCore(node.right); }
/** * 面试题38:打印输入字符串的所有排列 * 递归法,问题转换为先固定第一个字符,求剩余字符的排列;求剩余字符排列时跟原问题一样。 * (1) 遍历出所有可能出现在第一个位置的字符(即:依次将第一个字符同后面所有字符交换); * (2) 固定第一个字符,求后面字符的排列(即:在第1步的遍历过程中,插入递归进行实现)。 * 需要注意的几点: * (1) 先确定递归结束的条件; * (2) 去重复; * (3) 按字典顺序排列; * @param str * @return */ public ArrayList<String> Permutation(String str) { char[] strArr = str.toCharArray(); ArrayList<String> result = new ArrayList<>(); if(str==null||str.length()==0) return result; Set<String> res = new TreeSet<>(); //利用TreeSet集合类来实现去重复和排序 Permutation(strArr, 0, res); result.addAll(res); return result; } private void Permutation(char[] strArr, int index, Set<String> result) { if (index == strArr.length) { result.add(new String(strArr)); return; } for (int i = index; i < strArr.length; i++) { exch(strArr, index, i); //确定一个位置的字符 Permutation(strArr, index + 1, result); exch(strArr, index, i); } } private void exch(char[] arr, int i, int j) { char temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }