面试题27:二叉树的镜像
解题思路:遍历树,先交换根点的左右子节点的位置,继续遍历左右子节点,交换子节点的左右子节点位置,依次遍历完成。
面试题28:对称的二叉树
解题思路:利用树的遍历,前序遍历的特点是先遍历左子节点再遍历右子节点,重新设计一个对称遍历(先遍历右子节点再遍历左子节点),比较两个遍历过程中的值。
面试题29:顺时针打印矩阵
public void twentyNinth() {
int[][] matrix = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24}
};
printMatrixClockwisely(matrix);
}
private void printMatrixClockwisely(int[][] matrix) {
int rowMin = 0,
colMin = 0,
rowMax = matrix.length - 1,
colMax = matrix[0].length - 1;
int rowNum = 0;
int colNum = 0;
while (rowMin <= rowMax && colMin <= colMax) {
if (colMin <= colMax) {
for (int i = colNum; i <= colMax; i++) {
System.out.println(matrix[rowNum][i]);
}
colNum = colMax;
rowMin++;
}
if (rowMin <= rowMax) {
for (int i = rowNum + 1; i <= rowMax; i++) {
System.out.println(matrix[i][colNum]);
}
rowNum = rowMax;
colMax--;
}
if (colMin <= colMax) {
for (int i = colNum - 1; i >= colMin; i--) {
System.out.println(matrix[rowNum][i]);
}
colNum = colMin;
rowMax--;
}
if (rowMin <= rowMax) {
for (int i = rowNum - 1; i >= rowMin; i--) {
System.out.println(matrix[i][colNum]);
}
rowNum = rowMin;
colMin++;
colNum++;
}
}
}
面试题30:包含 min 函数的栈
题目:定义一个栈,调用 min、push、pop 的时间复杂度都是 O(1)。
解题思路:另外维护一个辅助栈用于存储最小值,push一个值时比较辅助栈的栈顶元素,若等于栈顶,插入栈顶元素到辅助栈,若小于,插入push值到辅助栈,若大于不操作。在pop的时候,比较pop出来的值和辅助栈顶元素,若大于不操作,等于就移除栈顶元素。min,直接返回辅助栈顶的元素。
面试题31:栈的压入、弹出序列
题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否是该栈的弹出顺序。
解题思路:利用一个辅助栈,按照压入顺序入辅助栈,按照弹出顺序出栈,若辅助栈弹出的元素与弹出顺序不一致即不满足,若结束后辅助栈仍然不为空也表示不满足。
public void thirtyFirst() {
int[] push = {1, 2, 3, 4, 5, 6};
int[] pop = {4, 5, 6, 3, 2, 1};
boolean flag = isPopOrder(push, pop, push.length);
System.out.println(flag);
int[] pop1 = {1, 4, 6, 5, 3, 2};
flag = isPopOrder(push, pop1, push.length);
System.out.println(flag);
int[] pop2 = {1, 4, 6, 5, 3, 1};
flag = isPopOrder(push, pop2, push.length);
System.out.println(flag);
}
private boolean isPopOrder(int[] push, int[] pop, int length) {
if (push == null || pop == null || push.length != pop.length) {
return false;
}
int i = 0, j = 0;
Stack<Integer> dataStack = new Stack<>();
for (i = 0; i < length; i++) {
dataStack.push(push[i]);
while (j < length && !dataStack.isEmpty()) {
Integer peek = dataStack.peek();
if (peek == pop[j]) {
dataStack.pop();
j++;
} else {
break;
}
}
}
return dataStack.isEmpty();
}
面试题32:从上到下打印二叉树
题目一:不分行从上到下打印二叉树。
题目二:分行从上到下打印二叉树。
题目三:之字形打印二叉树。
解题思路:利用辅助的队列,每层的所有元素入队列,记录层内的元素个数,元素出队列并打印,然后将左右子节点入队列,后面的逻辑类似。
public void thirtySecond3() {
TreeNode root = createNode(0, 7, 0, 7);
printTreeZigzag(root);
}
private void printTreeZigzag(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> treeNodes1 = new Stack<>();
Stack<TreeNode> treeNodes2 = new Stack<>();
treeNodes1.push(root);
int current = 0;
TreeNode pop;
while (!treeNodes1.isEmpty() || !treeNodes2.isEmpty()) {
if (current == 1) {
while (!treeNodes1.isEmpty()) {
pop = treeNodes1.pop();
System.out.print(pop.getValue() + " ");
if (pop.getLeft() != null) {
treeNodes2.push(pop.getLeft());
}
if (pop.getRight() != null) {
treeNodes2.push(pop.getRight());
}
}
System.out.println();
}
if (current == 0) {
while (!treeNodes2.isEmpty()) {
pop = treeNodes2.pop();
System.out.print(pop.getValue() + " ");
if (pop.getRight() != null) {
treeNodes1.push(pop.getRight());
}
if (pop.getLeft() != null) {
treeNodes1.push(pop.getLeft());
}
}
System.out.println();
}
current = 1 - current;
}
}
面试题33:二叉搜索树的后续遍历序列
题目:输入一个整数数组,判断这个数组是否是二叉搜索树的后序遍历。
解题思路:后序遍历的特点是先遍历左右子节点再遍历分支节点,数组的最后一个值就是根节点,从数组的下标0开始所有小于根节点的元素都是根节点的左子树的节点,大于的都是根节点的右子树中的节点,若右子树中存在比根节点小的就代表不满足,然后递归检查左右子树。
public void thirtyThird() {
int[] arrays = {5, 7, 6, 9, 11, 10, 8};
boolean b = verifyTreeBST(arrays, 0, arrays.length - 1);
System.out.println(b);
}
private boolean verifyTreeBST(int[] arrays, int begin, int end) {
if (arrays == null || begin > end) {
return false;
}
if (begin == end) {
return true;
}
int i = begin;
for (; i < end; i++) {
if (arrays[i] > arrays[end]) {
break;
}
}
int j = i;
for (; j < end; j++) {
if (arrays[j] < arrays[end]) {
return false;
}
}
boolean left = true;
if (i - begin > 1) {
left = verifyTreeBST(arrays, begin, i-1);
}
boolean right = true;
if (end - i > 1) {
right = verifyTreeBST(arrays, i, end - 1);
}
return left && right;
}
面试题34:二叉树中和为某一值的路径
题目:输入一棵二叉树和一个整数,输出节点值和为输入值的所有路径。从根节点开始一直往下到叶子结点所进过的节点形成一条路径。
解题思路:利用一个辅助栈存储前序遍历经过的节点,若到达叶子结点就判断路径和是否满足条件,不满足就弹出此叶子节点返回上一级。
public void thirtyForth() {
TreeNode root = createNode(0, 7, 0, 7);
Stack<Integer> dataStack = new Stack<>();
findPath(root, dataStack, 0, 9);
}
private void findPath(TreeNode node, Stack<Integer> dataStack, int currentNum, int exceptedNum) {
if (node == null) {
return;
}
currentNum += node.getValue();
//中序遍历
dataStack.push(node.getValue());
boolean isLeaf = node.getLeft() == null && node.getRight() == null;
if (isLeaf && currentNum == exceptedNum) {
dataStack.forEach(System.out::print);
}
if (node.getLeft() != null) {
findPath(node.getLeft(), dataStack, currentNum, exceptedNum);
}
if (node.getRight() != null) {
findPath(node.getRight(), dataStack, currentNum, exceptedNum);
}
dataStack.pop();
}
面试题35:复杂链表的复制
题目:复杂链表中除了有一个 next 指针,还有一个 m_pSibling 指向链表中任意一个节点。
解题方案1:先复制整个 next 链表,再沿着 next 指针设置每个节点的 m_pSibling,时间复杂度O(n2);
解题方案2:不沿着 next 指针找 m_pSibling,使哈希表存储,空间复杂度O(n);
解题方案3:在原链表的基础上,在每个节点后下个节点前插入一个新节点,维护一个长的链表,再设置 m_pSibling ,最后断开链表。
@Data
@Builder
private static class ComplexNode {
private Integer value;
private ComplexNode next;
private ComplexNode sibling;
@Override
public String toString() {
return "ComplexNode{" +
"value=" + value +
", next=" + next +
", sibling=" + (sibling == null ? null : sibling.getValue().toString()) +
'}';
}
}
public void thirtyFifth() {
ComplexNode headNode = createComplexNode();
//复制链表中的每一个节点
CloneNodes(headNode);
//设置复制的节点的 sibling 指针
ConnectSibling(headNode);
//重新连接,拆分为两个链表
ComplexNode cloneHeadNode = ReconnectNodes(headNode);
System.out.println(cloneHeadNode);
}
private ComplexNode createComplexNode() {
//next 1->2->3->5
//sibling 2->5
//sibling 3->1
ComplexNode node1 = ComplexNode.builder().value(1).build();
ComplexNode node2 = ComplexNode.builder().value(2).build();
ComplexNode node3 = ComplexNode.builder().value(3).build();
ComplexNode node5 = ComplexNode.builder().value(5).build();
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node5);
node2.setSibling(node5);
node3.setSibling(node1);
return node1;
}
private void CloneNodes(ComplexNode headNode) {
while (headNode != null) {
ComplexNode cloneNode = ComplexNode.builder()
.value(headNode.getValue())
.next(headNode.getNext())
.sibling(headNode.getSibling())
.build();
headNode.setNext(cloneNode);
headNode = cloneNode.getNext();
}
}
private void ConnectSibling(ComplexNode headNode) {
while (headNode != null) {
ComplexNode cloneNode = headNode.getNext();
if (headNode.getSibling() != null) {
cloneNode.setSibling(headNode.getSibling().getNext());
}
headNode = cloneNode.getNext();
}
}
private ComplexNode ReconnectNodes(ComplexNode headNode) {
ComplexNode cloneHeadNode = null;
ComplexNode node = null;
if (headNode != null) {
cloneHeadNode = headNode.getNext();
node = cloneHeadNode;
}
while (headNode != null) {
headNode.setNext(node.getNext());
headNode = node.getNext();
if (headNode != null) {
node.setNext(headNode.getNext());
node = node.getNext();
} else {
node.setNext(null);
}
}
return cloneHeadNode;
}
面试题36:二叉搜索树与双向链表
题目:将一棵二叉搜索树转换成一个排序的双向链表。
public void thirtySixth() {
TreeNode selectTree = createSelectTree();
System.out.println(selectTree);
TreeNode lastNodeInList = convertNode(selectTree, null);
while (lastNodeInList != null && lastNodeInList.getLeft() != null) {
lastNodeInList = lastNodeInList.getLeft();
}
System.out.println(lastNodeInList);
}
/**
* 10
* 6 14
* 4 8 12 16
*
* @return 二叉查找树
*/
private static TreeNode createSelectTree() {
TreeNode node4 = TreeNode.builder().value(4).build();
TreeNode node8 = TreeNode.builder().value(8).build();
TreeNode node6 = TreeNode.builder().value(6).left(node4).right(node8).build();
TreeNode node12 = TreeNode.builder().value(12).build();
TreeNode node16 = TreeNode.builder().value(16).build();
TreeNode node14 = TreeNode.builder().value(14).left(node12).right(node16).build();
TreeNode rootNode = TreeNode.builder().value(10).left(node6).right(node14).build();
return rootNode;
}
private TreeNode convertNode(TreeNode treeNode, TreeNode lastOfList) {
if (treeNode == null) {
return null;
}
if (treeNode.getLeft() != null) {
lastOfList = convertNode(treeNode.getLeft(), lastOfList);
}
if (lastOfList != null) {
lastOfList.setRight(treeNode);
}
treeNode.setLeft(lastOfList);
lastOfList = treeNode;
if (treeNode.getRight() != null) {
lastOfList = convertNode(treeNode.getRight(), lastOfList);
}
return lastOfList;
}
面试题38:字符串的排列
题目1:打印字符串中所有字符的排列。
public void thirtyEighth1() {
//全排列就是从第一个数字起每个数分别与它后面的数字交换
permutationCombination("abA".toCharArray(), 0);
}
private static void permutationCombination(char[] chars, int beginIndex) {
if (chars == null) {
return;
}
if (beginIndex == chars.length) {
for (int i = 0; i < chars.length; i++) {
System.out.print(chars[i]);
}
System.out.println();
} else {
for (int i = beginIndex; i < chars.length; i++) {
char temp = chars[i];
chars[i] = chars[beginIndex];
chars[beginIndex] = temp;
permutationCombination(chars, beginIndex + 1);
// 下面的代码作用是换回来,此步骤必须
temp = chars[i];
chars[i] = chars[beginIndex];
chars[beginIndex] = temp;
}
}
}
题目2:打印字符串中所有字符的组合。
public void thirtyEighth2() {
char[] chars = "abcd".toCharArray();
Stack<Character> stack = new Stack<>();
for (int i = 1; i <= chars.length; i++) {
permutation(chars, 0, i, stack);
}
}
private static void permutation(char[] chars, int beginIndex, int m, Stack<Character> stack) {
// 分别求n-1个字符串中长度为m-1的组合,以及n-1个字符串中长度为m的组合
// 若组合中包含第一个字符,则在剩下的字符中选取m-1个字符;若不包含,则在剩下的n-1个字符中选取m个字符。
if (m > 0) {
if (beginIndex == chars.length) {
return;
}
stack.push(chars[beginIndex]);
permutation(chars, beginIndex + 1, m - 1, stack);
stack.pop();
permutation(chars, beginIndex + 1, m, stack);
} else {
System.out.println(stack.toString());
}
}
题目2:另类求解
/**
* 字符串长度为m的所有组合(位运算)
*/
private static void permutation1(char[] chars) {
if (chars == null) {
return;
}
int length = chars.length;
StringBuilder stringBuilder = new StringBuilder(length);
// n位可以表示2^n个数,需要排除0 ,可能的组合结果有 (2^n)-1 种
for (int i = 1; i < (1 << length); i++) {
for (int j = 0; j < length; j++) {
if ((i & (1 << j)) > 0) {
stringBuilder.append(chars[j]);
}
}
System.out.println(stringBuilder.toString());
stringBuilder.delete(0, length);
// 需要确定那些位为1
}
}
题目3:八皇后问题
public void thirtyEighth4() {
permutationCombination("12345678".toCharArray(), 0);
}
private static void permutationCombination(char[] chars, int beginIndex) {
if (chars == null) {
return;
}
if (beginIndex == chars.length) {
if (check(chars)) {
for (int i = 0; i < chars.length; i++) {
System.out.print(chars[i]);
}
System.out.println();
}
} else {
for (int i = beginIndex; i < chars.length; i++) {
char temp = chars[i];
chars[i] = chars[beginIndex];
chars[beginIndex] = temp;
permutationCombination(chars, beginIndex + 1);
// 下面的代码作用是换回来,此步骤必须
temp = chars[i];
chars[i] = chars[beginIndex];
chars[beginIndex] = temp;
}
}
}
private static boolean check(char[] chars) {
for (int i = 0; i < chars.length; i++) {
for (int j = i + 1; j < chars.length; j++) {
if ((i - j == chars[i] - chars[j]) || (j - i == chars[i] - chars[j])) {
return false;
}
}
}
return true;
}