剑指offer题解
7 重建二叉树
根据前序和中序遍历,可以确定每颗子树根节点所在的位置,然后根据根节点,划分左右子树,之后再分别在左右子树中重复之前的划分过程。(递归实现)
private static class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
//递归求解,第一步划分
public static TreeNode constructBinaryTreeByPreInOrder(int[] preOrder,int[] inOrder,int preStart,int inStart,int length){
if (length ==0){
return null;
}
int rootIndex =0;
for (int i = inStart; i < inStart+ length; i++) {
if (preOrder[preStart] == inOrder[i]){
rootIndex = i;
break;
}
}
int left_length = rootIndex -inStart;
int right_length =length - left_length -1;
TreeNode root =new TreeNode(preOrder[preStart]);
root.left=constructBinaryTreeByPreInOrder(preOrder,inOrder,
preStart+1, inStart,left_length);
root.right=constructBinaryTreeByPreInOrder(preOrder,inOrder, preStart+left_length+1,rootIndex+1,right_length);
return root;
}
8 二叉树的下一个节点
分三种情况:
- 当前节点有右子树,下一个节点是右子树中最左的节点
- 无右子树
- 父节点的左孩子是当前节点,下一个节点是父节点
- 遍历该节点的父节点,直到父节点的左孩子是当前节点,下一个节点是父节点
- 向上一直找,返现node结点的父节点为null,说明无后继结点了
public class P08FindBinaryTreeNextNode {
private static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode parent;
public TreeNode(int val) {
this.val = val;
}
}
public static TreeNode findNextNode(TreeNode node){
if (node ==null){
return null;
}
//有右孩子,右孩子最左的节点
if (node.right !=null ){
return getMostLeft(node.right);
}else{
TreeNode parent = node.parent;
while (parent != null && parent.left != node){
node = parent;
parent = node.parent;
}
return parent;
}
}
//得到右子树最左的节点
private static TreeNode getMostLeft(TreeNode node){
if (node ==null){
return null;
}
while (node.left != null){
node = node.left;
}
return node;
}
}
9 用两个栈实现队列
创建两个栈push,pop栈,倒数据的操作
- 如果pop栈为空
- push 栈往pop栈里倒数据,直到为空
//两个栈实现队列
//方法appendTail,deleteHead
public class P09TwoStackToQueue {
private static Stack<Integer> pushStack;
private static Stack<Integer> popStack;
public P09TwoStackToQueue() {
pushStack =new Stack<Integer>();
popStack =new Stack<Integer>();
}
public static void appendTail(int val){
pushStack.push(val);
pushToPop();
}
public static int deleteHead(){
if (popStack.isEmpty() && pushStack.isEmpty()){
throw new RuntimeException("队列里没有东西删不掉呢");
}
pushToPop();
return popStack.pop() ;
}
//导数据的操作
private static void pushToPop(){
if (popStack.isEmpty()){
while (!pushStack.isEmpty()){
popStack.push(pushStack.pop());
}
}
}
}
用两个队列实现栈
引入队列queue1和queue2,每次pop操作,就将queue1中的节点都放入queue2中,直至queue1中的节点个数为1,然后再将queue1的节点poll,之后,再交换queue1和queue2中的值。peek操作类似。
public class P09_2TwoQueueToStack {
private Queue<Integer> queue;
private Queue<Integer> help;
public P09_2TwoQueueToStack() {
queue = new LinkedList<Integer>();
help = new LinkedList<Integer>();
}
public void push(int val) {
queue.offer(val);
}
public int pop() {
if (queue.isEmpty()) {
throw new RuntimeException("栈里无元素!!");
}
while (queue.size() > 1) {
help.offer(queue.poll());
}
int res = queue.poll();
swap();
return res;
}
public int peek() {
if (queue.isEmpty()) {
throw new RuntimeException("栈里没有元素可以看 !1");
}
int res = 0;
while (!queue.isEmpty()) {
res = queue.poll();
help.offer(res);
}
swap();
return res;
}
private void swap() {
Queue<Integer> temp = queue;
queue = help;
help = temp;
}
}
10 斐波那契数列
1.递归,时间复杂度O(2n)
2.循环,时间复杂度O(n)
public class P10Fibonacci {
//递归方法,时间复杂度很高,面试官很不喜欢
public static int fibonacciRecursive(int n){
if (n ==0){
return 0;
}
if (n==1){
return 1;
}
return fibonacciRecursive(n-1)+fibonacciRecursive(n-2);
}
//非递归,效率不是最高,很实用,面试官期待看到的
public static int fibonacciNoRecursive(int n){
int[] begin ={0,1};
if (0<=n&&n<2){
return begin[n];
}
int zero =0;
int one = 1;
int res =0;
for (int i = 2; i <= n; i++) {
res =zero+one;
zero =one;
one =res;
}
return res;
}
}
11 旋转数组的最小数字
解法:
- 暴力,时间复杂度O(n)!!简单但复杂度高,不够拿到offer
- 二分查找,时间复杂度O(logn)。使用两个指针p1,p2,然后根据计算的mid值来移动p1,p2。
- 当arr[p1]== arr[mid] == arr[p2]时,无法判断p1和p2属于哪个递增子数组,直接调用getMin,进行顺序查找。
- 当arr[mid]>=arr[p1]时,mid属于p1所在的递增子数组,令p1=mid,继续二分。
- 当arr[mid]<=arr[p2]时,处理过程和2类似。
public class P11FindMinNumInRotateArray {
public static int getMInNumber(int[] arr){
//判断极端情况
if (arr[0]<arr[arr.length-1]){
return arr[0];
}
int p1 = 0;
int p2 = arr.length-1;
int mid =0;
int min =arr[0];
while (arr[p1] >= arr[p2]){
if (p1 +1 ==p2){
min = arr[p2];
break;
}
mid =p1 +((p2-p1)>>1);
if (arr[p1]==arr[mid]&& arr[mid]==arr[p2]){
return getMin(arr,p1,p2);
}
if (arr[p1]<= arr[mid]){
p1 = mid;
}else if(arr[mid] <= arr[p2]){
p2 = mid;
}
}
return min;
}
private static int getMin(int[] arr,int p1,int p2){
int min =arr[p1];
for (int i = p1+1; i < p2; i++) {
if (min> arr[i]){
min = arr[i];
}
}
return min;
}
}