剑指offer

来源

牛客网上的题目与牛友解析评论

入门

斐波那契数列

题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n≤39

示例1
输入
4
返回值
3

描述
此题是非常经典的入门题了。我记得第一次遇到此题是在课堂上,老师拿来讲“递归”的(哈哈哈)。同样的类型的题还有兔子繁殖的问题。大同小异。此题将用三个方法来解决,从入门到会做。
考察知识:递归,记忆化搜索,动态规划和动态规划的空间优化。
难度:一星

题解
方法一:递归
题目分析,斐波那契数列公式为:f[n] = f[n-1] + f[n-2], 初始值f[0]=0, f[1]=1,目标求f[n]
看到公式很亲切,代码秒秒钟写完。

int Fibonacci(int n) {
    if (n==0 || n==1) return n;
    return Fibonacci(n-1) + Fibonacci(n-2);
}

优点,代码简单好写,缺点:慢,会超时
时间复杂度:O(2^n)
空间复杂度:递归栈的空间

方法二:记忆化搜索
拿求f[5] 举例
在这里插入图片描述

通过图会发现,方法一中,存在很多重复计算,因为为了改进,就把计算过的保存下来。
那么用什么保存呢?一般会想到map, 但是此处不用牛刀,此处用数组就好了。

int Fib(int n, vector<int>& dp) {
    if (n==0 || n==1) return n;
    if (dp[n] != -1) return dp[n];
    return dp[n] = Fib(n-1) + Fib(n-2);
}
int Fibonacci(int n) {
    vector<int> dp(45, -1); // 因为答案都是>=0 的, 所以初始为-1,表示没计算过
    return Fib(n, dp);
}

时间复杂度:O(n), 没有重复的计算
空间复杂度:O(n)和递归栈的空间

方法三:动态规划
虽然方法二可以解决此题了,但是如果想让空间继续优化,那就用动态规划,优化掉递归栈空间。
方法二是从上往下递归的然后再从下往上回溯的,最后回溯的时候来合并子树从而求得答案。
那么动态规划不同的是,不用递归的过程,直接从子树求得答案。过程是从下往上。

int Fibonacci(int n) {
    vector<int> dp(n+1, 0);
        dp[1] = 1;
        for (int i=2; i<=n; ++i) {
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n];
}

时间复杂度:O(n)
空间复杂度:O(n)

继续优化
发现计算f[5]的时候只用到了f[4]和f[3], 没有用到f[2]…f[0],所以保存f[2]…f[0]是浪费了空间。
只需要用3个变量即可。

int Fibonacci(int n) {
     if (n == 0 || n == 1) return n;
        int a = 0, b = 1, c;
        for (int i=2; i<=n; ++i) {
            c = a + b;
            a = b;
            b = c;
        }
        return c;
}

时间复杂度:O(n)
空间复杂度:O(1)
完美!

通过的代码

public class Solution {
    public int Fibonacci(int n) {
        int a=1;
        int b=1;
        int c=1;
        if(n==0){
            return 0;
        }
        if(n<3){
            return 1;
        }
        for(int i=2;i<=n;i++){
            b=c;
            a=c-a;
            c=a+b;
        }
        return c;
         
    }
}
public class Solution {
    public int Fibonacci(int n) {
        int[] array = new int[n+1];
        if (n == 0 || n == 1) return n;
        if (n > 39) return 0;
        array[0] = 0;
        array[1] = 1;
        for (int i = 2; i <= n; i++) {
            array[i] = array[i-1] + array[i-2];
        }
        return array[n];
    }
}
import java.util.Scanner;
public class Solution {
    public int Fibonacci(int n) {
int a[] = new int[n+1];
       if (n==1 || n==2){
           a[n] = 1;
       }
       if (n>2){
           a[1] = a[2] = 1;
           for (int i = 3; i <= n ;i++){
               a[i] = a[i-1] + a[i-2];
               a[n] = a[i];
           }
 
       }
       return a[n];
    }
    public static void main(String[] s){
        Solution t = new Solution();
        Scanner scanner = new Scanner(System.in);
        int fibonacci = t.Fibonacci(scanner.nextInt());
        System.out.println(fibonacci);
    }
}
public class Solution {
    public int Fibonacci(int n) {
        switch(n){
            case 0:
                return 0;
            case 1:
                return 1;
            case 2:
                return 1;
            default:
                int [] a=new int[n+1];
                a[0]=0;
                a[1]=1;
                for(int i=2;i<n+1;i++){
                    a[i]=a[i-1]+a[i-2];
                }
                return a[n];
        }
    }
}
public class Solution {
    public int Fibonacci(int n) {
        int[] array = new int[40];
        array[0] = 0;
        array[1] = 1;
        for(int i=2;i<40;i++){
            array[i]=array[i-1]+array[i-2];
        }
        return array[n];
    }
}
public class Solution {
    public int Fibonacci(int n) {
        if(n == 2 || n == 1){
            return 1;
        }
        int q1 = 1;
        int q2 = 1;
        int temp = 0;
        int val = 0;
        for(int i = 2; i < n; i++){
            val = q1 + q2;
            temp = q1;
            q1 = val;
            q2 = temp;
        }
 
        return val;
    }
}
public class Solution {
    public int Fibonacci(int n) {
   int[] array=new int[40];
        array[0]=0;
        array[1]=1;
        for(int i=2;i<=n;i++){
            array[i]=array[i-1]+array[i-2];
            
        }
             
                return array[n];
    }  
}
public class Solution {
    public int Fibonacci(int n) {
        if(n == 0){
            return 0;
        }else if(n == 1){
            return 1;
        }
        int sum = 1;
        int one = 0;
        for(int i=2;i<=n;i++){
            sum = sum + one;
            one = sum - one;
        }
        return sum;
    }
}
public class Solution {
    int[] dp = new int[40];
    public int Fibonacci(int n) {
        dp[1] = 1;
        dp[2] = 1;
        for(int i = 3; i <=n ; i++){
            dp[i] = dp[i-1] + dp[i-2];
        }
         
        return dp[n];
         
        
    }
}

简单

用两个栈实现队列

题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

题解
描述
这是一道对栈和队列之间灵活转化的题目。
难度:一星
考察知识:队列,栈

题解
方法:模拟
如果我知道队列是FIFO,栈是FILO,但是这道题我还是不知道怎么写怎么办?
对于这种感觉不难,但是又不会写的,方法就是模拟。
比如有如下操作:(pop操作确保栈中有元素)

push(1);push(2);pop(3);push(4);

在这里插入图片描述

根据队列的特性,只能pop(1),pop(2),pop之后的结果

在这里插入图片描述

上述是队列的操作。
当push的时候,我们必须要用一个stack来存,假设用stack1来存。
在这里插入图片描述

那么push操作解决了。那么pop操作怎么办呢?
如果pop(1),但是此时在stack1的栈底,如果要pop,必须再将stack1中的数据push到stack2中,然后在pop,如图

在这里插入图片描述

这样直接弹出stack2的栈顶就可以了。
如果要继续pop,那就继续弹出stack2就可以了

但是现在总感觉哪里还是有点问题。如果是这样就继续测试几个例子。
如果push(5),
在这里插入图片描述

所以最后总结一下:push操作就直接往stack1中push, pop操作需要分类一下:如果stack2为空,那么需要将stack1中的数据转移到stack2中,然后在对stack2进行pop,如果stack2不为空,直接pop就ok。

复杂度分析
时间复杂度:push操作为O(1),pop操作为O(1)
空间复杂度:需要stack来存,O(n)

代码

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }
 
    int pop() {
        if (stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int ret = stack2.top();
        stack2.pop();
        return ret;
    }
 
private:
    stack<int> stack1;
    stack<int> stack2;
};

题解
分析
队列的特性是:“先入先出”,栈的特性是:“先入后出”

当我们向模拟的队列插入数 a,b,c 时,假设插入的是 stack1,此时的栈情况为:

  • 栈 stack1:{a,b,c}
  • 栈 stack2:{}

当需要弹出一个数,根据队列的"先进先出"原则,a 先进入,则 a 应该先弹出。但是此时 a 在 stack1 的最下面,将 stack1 中全部元素逐个弹出压入 stack2,现在可以正确的从 stack2 中弹出 a,此时的栈情况为:

  • 栈 stack1:{}
  • 栈 stack2:{c,b}

继续弹出一个数,b 比 c 先进入"队列",b 弹出,注意此时 b 在 stack2 的栈顶,可直接弹出,此时的栈情况为:

  • 栈 stack1:{}
  • 栈 stack2:{c}

此时向模拟队列插入一个数 d,还是插入 stack1,此时的栈情况为:

  • 栈 stack1:{d}
  • 栈 stack2:{c}

弹出一个数,c 比 d 先进入,c 弹出,注意此时 c 在 stack2 的栈顶,可直接弹出,此时的栈情况为:

  • 栈 stack1:{d}
  • 栈 stack2:{c}

根据上述栗子可得出结论:

  1. 当插入时,直接插入 stack1
  2. 当弹出时,当 stack2 不为空,弹出 stack2 栈顶元素,如果 stack2 为空,将stack1 中的全部数逐个出栈入栈stack2,再弹出 stack2 栈顶元素

代码

import java.util.Stack;
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
 
    public void push(int node) {
        stack1.push(node);
    }
 
    public int pop() {
        if (stack2.size() <= 0) {
            while (stack1.size() != 0) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

复杂度
push时间复杂度:
pop空间复杂度:

通过的代码

import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
      
    public void push(int node) {
        stack1.push(node);
    }
      
    public int pop() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }else{
            return stack2.pop();
        }
    }
}
import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
         stack1.push(node);
    }
     
    public int pop() {
                if (stack2.size() <= 0) {
            while (stack1.size() != 0) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}
import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.push(node);
    }
     
    public int pop() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }else{
            return stack2.pop();
        }
    }
}
import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.push(node);
    }
     
    public int pop() {
        if(stack2.isEmpty()){//注意:栈2为空时,才能将栈1中的所有元素弹出到栈2
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
//         if(stack2.isEmpty()){//此时栈2依旧为空,则返回-1
//             return -1;
//         }else{
//             return stack2.pop();//删除队首元素就是将此时的栈2栈顶元素弹出即可
//         }
    }
}
import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
 
    public void push(int node) {
        stack1.push(node);
    }
 
    public int pop() {
        if (!stack2.isEmpty()){
            return stack2.pop();
        }else {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}
import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.push(node);
    }
     
    public int pop() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}
import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.push(node);
    }
    //只改变出即可
    public int pop() throws Exception{
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        if(stack2.isEmpty()){
            throw new Exception("queue is Empty!");
        }
        return stack2.pop();
    }
}
import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.push(node);
    }
     
    public int pop() {
        if(stack1.isEmpty() && stack2.isEmpty()){
            throw new RuntimeException("The queue is enpty");
        }
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}
import java.util.Stack;
 
public class Solution {
    //思路:s1 作为出队的栈   s2作为入队的栈
    //入队:入到s2里
    //出队:出s1,如果s1为空,则将s2元素导入到s1中
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack2.push(node);
    }
     
    public int pop() {
        if(stack1.isEmpty()){
            while(!stack2.isEmpty()){
                stack1.push(stack2.pop());
            }
        }
        if(stack1.isEmpty()){//该情况说明在没有数据的情况下调用pop,应该抛异常
            return -1;
        }
        return stack1.pop();
    }
}
import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.add(node);
    }
     
    public int pop() {
        if (stack2.size() == 0 && stack1.size() == 0){
            throw new RuntimeException("queue is empty!");
        }
        if (stack2.size() == 0) {
            while (stack1.size()  != 0){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

旋转数组的最小数字

题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
示例1
输入
[3,4,5,1,2]
返回值
1

题解
描述
这是一道对二分查找算法灵活运用的一道题目。
二分查找算法不限于运用在有序数组上。如果能够明确二分之后,答案存在于二分的某一侧,就可以使用二分。本题就是如此。
难度:二星
考察知识:数组,二分查找

题解
方法一:暴力方法:
直接遍历一遍数组,即可找到最小值。但是本题的附加条件就没有用上。肯定不是面试官所期望的答案。

方法二:二分查找
这种二分查找难就难在,arr[mid]跟谁比.
我们的目的是:当进行一次比较时,一定能够确定答案在mid的某一侧。一次比较为 arr[mid]跟谁比的问题。
一般的比较原则有:

  • 如果有目标值target,那么直接让arr[mid] 和 target 比较即可。
  • 如果没有目标值,一般可以考虑端点

这里我们把target 看作是右端点,来进行分析,那就要分析以下三种情况,看是否可以达到上述的目标。

  1. 情况1,arr[mid] > target:4 5 6 1 2 3
    arr[mid] 为 6, target为右端点 3,arr[mid] > target, 说明[first … mid] 都是 >= target的,因为原始数组是非递减,所以可以确定答案为 [mid+1…last]区间,所以 first = mid + 1
  2. 情况2,arr[mid] < target:5 6 1 2 3 4
    arr[mid] 为 1, target为右端点 4,arr[mid] < target, 说明答案肯定不在[mid+1…last],但是arr[mid]有可能是答案,所以答案在[first, mid]区间,所以last = mid;
  3. 情况3,arr[mid] == target:
    如果是1 0 1 1 1, arr[mid] = target = 1, 显然答案在左边
    如果是 1 1 1 0 1, arr[mid] =target = 1, 显然答案在右边 所以这种情况,不能确定答案在左边还是右边,那么就让last = last -1;慢慢缩少区间,同时也不会错过答案。

接下来我们用个例子来说明一下:
在这里插入图片描述

误区:那我们肯定在想,能不能把左端点看成target, 答案是不能。
原因:
情况1 :1 2 3 4 5 , arr[mid] = 3. target = 1, arr[mid] > target, 答案在mid 的左侧
情况2 :3 4 5 1 2 , arr[mid] = 5, target = 3, arr[mid] > target, 答案却在mid 的右侧
所以不能把左端点当做target

复杂度分析
时间复杂度:二分,所以为O(longN), 但是如果是[1, 1, 1, 1],会退化到O(n)
空间复杂度:没有开辟额外空间,为O(1)

代码

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if (rotateArray.size() == 0) return 0;
        int first = 0, last = rotateArray.size() - 1;
        while (first < last) { // 最后剩下一个元素,即为答案
            if (rotateArray[first] < rotateArray[last]) { // 提前退出
                return rotateArray[first];
            }
            int mid = first + ((last - first) >> 1);
            if (rotateArray[mid] > rotateArray[last]) { // 情况1
                first = mid + 1;
 
            }
            else if (rotateArray[mid] < rotateArray[last]) { //情况2
                last = mid;
            }
            else { // 情况3
                --last;
            }
        }
        return rotateArray[first];
    }
};

通过的代码

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if (array.length == 0) {
            return 0 ;
        }
        int l = 0 ;
        int r = array.length - 1 ;
        int m = 0 ;
        while (array[l] >= array[r]) {
            if (r - l == 1) {
                m = r ;
                break ;
            }
            m = l + (r - l) / 2 ;
            if (array[l] <= array[m]) {
                l = m ;
            }
            if (array[r] >= array[m]) {
                r = m ;
            }
        }
        return array[m] ;
    }
}
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
    int length=array.length;
        if (length==0){
            return 0;
        }
 
        if (length==1){
            return array[0];
        }
 
        int left=0;
        int right=length-1;
        while (left<right){
            int midIndex=left+(right-left)/2;
            if (array[midIndex]>array[right]){
                left=midIndex+1;
            }else if (array[midIndex]==array[right]){
                right--;
            }else {
                right=midIndex;
            }
        }
        return array[left];
    }
}
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        //可以这样做,但是没有优化,拿不到offer
        /*if (array.length == 0 || array == null)
            return 0;
        int a=array[0];
        for (int i=0; i<array.length; i++) {
            if (a > array[i])
                a = array[i];
        }
        return a;*/
         
        //用二分查找
        //注意:非递减表示可以递增或相等
        if (array.length == 0 || array == null)
            return 0;
        int l=0, r=array.length-1;
        while(l < r) {
            if (array[l] < array[r])    //防止出现 1 0 1 1 1这种情况
                return array[l];
            int mid=(l+r)/2;
            if (array[l] < array[mid])    //说明最小元素一定在右边
                l=mid+1;
            else if (array[r] > array[mid])    //说明最小元素一定在左边
                r=mid;
            else
                l++;    //否则往后遍历继续二分
        }
        return array[l];
    }
}
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0) return 0;
        int right=array.length-1;
        int target=array[right];
        int left=0;
        while(left<right){
            if(array[left]<array[right]) return array[left];
            int mid=(left+right)/2;
            if(array[mid]>target){
                left=mid+1;
            }
            else if(array[mid]<target){
                right=mid;
            }
            else{
                left++;
            }
        }
        return array[left];
    }
}
import java.util.ArrayList;
public class Solution {
    // 1, 2,3, 4, 5
    public int minNumberInRotateArray(int [] array) {
        if (array.length == 0) {
            return 0;
        }
        int left  =0, right = array.length-1;
        while (left < right) {
            if (right - left < 2) {
                return Math.min(array[left], array[right]);
            }
            int mid = (left + right) / 2;
            if (array[left] < array[right]) { // 有序
                right = mid;
            }else { //旋转
                if (array[mid] < array[right]) {
                    right = mid;
                }else {
                    left = mid;
                }
            }
        }
        return array[left];
    }
}
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
           if(array.length == 0) return 0;
        int f = 0;
        int e = array.length - 1;
        
        while(f < e) {
            if(array[f] < array[e]) return array[f];
           int m = (f+e)/2;
            if(array[f] < array[m]) {
                f = m + 1;
            } else if(array[e] > array[m]) {
                e = m;
            } else {
                f++;
            }     
        }
        return array[f];
    }
}
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
            if (array.length == 0) {
                return 0;
            }
 
            int minValue = array[0];
            int l = 0, r = array.length - 1;
 
            while (l <= r) {
                final int mid = l + (r - l) / 2;
 
                if (array[mid] < array[l]) {    // 至少右边是有序的
                    minValue = array[mid];
                    r = mid - 1;
                }
                else if (array[mid] > array[r]) {// 至少左边是有序的
                    minValue = array[l];
                    l = mid + 1;
                }
                else {  // 无法判断到底哪边有序,比如 [0, 0, 0, 0, 0, 0, 1, 0]
                    if (array[r] < minValue) {
                        minValue = array[r];
                    }
                    r --;
                }
            }
            return minValue;
    }
}
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0||array==null)return 0;
         
        int low=0,high=array.length-1;
         
        while(low<high){//二者相等就跳出循环
            if(array[low]<array[high])return array[low];//防止出现 1 0 1 1 1这种情况
             
            int mid=(high+low)/2;
            if(array[mid] > array[low])
                low = mid + 1;
            else if(array[mid] < array[high])
                high = mid;
            else low++;//其余情况 无法判定左右的有序情况
        }
        return array[low];
    }
}
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int p1 = 0;
        int p2 = array.length-1;
        /*while(array[p1]==array[p2]){
            p1++;
            p2--;
        }*/
        while(p1<p2){
            int mid = (p2-p1)/2+p1;
            if (array[p1] < array[p2]) {
                return array[p1];
            }
            if(array[mid]<array[p1]){
                p2=mid;
            }else if(array[mid]>array[p1]) {
                 p1 = mid+1;
            }else{
                p1++;
            }
        }   
     return array[p1];     
    }
}
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0) return 0;
        if(array.length==1) return array[0];
        int a=array[0];
        int l=0,r=array.length-1,mid=(l+r)/2;
        while(l!=r){
            if(array[mid]>=a){l=mid+1;mid=(l+r)/2;}
            else if(array[mid]<a){r=mid;mid=(l+r)/2;}
        }
        return Math.min(array[l],a);
    }
}

跳台阶扩展问题

题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
示例1
输入
3
返回值
4

题解
描述:
这是一道可以递归,记忆化递归,动态规划,递推思想的题目。
知识点:递归,动态规划,递推
难度:一星

题解:
方法一:暴力方法
设f[i] 表示 当前跳道第 i 个台阶的方法数。那么f[n]就是所求答案。
假设现在已经跳到了第 n 个台阶,那么前一步可以从哪些台阶到达呢?
如果上一步跳 1 步到达第 n 个台阶,说明上一步在第 n-1 个台阶。已知跳到第n-1个台阶的方法数为f[n-1]
如果上一步跳 2 步到达第 n 个台阶,说明上一步在第 n-2 个台阶。已知跳到第n-2个台阶的方法数为f[n-2]
。。。
如果上一步跳 n 步到达第 n 个台阶,说明上一步在第 0 个台阶。已知跳到 第0个台阶的方法数为f[0]
那么总的方法数就是所有可能的和。也就是f[n] = f[n-1] + f[n-2] + … + f[0]
显然初始条件f[0] = f[1] = 1
所以我们就可以先求f[2],然后f[3]…f[n-1], 最后f[n]

代码:

int jumpFloorII(int n) {
    if (n==0 || n==1) return 1;
    vector f(n+1, 0);
    f[0] = f[1] = 1;
    for (int i=2; i<=n; ++i) {
        for (int j=0; j<i; ++j) {
            f[i] += f[j];
        }
    }
    return f[n];
}

复杂度分析:
时间复杂度:O(n2)
空间复杂度:O(n)

方法二:继续优化
对于方法一中的:f[n] = f[n-1] + f[n-2] + … + f[0]
那么f[n-1] 为多少呢?
f[n-1] = f[n-2] + f[n-3] + … + f[0]
所以一合并,f[n] = 2*f[n-1],初始条件f[0] = f[1] = 1
所以可以采用递归,记忆化递归,动态规划,递推。具体详细过程,可查看青蛙跳台阶。
这里直接贴出递推的代码。

代码:

int jumpFloorII(int n) {
    if (n==0 || n==1) return 1;
    int a = 1, b;
    for (int i=2; i<=n; ++i) {
        b = a << 1; //  口诀:左移乘2,右移除2
        a = b;
    }
    return b;
}

当然,你会发现一个规律:
f[0] = f[1] = 1
f[2] = 2 = 21
f[3] = 4 = 22
f[4] = 8 = 23

f[n] = 2n-1

所以,针对本题还可以写出更加简单的代码。

int jumpFloorII(int n) {
     if (n == 0 || n == 1) return 1;
     return static_cast(pow(2, n-1));
}

复杂度分析:
时间复杂度:O(n)
空间复杂度:O(1)

变态跳台阶
易知 f(n)=f(n-1)+f(n-2)+……f(1)
f(n-1)=f(n-2)+……f(1)
两式相减得f(n)=2f(n-1)

复制代码

 -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        n=1
        for i in range(2,number+1):
            n=2*n
        return n

通过的代码

public class Solution {
    public int jumpFloorII(int target) {
        int res = 0;
        int sum = 0;
        int i = 1;
        while(i <= target) {
            res = sum + 1;
            sum += res;
            i++;
        }
        return res;
    }
}
public class Solution {
    public int jumpFloorII(int target) {
        if(target<=1) {
            return 1;
        }
        return 2*jumpFloorII(target-1);
    }
}
public class Solution {
    public int jumpFloorII(int target) {
        if(target<=2) {
            return target;
        }
         
        return 2*jumpFloorII(target-1);
    }
}
public class Solution {
    public int jumpFloorII(int target) {
        int sum=1;
        for(int i=2;i<=target;i++){
            sum*=2;
        }
        return sum;
    }
}
public class Solution {
    public int jumpFloorII(int target) {
        if(target==1||target==2){
            return target;
        }
        int a=1;
        int b=2;
        for(int i=2;i<target;i++){
            a=b;
            b=b<<1;
        }
        return b;
    }
}
public class Solution {
    public int jumpFloorII(int target) {
        int a = 1;
//         int b = 1;
        for(int i=2;i<=target;i++){
            a = a<<1;
        }
        return a;
    }
}
public class Solution {
    public int jumpFloorII(int target) {
        if(target <= 0){
            return -1;
        }else if(target == 1){
            return 1;
        }else{
            return 2 * jumpFloorII(target - 1);
        }
    }
}
public class Solution {
    public int jumpFloorII(int target) {
        if(target == 0|| target == 1) return 1;
        int x = 1;
        for(int i = 2;i<=target;i++){
            x = x * 2;
        }
         
        return x;
    }
  }
public class Solution {
    public int jumpFloorII(int target) {
        if(target < 2) return 1;
        return (int)Math.pow(2,target-1);
    }
}
public class Solution {
    public int jumpFloorII(int target) {
        if(target<=0)return 0;
        if(target==1)return 1;
        int b=2;
        int result=2;
        for(int i=2;i<target;i++){
            result=result*b;
        }
        return result;
    }

合并两个排序的链表

题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
示例1
输入
{1,3,5},{2,4,6}
返回值
{1,2,3,4,5,6}
说明:本题目包含复杂数据结构ListNode

题解
描述
这是一篇针对初学者的题解,共用2种方法解决。
知识点:单链表,递归
难度:一星

题解:
题目要求:给两个非递减单链表l1, l2,合并为一个非递减的单链表。

方法一:迭代版本求解
初始化:定义cur指向新链表的头结点
操作:

  1. 如果l1指向的结点值小于等于l2指向的结点值,则将l1指向的结点值链接到cur的next指针,然后l1指向下一个结点值
  2. 否则,让l2指向下一个结点值
  3. 循环步骤1,2,直到l1或者l2为nullptr
  4. 将l1或者l2剩下的部分链接到cur的后面

技巧
一般创建单链表,都会设一个虚拟头结点,也叫哨兵,因为这样每一个结点都有一个前驱结点。

代码

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        ListNode *vhead = new ListNode(-1);
        ListNode *cur = vhead;
        while (pHead1 && pHead2) {
            if (pHead1->val <= pHead2->val) {
                cur->next = pHead1;
                pHead1 = pHead1->next;
            }
            else {
                cur->next = pHead2;
                pHead2 = pHead2->next;
            }
            cur = cur->next;
        }
        cur->next = pHead1 ? pHead1 : pHead2;
        return vhead->next;
    }
};

时间复杂度:O(m+n),m,n分别为两个单链表的长度
空间复杂度:O(1)

方法二:递归版本
方法一的迭代版本,很好理解,代码也好写。但是有必要介绍一下递归版本,可以练习递归代码。
写递归代码,最重要的要明白递归函数的功能。可以不必关心递归函数的具体实现。
比如这个ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
函数功能:合并两个单链表,返回两个单链表头结点值小的那个节点。

如果知道了这个函数功能,那么接下来需要考虑2个问题:

  1. 递归函数结束的条件是什么?
  2. 递归函数一定是缩小递归区间的,那么下一步的递归区间是什么?
    对于问题1.对于链表就是,如果为空,返回什么
    对于问题2,跟迭代方法中的一样,如果PHead1的所指节点值小于等于pHead2所指的结点值,那么phead1后续节点和pHead节点继续递归

代码

class Solution {
public:
 ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
 {
     if (!pHead1) return pHead2;
     if (!pHead2) return pHead1;
     if (pHead1->val <= pHead2->val) {
         pHead1->next = Merge(pHead1->next, pHead2);
         return pHead1;
     }
     else {
         pHead2->next = Merge(pHead1, pHead2->next);
         return pHead2;
     }
 }
};

时间复杂度:O(m+n)
空间复杂度:O(m+n),每一次递归,递归栈都会保存一个变量,最差情况会保存(m+n)个变量

通过的代码

/*
public class ListNode {
    int val;
    ListNode next = null;
 
    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode newHeadNode = new ListNode(0);
        ListNode tempNode = newHeadNode;
        while(null!=list1&&null!=list2){
            if(list1.val>=list2.val){
                tempNode.next = list2;
                tempNode = tempNode.next;
                list2 = list2.next;
            }else{
                tempNode.next = list1;
                tempNode = tempNode.next;
                list1 = list1.next;
            }
        }
        if(null == list1){
            tempNode.next = list2;
        }else if(null == list2){
            tempNode.next = list1;
        }
        return newHeadNode.next;
    }
}
/*
public class ListNode {
    int val;
    ListNode next = null;
 
    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
         
       ListNode ans=null;
       if(list1==null) return list2;
       if(list2==null) return list1;
       if(list1.val<list2.val){
            ans=list1;
            list1=list1.next;
           }else{
            ans=list2;
            list2=list2.next;
        }
        ListNode t=ans;
       while(list1!=null && list2!=null){
          if(list1.val<=list2.val){
            ans.next=list1;
              ans=ans.next;
            list1=list1.next;
           }else{
            ans.next=list2;
              ans=ans.next;
            list2=list2.next;
        }
       }
        while(list1!=null){
            ans.next=list1;
            ans=ans.next;
            list1=list1.next;
        }
            
        while(list2!=null){
            ans.next=list2;
            ans=ans.next;
            list2=list2.next;}
       
            return t;
    }
}
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode head = new ListNode(0);
        ListNode p = head;
        while(list1!=null && list2!=null){
            if(list1.val<list2.val){
                p.next = list1;
                list1=list1.next;
            }else{
                p.next = list2;
                list2=list2.next;
            }
            p=p.next;
        }
        if(list1==null) p.next = list2;
        if(list2==null) p.next = list1;
        return head.next;
    }
}
public class Solution {
 
    public ListNode Merge(ListNode listA, ListNode listB) {
        // 非空判断
        if(listA == null) {
            return listB;
        }
 
        if(listB == null) {
            return listA;
        }
 
        // 创建新链表的头,尾指针
        ListNode newHead = null, newTail = null, tempNode;
 
        while(listA != null && listB != null) {
            if(listA.val <= listB.val) {
                tempNode = listA;
                listA = listA.next;
            } else {
                tempNode = listB;
                listB = listB.next;
            }
            // 第一次 newHead 和 newTail 都为空
            if(newHead == null) {
                newHead = tempNode;
                newTail = newHead;
            } else {
                newTail.next = tempNode;
                newTail = newTail.next;
            }
        }
        newTail.next = listA != null ? listA : listB;
 
        return newHead;
    }
}
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode head = new ListNode(-1);
        ListNode cur = head;
        while(list1 != null && list2 != null){
            if(list1.val <= list2.val){
                cur.next = list1;
                list1 = list1.next;
            } else {
                cur.next = list2;
                list2 = list2.next;
            }
            cur = cur.next;
        }
        if(list1 == null){
            cur.next = list2;
        } else {
            cur.next = list1;
        }
        return head.next;
    }
}
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null) return list2;
        if(list2==null) return list1;
        ListNode Merge_=null;
        if(list1.val<list2.val){
            Merge_=list1;
            Merge_.next=Merge(list1.next,list2);
        }else{
            Merge_=list2;
            Merge_.next=Merge(list1,list2.next);        
        }
        return Merge_;
    }
}
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
      if(list1==null) return list2;
      if(list2==null) return list1;
       
      if(list1.val<list2.val){
          list1.next = Merge(list1.next,list2);
          return list1;
      }else{
          list2.next = Merge(list1,list2.next);
          return list2;
      }   
    }
}
//递归
//找到res和res.next即可
public class Solution {
    public static ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null){
            return list2;
        }
        if(list2==null){
            return list1;
        }
        ListNode res=null;
        if(list1.val< list2.val){
            res=list1;
            res.next=Merge(list1.next,list2);
        }else{
            res=list2;
            res.next=Merge(list1,list2.next);
        }
        return res;
    }
}
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if (list1 == null || list2 == null)
            return list1 == null ? list2 : list1;
        if (list1.val < list2.val) {
            ListNode second = Merge(list1.next, list2);
            list1.next = second;
            return list1;
        }
        else {
            ListNode second = Merge(list1, list2.next);
            list2.next = second;
            return list2;
        }
    }
}
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode rst= new ListNode(0), p;
        p = rst;
        while(list1!=null && list2!=null){
            if(list1.val<list2.val){
                p.next = list1;
                list1 = list1.next;
                p = p.next;
            }
            else{
                p.next = list2;
                list2 = list2.next;
                p=p.next;
            }
        }
        p.next = list2==null ? list1:list2;
        return rst.next;
    }
}

二叉树的镜像

题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
比如: 源二叉树
8
/
6 10
/ \ /
5 7 9 11
镜像二叉树
8
/
10 6
/ \ /
11 9 7 5
示例1
输入
{8,6,10,5,7,9,11}
返回值
{8,10,6,11,9,7,5}
说明:本题目包含复杂数据结构TreeNode

题解
使用递归思想:
先镜像当前root左右节点,然后再递归镜像左孩子和右孩子即可。
代码如下:

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if(pRoot == null) return pRoot;
        TreeNode temp= pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = temp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    }
}

【数据结构和算法】BFS,DFS,递归等多种实现方式,图文详解
1,BFS解决
之前讲373,数据结构-6,树的时候,提到过二叉树的广度优先搜索,就是一层一层的访问,像下面这样
在这里插入图片描述
二叉树的BFS代码如下

public static void treeBFS(TreeNode root) {
    //如果为空直接返回
    if (root == null)
        return;
    //队列
    Queue<TreeNode> queue = new LinkedList<>();
    //首先把根节点加入到队列中
    queue.add(root);
    //如果队列不为空就继续循环
    while (!queue.isEmpty()) {
        //poll方法相当于移除队列头部的元素
        TreeNode node = queue.poll();
        //打印当前节点
        System.out.println(node.val);
        //如果当前节点的左子树不为空,就把左子树
        //节点加入到队列中
        if (node.left != null)
            queue.add(node.left);
        //如果当前节点的右子树不为空,就把右子树
        //节点加入到队列中
        if (node.right != null)
            queue.add(node.right);
    }
}

这题要求的是输出二叉树的镜像,就是每一个节点的左右子节点进行交换,随便画个二叉树看一下
在这里插入图片描述
我们需要遍历每一个节点,然后交换他的两个子节点,一直循环下去,直到所有的节点都遍历完为止,代码如下

public TreeNode Mirror(TreeNode root) {
    //如果为空直接返回
    if (root == null)
        return null;
    //队列
    final Queue<TreeNode> queue = new LinkedList<>();
    //首先把根节点加入到队列中
    queue.add(root);
    while (!queue.isEmpty()) {
        //poll方法相当于移除队列头部的元素
        TreeNode node = queue.poll();
        //交换node节点的两个子节点
        TreeNode left = node.left;
        node.left = node.right;
        node.right = left;
        //如果当前节点的左子树不为空,就把左子树
        //节点加入到队列中
        if (node.left != null) {
            queue.add(node.left);
        }
        //如果当前节点的右子树不为空,就把右子树
        //节点加入到队列中
        if (node.right != null) {
            queue.add(node.right);
        }
    }
    return root;
}

2,DFS解决
无论是BFS还是DFS都会访问到每一个节点,访问每个节点的时候交换他的左右子节点,直到所有的节点都访问完为止,代码如下

public TreeNode Mirror(TreeNode root) {//DFS
    //如果为空直接返回
    if (root == null)
        return null;
    //栈
    Stack<TreeNode> stack = new Stack<>();
    //根节点压栈
    stack.push(root);
    //如果栈不为空就继续循环
    while (!stack.empty()) {
        //出栈
        TreeNode node = stack.pop();
        //子节点交换
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
        //左子节点不为空入栈
        if (node.left != null)
            stack.push(node.left);
        //右子节点不为空入栈
        if (node.right != null)
            stack.push(node.right);
    }
    return root;
}

3,中序遍历解决
这题其实解法比较多,只要访问他的每一个节点,然后交换子节点即可,我们知道二叉树不光有BFS和DFS访问顺序,而且还有前序遍历,中序遍历和后续遍历等,不管哪种访问方式,只要能把所有节点都能访问一遍然后交换子节点就能解决,我们这里就以中序遍历来看下,前序和后序就不在看了。在373,数据结构-6,树中,提到二叉树中序遍历的非递归写法如下

public static void inOrderTraversal(TreeNode tree) {
    Stack<TreeNode> stack = new Stack<>();
    while (tree != null || !stack.isEmpty()) {
        while (tree != null) {
            stack.push(tree);
            tree = tree.left;
        }
        if (!stack.isEmpty()) {
            tree = stack.pop();
            System.out.println(tree.val);
            tree = tree.right;
        }
    }
}

我们来对他改造一下,就是在访问每个节点的时候交换,代码如下

public static TreeNode Mirror(TreeNode root) {
    //如果为空直接返回
    if (root == null)
        return null;
    Stack<TreeNode> stack = new Stack<>();
    TreeNode node = root;
    while (node != null || !stack.isEmpty()) {
        while (node != null) {
            stack.push(node);
            node = node.left;
        }
        if (!stack.isEmpty()) {
            node = stack.pop();
            //子节点交换
            TreeNode temp = node.left;
            node.left = node.right;
            node.right = temp;
            //注意这里以前是node.right,因为上面已经交换了
            //,所以这里要改为node.left
            node = node.left;
        }
    }
    return root;
}

4,递归方式解决
二叉树中序遍历的递归代码如下

public void inOrderTraversal(TreeNode node) {
    if (node == null)
        return;
    inOrderTraversal(node.left);
    System.out.println(node.val);
    inOrderTraversal(node.right);
}

上面说了,只要能访问二叉树的每一个节点,然后交换左右子节点就行了,这里就以二叉树中序遍历递归的方式来看下

public TreeNode Mirror(TreeNode root) {
    if (root == null)
        return null;
    Mirror(root.left);
    //子节点交换
    TreeNode temp = root.left;
    root.left = root.right;
    root.right = temp;
    //上面交换过了,这里root.right要变成root.left
    Mirror(root.left);
    return root;
}

再来看一个后续遍历的

public TreeNode Mirror(TreeNode root) {
    if (root == null)
        return null;
    TreeNode left = Mirror(root.left);
    TreeNode right = Mirror(root.right);
    root.left = right;
    root.right = left;
    return root;
}

5,总结
这题没什么难度,但解法比较多,主要是因为二叉树的遍历方式比较多,如果每一种方式递归和非递归都写的话就更多了,这里不在一直写下去了。

通过的代码

import java.util.*;
/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        TreeNode tmp;
        if(pRoot == null) return pRoot;
        tmp = pRoot.right;
        pRoot.right = pRoot.left;
        pRoot.left = tmp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        if(pRoot == null){
            return null;
        }
         
        if(pRoot != null){
            TreeNode temp = pRoot.left;
            pRoot.left = pRoot.right;
            pRoot.right = temp;
             
            if(pRoot.left != null){
                Mirror(pRoot.left);
            }
             
            if(pRoot.right != null){
                Mirror(pRoot.right);
            }
        }
        return pRoot;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if(pRoot==null)
            return null;
        if(pRoot.left!=null){
            Mirror(pRoot.left);
        }
        if(pRoot.right!=null){
            Mirror(pRoot.right);
        }
        TreeNode temp;
        temp=pRoot.left;
        pRoot.left=pRoot.right;
        pRoot.right=temp;
         
        return pRoot;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
         
        if (pRoot == null)
            return null;
        // write code here
        
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        TreeNode temp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = temp;
        return pRoot;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
  //  Queue<TreeNode> q=new Queue<>();
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if(pRoot==null) return pRoot;
        TreeNode temp=pRoot.left;
        pRoot.left=pRoot.right;
        pRoot.right=temp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
         
        }
}
public class Solution {
    public TreeNode Mirror (TreeNode pRoot) {
        if (pRoot == null) return null;
        if (pRoot.left == null && pRoot.right == null) return pRoot;
        TreeNode temp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = temp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    TreeNode temp;
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if (pRoot == null) {
            return pRoot;
        }
        temp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = temp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        if(pRoot == null) {
            return null;
        }
        TreeNode temp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = temp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
         
        return pRoot;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if (pRoot == null) return pRoot;
        if(pRoot!=null){
            TreeNode temp = pRoot.left;
            pRoot.left = pRoot.right;
            pRoot.right = temp;
        }
        if(pRoot.left != null){
            Mirror(pRoot.left);
        }
        if(pRoot.right != null){
            Mirror(pRoot.right);
        }
        return pRoot;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param pRoot TreeNode类
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if(pRoot == null) return pRoot;
        TreeNode temp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = temp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    }
}

数组中出现次数超过一半的数字

题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
示例1
输入
[1,2,3,2,2,2,5,4,2]
返回值
2

题解
描述
这是一篇针对初学者的题解。共用三种方法解决。
知识点:数组,排序,哈希
难度:一星

题解
题目抽象:给定一个数组,找出数组中的众数,若有,返回众数,若没有,返回0
众数定义:数组中出现次数大于数组一般的元素

方法一:哈希法
根据题目意思,显然可以先遍历一遍数组,在map中存每个元素出现的次数,然后再遍历一次数组,找出众数。

代码

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        unordered_map<int,int> mp;
        for (const int val : numbers) ++mp[val];
        for (const int val : numbers) {
            if (mp[val] > numbers.size() / 2 ) return val;
        }
        return 0;
    }
};

时间复杂度:O(n)
空间复杂度:O(n)

方法二:排序法
可以先将数组排序,然后可能的众数肯定在数组中间,然后判断一下。

代码

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        sort(numbers.begin(), numbers.end());
        int cond = numbers[numbers.size() / 2];
        int cnt = 0;
        for (const int k :numbers) {
            if (cond == k) ++cnt;
        }
        if (cnt > numbers.size() / 2) return cond;
        return 0;
    }
};

时间复杂度:O(nlongn)
空间复杂度:O(1)

方法三:候选法(最优解)
加入数组中存在众数,那么众数一定大于数组的长度的一半。
思想就是:如果两个数不相等,就消去这两个数,最坏情况下,每次消去一个众数和一个非众数,那么如果存在众数,最后留下的数肯定是众数。

具体做法:

  1. 初始化:候选人cond = -1, 候选人的投票次数cnt = 0
  2. 遍历数组,如果cnt=0, 表示没有候选人,则选取当前数为候选人,++cnt
  3. 否则,如果cnt > 0, 表示有候选人,如果当前数=cond,则++cnt,否则–cnt
  4. 直到数组遍历完毕,最后检查cond是否为众数

代码

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int cond = -1;
        int cnt = 0;
        for (int i=0; i<numbers.size(); ++i) {
            if (cnt == 0) {
                cond = numbers[i];
                ++cnt;
            }
            else {
                if (cond == numbers[i]) ++cnt;
                else --cnt;
            }
        }
        cnt = 0;
        for (const int k :numbers) {
            if (cond == k) ++cnt;
        }
        if (cnt > numbers.size() / 2) return cond;
        return 0;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)

通过的代码

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if (array.length == 0) {
            return 0;
        }
        int temp = array[0];
        int count = 1;
        for (int i = 1; i < array.length; i++) {
            if (array[i] == temp) {
                count++;
            } else {
                count--;
            }
            if (count == 0) {
                temp = array[i];
                count = 1;
            }
        }
        count = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == temp) {
                count++;
            }
        }
        return count > array.length / 2 ? temp : 0;
    }
}
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        int len=array.length;
        if(len==0) return 0;
        int ret=0,res=array[0],count=1;
        for(int i=1;i<len;i++){
            if(count==0){
                res=array[i];
                count++;
                continue;
            }
            if(array[i]!=res){
                count--;
            }else count++;
        }
        for(int num:array){
            if(num==res) ret++;
        }
        return ret>(len>>1)?res:0;
    }
}

import java.util.HashMap;
 
public class Solution {
     
    public int MoreThanHalfNum_Solution(int [] array) {
        HashMap<Integer, Integer> hashMap = new HashMap();
         
        for (int i  = 0; i < array.length; ++i){
            hashMap.put(array[i], hashMap.getOrDefault(array[i], 0) + 1);
             
            if (hashMap.get(array[i]) > array.length / 2){
                return array[i];
            }
        }
        return 0;
    }
}
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
       if (array.length == 0) {
            return 0;
        }
        int temp = array[0];
        int count = 1;
        for(int num : array){
            if(count == 0) temp = num;
            count += num == temp ? 1 : -1;
        }
         
        count = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == temp) {
                count++;
            }
        }
        return count > array.length / 2 ? temp : 0;
    }
}


public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array==null||array.length==0)return 0;
        int val = array[0];
        int count  = 1;
        for(int i = 1;i<array.length;i++){
            if(array[i]==val){
                count++;
            }else{
                count--;
                if(count==0){
                    val = array[i];
                    count=1;
                }
            }
        }
        int num = 0;
        for(int i = 0;i<array.length;i++){
            if(array[i]==val){
                num++;
            }
        }
        return num>array.length/2?val:0;
    }
}
import java.util.HashMap;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if (array.length == 0 || array == null) return 0;
        int index = array[0];
        int count = 1;
        for (int i = 1; i < array.length; i++) {
            if (array[i] == index) count ++;
            else {
                count --;
                if (count == 0) {
                    index = array[i];
                    count = 1;
                }
            }
        }
        count = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == index) count++;
        }
        if (count > array.length / 2) return index;
        return 0;
    }
}
public class Solution {
     private boolean slove(int key, int[] array, int length) {
        boolean flag = false;
        int count = 0;
        for (int i = 0 ;i<length;i++){
            if(key==array[i]){
              count++;
            }
        }
        if (count>length/2){
            flag=true;
        }
        return flag;
    }
    public int MoreThanHalfNum_Solution(int [] array) {
        //一 一对比,计数,只有一个
        //想到一对一的map数据结构
        int length = array.length;
 
        for (int i = 0 ;i<length;i++){
            if(slove(array[i],array,length)){
                return array[i];
            };
        }
    return 0;
    }
 
}
import java.util.HashMap;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length==0) return 0;
        int pre = array[0];
        int count=1;
        for(int i=1; i<array.length; i++){
            if(pre==array[i]){
                ++count;
            }else if(--count<0) {
                pre = array[i];
                count = 1;
            }
        }
        count = 0;
        for(int i=0; i<array.length; i++){
            if(array[i]==pre) count++;
        }
        return count>array.length/2? pre: 0;
    }
}
import java.util.HashMap;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if (array.length == 0) return 0;
        if (array.length == 1) return array[0];
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < array.length; i++) {
            int key = array[i];
            if (map.containsKey(key)) {
                if (map.get(key) + 1 > array.length / 2) {
                    return key;
                } else {
                    map.replace(key, map.get(key) + 1);
                }
            } else {
                map.put(key, 1);
            }
        }
        return 0;
    }
}
import java.util.Arrays;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        Arrays.sort(array);
        int count=0;
        int num=array[array.length/2];
        for(int n:array){
            if(n==num){
                count++;
            }else if(n>num)
                break;
        }
        return count>array.length/2?num:0;
    }
}

连续子数组的最大和

题目描述
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为 O(n).
示例1
输入
[1,-2,3,10,-4,7,2,-5]
返回值
18
说明
输入的数组为{1,-2,3,10,—4,7,2,一5},和最大的子数组为{3,10,一4,7,2},因此输出为该子数组的和 18。

题解
描述
这是一篇针对初学者的题解,共用两种方法解决。
知识点:数组,动态规划
难度:一星

题解
题目抽象:给定一个数组,求连续子数组的最大和。

方法一:动态规划
状态定义:dp[i]表示以i结尾的连续子数组的最大和。所以最终要求dp[n-1]
状态转移方程:dp[i] = max(array[i], dp[i-1]+array[i])
解释:如果当前元素为整数,并且dp[i-1]为负数,那么当然结果就是只选当前元素
技巧:这里为了统一代码的书写,定义dp[i]表示前i个元素的连续子数组的最大和,结尾元素为array[i-1]

代码

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int sz = array.size();
        vector<int> dp(sz+1, 1);
        dp[0] = 0; // 表示没有元素
        int ret = array[0];
        for (int i=1; i<=sz; ++i) {
            dp[i] = max(array[i-1], dp[i-1]+array[i-1]);
            ret = max(ret, dp[i]);
        }
        return ret;
    }
};

时间复杂度:O(n)
空间复杂度:O(n)

方法二:空间复杂度O(1)解法
思想很简单,就是对下标为i的元素array[i],先试探的加上array[i], 如果和为负数,显然,以i结尾的元素对整个结果不作贡献。
具体过程:

  1. 初始化:维护一个变量tmp = 0
  2. 如果tmp+array[i] < 0, 说明以i结尾的不作贡献,重新赋值tmp = 0
  3. 否则更新tmp = tmp + array[i]
  4. 最后判断tmp是否等于0, 如果等于0, 说明数组都是负数,选取一个最大值为答案。

代码

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int ret = array[0];
        int tmp = 0;
        for (const int k : array) {
            if (tmp + k < 0) {
                tmp = 0;
            }
            else {
                tmp += k;
            }
            ret = max(ret, tmp);
        }
        if (tmp != 0)
            return ret;
        return *max_element(array.begin(), array.end());
    }
};

时间复杂度:O(n)
空间复杂度:O(1)

通过的代码

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int len=array.length;
        if(len<=0){
            return 0;
        }
         
        int dp=array[0];
        //int[] dp=new int[len];
        //dp[0]=array[0];
        int max=dp;
        for(int i=1;i<len;i++){
            if(dp>0){
                dp=dp+array[i];
            }
            else{
                dp=array[i];
            }
            max=Math.max(max,dp);
        }
        return max;
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int maxVal = 0;
        int curSum = 0;
        int len = array.length;
        for(int i=0; i<len; i++){
            curSum = curSum+array[i];
            maxVal = Math.max(maxVal, curSum);
            if(curSum<0) {
                if (array[i]>0){
                    curSum = array[i];
                } else {
                    curSum = 0;
                }
            }
        }
        return maxVal>0?maxVal:-1;
    }
}
import java.lang.*;
import java.util.*;
import java.io.*;
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        //int[] dp = new int[array.length];
        int sum=array[0];
        //int res=Integer.MIN_VALUE();
        //int res=-100;
        for (int i=1;i<array.length;i++) {
            array[i] += Math.max(array[i-1], 0);
            sum= Math.max(array[i], sum);
             //sum=0;
        }
        return sum;
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array.length==0) return 0;
        int dp[]=new int[array.length];
        int max=array[0];
        dp[0]=array[0];
        for(int i=1;i<array.length;i++){
            if(dp[i-1]+array[i]>array[i])  dp[i]=dp[i-1]+array[i];
            else dp[i]=array[i];
            if(max<dp[i]) max=dp[i];
        }
        return max;
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] arr) {
        // 排异
        if (arr == null || arr.length < 1) {
            System.out.println("数组为空!");
        }
 
        // max用来记录最大的子数组和,因为有负数存在,所以设置为MIN_VALUE而不能为0
        int max = Integer.MIN_VALUE;
        // tmpMax用来记录当前的和,初始值给0
        int tmpMax = 0;
        // 遍历数组arr来找最大的子数组和
        for (int i : arr) {
        // 如果当前的和tmpMax小于等于0,就重新设置当前和;如果当前和大于0,累加当前和
        //这样做就可以简化逻辑,因为如果遍历到i号元素时的和tmpMax(假设从0开始遍历,那么这里的tmpMax就是0至i-1的和)是非正数。就可以将tmpMax的值直接给成i,为什么呢?
        //因为如果此时的i为正数,那再好不过,正零负三者相比,高下立判;
        // 如果为负数,我们也不慌,因为max变量记录了tmpMax的值,如果i作为负数比tmpMax还小,虽然本次i依旧会赋值给tmpMax,
        // 但是根据下面if (max < tmpMax)的判断,最终max还是上一轮tmpMax的值,所以逻辑是通的。
            if (tmpMax <= 0) {
                tmpMax = i;
            }
            else {
                tmpMax += i;
            }
 
            // 用变量max记录tmpMax的值
            if (max < tmpMax) {
                max = tmpMax;
            }
        }
            //返回最大的子数组和
        return max;
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        //dp状态转移方程为dp[i] = max(array[i],dp[i-1]+array[i]);
         
        int[] dp = new int[array.length+1];
        int ret = array[0];
        for(int i = 1;i <= array.length ;i++){
            dp[i] = Math.max(array[i-1],dp[i-1]+array[i-1]);
            ret = Math.max(ret,dp[i]);
        }
        return ret;
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if (array.length == 0) return 0;
        int ret = Integer.MIN_VALUE;;
        int crt = 0;
        for (int i=0; i<array.length; i++) {
            if (crt >= 0) crt += array[i];
            else crt = array[i];
            if (crt > ret) ret = crt;
        }
        return ret;
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int ret = array[0];
        int temp = 0;
        for (int i : array) {
            if (temp + i < 0){
                temp = 0;
            }else {
                temp += i;
            }
            ret = Math.max(temp,ret);
        }
        if (temp != 0){
            return ret;
        }
        int max = array[0];
        for (int i : array) {
            if (max < i){
                max = i;
            }
        }
        return max;
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int len=array.length;
        int pre=array[0];
        int max=pre;
        for(int i=1;i<len;i++){
            pre=Math.max(pre+array[i],array[i]);
            if(pre>max)max=pre;
        }
        return max;
         
    }
}
public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int max=Integer.MIN_VALUE;
        int sum=0;
        for(int num:array){
            sum+=num;
            max=Math.max(max,sum);
            if(sum<0){
                sum=0;
            }
        }
        return max;
    }
}

第一个只出现一次的字符

题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
示例1
输入
“google”
返回值
4

题解
描述
这是一篇针对初学者的题解。共用两种方法解决。
知识点:哈希,bitset
难度:一星

题解
方法一:哈希法
很显然,遍历一遍字符串,统计每个字符出现的次数。然后再遍历一遍字符串,找出答案。

代码一:用map实现

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        unordered_map<char, int> mp;
        for (const char ch : str) {
            ++mp[ch];
        }    
        for (int i=0; i<str.length(); ++i) {
            if (mp[str[i]] == 1) return i;
        }
        return -1;
    }
};

代码二:用数组代替map

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        int mp[128] = {0};
        for (const char ch : str) {
            ++mp[ch];
        }    
        for (int i=0; i<str.length(); ++i) {
            if (mp[str[i]] == 1) return i;
        }
        return -1;
    }
};

时间复杂度:O(2n), 需要遍历两次字符串
空间复杂度:O(n)

方法二:使用bitset
其实思想还是哈希,主要是这里bitset更节省空间,每个位置的值为0或1, 同时可以练习下bitset的使用。
具体过程:

  1. 初始化:bitset<128> b1表示只出现1次, b2表示出现2次以上
  2. 遍历字符串,第一次出现,b1[ch] = 1
  3. 如果第二次出现,b2[ch] = 1
  4. 最后,找出第一个b1[ch] == 1 && b2[ch] == 0的字符

代码

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        bitset<128> b1, b2;
        for (const char ch : str) {
            if (!b1[ch] && !b2[ch]) {
                b1[ch] = 1;
            }
            else if (b1[ch] && !b2[ch]) {
                b2[ch] = 1;
            }
        }
        for (int i=0; i<str.length(); ++i) {
            if (b1[str[i]] && !b2[str[i]]) {
                return i;
            }
        }
        return -1;
    }
};

时间复杂度:O(2n), 需要遍历两次字符串
空间复杂度:O(n)

通过的代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
           String str ;
        while ((str = bufferedReader.readLine()) != null){
            int i = FirstNotRepeatingChar(str);
            System.out.println(i);
        }
    }
    public  static int FirstNotRepeatingChar(String str) {
        int [] mp = new int[128];
        for (int i = 0; i < str.length(); i++) {
            mp[str.charAt(i)]++;
        }
        for (int i = 0; i < str.length(); i++) {
            if( mp[str.charAt(i)]==1){
                return i;
            }
        }
        return -1;
    }
}
import java.util.*;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
//         HashMap<Character,Integer> res = new HashMap();
//         char[] arr = str.toCharArray();
//         for(char c:arr){
//             if(res.containsKey(c)){
//                 res.put(c,res.get(c)+1);
//             }else{
//                 res.put(c,1);
//             }
//         }
//         for(int i=0;i<str.length();i++){
//             if(res.get(str.charAt(i))==1){
//                     return i;
//             }
//         }
//         return -1;
         
        if(str == null || str.length()==0){
            return -1;
        }
        int[] res = new int[60];
        char[] arr = str.toCharArray();
        for(char c:arr){
            res[c-'A']++;
        }
        for(int i=0;i<arr.length;i++){
            if(res[arr[i]-'A']==1){
                return i;
            }
        }
        return -1;
    }
}
import java.util.*;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        int [] mp = new int[128];
        for (int i = 0; i < str.length(); i++) {
            mp[str.charAt(i)]++;
        }
        for (int i = 0; i < str.length(); i++) {
            if( mp[str.charAt(i)]==1){
                return i;
            }
        }
        return -1;
    }
}
import java.util.HashMap;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        //异常,str为空,长度0,返回-1
        if(str==null||str.isEmpty()){
            return -1;
        }
        int[]arr=new int[128];
        for(int i=0;i<str.length();i++){
            arr[str.charAt(i)]++;
        }
        for(int i=0;i<str.length();i++){
            if(arr[str.charAt(i)]==1){
                return i;
            }
        }
        return -1;
    }
}
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        if (str == null || str.length() == 0) {
            return -1;
        }
        int[] arr = new int[58];
        for (int i = 0; i < str.length(); i++) {
            arr[str.charAt(i) - 'A'] += 1;
        }
        for (int i = 0; i < str.length(); i++) {
            if (arr[str.charAt(i) - 'A'] == 1) {
                return i;
            }
        }
        return -1;
    }
}
import java.util.HashMap;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        if(str==null||str.length()==0)
            return -1;
        int[] count=new int[58];
        for(int i=0;i<str.length();i++)
        {
            char tmp=str.charAt(i);
            count[tmp-'A']++;
        }
        for(int i=0;i<str.length();i++){
            char c=str.charAt(i);
            if(count[c-'A']==1)
                return i;
        }
               return -1;
    }
}
import java.util.*;
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        for(int i =0;i<str.length();i++){
            if(str.indexOf(str.charAt(i)) == i && str.indexOf(str.charAt(i),i+1) == -1) return i;   
        }
        return -1;
    }
}
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        int[] counts = new int[58];
        for(int i=0;i<str.length();i++){
            counts[str.charAt(i)-'A'] += 1;
        }
        for(int i=0;i<str.length();i++){
            if(counts[str.charAt(i)-'A'] == 1){
                return i;
            }
        }
        return -1;     
    }
}
import java.util.HashMap;
import java.util.Map;
 
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        if(str==null || str.length()<=0){
            return -1;
        }
        int[] letter = new int[58];
        char[] cc = str.toCharArray();
        for(int i=0; i<cc.length; i++){
            int index=cc[i]-'A';
            letter[index]++;
        }
        for(int i=0; i<cc.length; i++){
            int index=cc[i]-'A';
            if(letter[index]==1){
                return i;
            }
        }
        return -1;
    }
}
public class Solution {
    public int FirstNotRepeatingChar(String str) {
        int[] a = new int[58];
        for(int i=0;i<str.length();i++){
            a[(int)str.charAt(i)-65]+=1;
        }
         for(int i=0;i<str.length();i++){
            if(a[(int)str.charAt(i)-65]==1)
             return i;
        }
//         for (int i = 0; i < str.length(); i++) {
//             a[((int) str.charAt(i)) - 65] += 1;
//         }
//         for (int i = 0; i < str.length(); i++) {
//             if (a[((int) str.charAt(i)) - 65] == 1)
//                 return i;
//         }
        return -1;
    }
}

二叉树的深度

题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
示例1
输入
{1,2,3,4,5,#,6,#,#,7}
返回值
4
说明:本题目包含复杂数据结构TreeNode

题解
描述
这是一篇指针初学者的题解。这里用2种方法。
知识点:二叉树,队列,树的层次遍历, 分治法
难度:一星

题解
题目抽象:给出一颗二叉树,求树的最大深度,也就是从根节点到所有叶子节点中的最大值。

方法一:分治法
分治法简介:求一个规模为n的问题,先求左边规模大约为n/2的问题,再求右边规模大约为n/2的问题,然后合并左边,右边的解,从而求得最终解。具体可参考归并排序。
步骤:

  1. 求 pro(left, rigth) -> int
  2. 先求pro(left, (left+right)/2) -> lval
  3. 再求pro((left+right)/2 + 1, right) -> rval
  4. merge(lval, rval) -> result

这里以本题为具体例子:
函数是求二叉树的最大深度,我们不必管函数具体是怎么实现的。
所以最终结果为 max( 头结点左子树的最大深度, 头结点右子树的最大深度)+1

  1. TreeDepth(TreeNode* pRoot)->int
  2. 先求 TreeDepth(pRoot->left) ->lval
  3. 再求TreeDepth(pRoot->right) ->rval
  4. return max(lval, rval) + 1

代码

class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if (!pRoot) return 0;
        int lval = TreeDepth(pRoot->left);
        int rval = TreeDepth(pRoot->right);
        return max(lval, rval) + 1;
 
    }
};

时间复杂度:O(n)
空间复杂度:O(n),当退化到链表时

方法二:层次遍历
求最大深度,可用队列。因为要满足先进先出的特性。

  1. 初始化:一个队列queue<TreeNode*> q, 将root节点入队列q
  2. 如果队列不空,做如下操作:
  3. 弹出队列头,保存为node,将node的左右非空孩子加入队列
  4. 做2,3步骤,知道队列为空

如果不需要确定当前遍历到了哪一层,模板如下:

void bfs() {
    vis[] = 0;
    queue<int> pq(start_val);
 
    while (!pq.empty()) {
        int cur = pq.front(); pq.pop();
        for (遍历cur所有的相邻节点nex) {
            if (nex节点有效 && vis[nex]==0){
                vis[nex] = 1;
                pq.push(nex)
            }
        }
    }
}

如果需要确定遍历到哪一层,模板如下;

void bfs() {
    int level = 0;
    vis[] = 0; // or set
    queue<int> pq(original_val);
    while (!pq.empty()) {
        int sz = pq.size();
 
        while (sz--) {
                int cur = pq.front(); pq.pop();
            for (遍历cur所有的相邻节点nex) {
                if (nex节点有效 && vis[nex] == 0) {
                    vis[nex] = 1;
                    pq.push(nex)
                }
            } // end for
        } // end inner while
        level++;
 
    } // end outer while
}

所以本题直接套模板即可:

代码如下:

class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if (!pRoot) return 0;
        queue<TreeNode*> q;
        q.push(pRoot);
        int leve1 = 0;
        while (!q.empty()) {
            int sz = q.size();
            while (sz--) {
                auto node = q.front(); q.pop();
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            leve1 += 1;
        }
        return level;
    }
};

时间复杂度:O(n),二叉树的每个节点遍历一次
空间复杂度:O(n),最差情况下,树平衡时,队列最多存储n/2个节点。

通过的代码

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
 
    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    private int ret = 0;
    public int TreeDepth(TreeNode root) {
        dfs(root,0);
        return ret;
    }
    public void dfs(TreeNode root,int depth){
        if(root != null){
            ret = Math.max(ret,++depth);
            dfs(root.left,depth);
            dfs(root.right,depth);
        }
    }
}
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
            TreeDepth(root.left);
            TreeDepth(root.right);
        return Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;
    }
}
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        if(root.left == null && root.right == null){
            return 1;
        }
        return 1 + Math.max(TreeDepth(root.left),TreeDepth(root.right));
    }
}
public class Solution {
    public int TreeDepth(TreeNode root) {
        if (root == null) return 0;
        return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
    }
}
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null)
            return 0;
         
        int depth1 = 1 + TreeDepth(root.left);
        int depth2 = 1 + TreeDepth(root.right);
         
        return depth1 > depth2 ? depth1 : depth2;
    }
}
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null) return 0;
        int l = TreeDepth(root.left);
        int r = TreeDepth(root.right);
        return l > r ?l + 1:r + 1;
    }
}
public class Solution {
    public int TreeDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return Math.max(TreeDepth(root.left),TreeDepth(root.right)) + 1;
    }
}
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null) return 0;
        return Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;
    }
}
public class Solution {
    public int TreeDepth(TreeNode root) {
        int n=0,m=0;
        if(root==null) return 0;
        else{
            n=TreeDepth(root.left);
            m=TreeDepth(root.right);
             if(n>m) return n+1;
        else return m+1;
        }
    }
}
import java.util.*;
public class Solution {
    public int TreeDepth(TreeNode root) {
        if (root == null) return 0;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        return Math.max(left, right) + 1;
    }
}

平衡二叉树

题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
示例1
输入
{1,2,3,4,5,6,7}
返回值
true
说明:本题目包含复杂数据结构TreeNode

题解
这是一篇针对初学者的题解,用两种方法解决。
知识点:树,递归
难度:一星

题解
方法一:自顶向下
判断一个数是否为平衡二叉树。平衡二叉树是左子树的高度与右子树的高度差的绝对值小于等于1,同样左子树是平衡二叉树,右子树为平衡二叉树。

根据定义,如果我们能够求出以每个结点为根的树的高度,然后再根据左右子树高度差绝对值小于等于1,,就可以判断以每个结点为根的树是否满足定义。
我们可以用hash<TreeNode*, int>来存以每个结点的树的高度。
代码如下:

map<TreeNode*, int> hs;
int depth(TreeNode *root) {
    if (!root) return 0;
    if (hs.find(root) != hs.end()) return hs[root];
    int ldep = depth(root->left);
    int rdep = depth(root->right);
    return hs[root] = max(ldep, rdep) + 1;
}

然后再用先序遍历:根节点、左子树、右子树来判断以每个结点为根的树是否满足条件。
代码如下:

bool judge(TreeNode *root) {
    if (!root) return true;
    return abs(hs[root->left] - hs[root->right]) <= 1 &&
    judge(root->left) && judge(root->right);
}

最后的代码为:

class Solution {
public:
    map<TreeNode*, int> hs;
    int depth(TreeNode *root) {
        if (!root) return 0;
        if (hs.find(root) != hs.end()) return hs[root];
        int ldep = depth(root->left);
        int rdep = depth(root->right);
        return hs[root] = max(ldep, rdep) + 1;
    }
    bool judge(TreeNode *root) {
        if (!root) return true;
        return abs(hs[root->left] - hs[root->right]) <= 1 &&
        judge(root->left) && judge(root->right);
    }
    bool IsBalanced_Solution(TreeNode* root) {
        depth(root);
        return judge(root);
    }
};

时间复杂度:O(N)
空间复杂度:O(N)

方法二:自底向上
方法一是先求出以每个结点为根的树的高度,然后再判断,其实可以直接再求高度的同时,直接判断即可。
利用后序遍历:左子树、右子树、根节点,可以先递归到叶子节点,然后在回溯的过程中来判断是否满足条件。
求树的高度的代码为:

int depth(TreeNode *root) {
    if (!root) return 0;
    int ldep = depth(root->left);
    int rdep = depth(root->right);
    return max(ldep, rdep) + 1;
}

然后对上述代码加以改造,如果不满足平衡二叉树的定义,则返回-1,并且如果左子树不满足条件了,直接返回-1,右子树也是如此,相当于剪枝,加速结束递归。
代码如下:

int depth(TreeNode *root) {
    if (!root) return 0;
    int ldep = depth(root->left);
    if (ldep == -1) return -1;
    int rdep = depth(root->right);
    if (rdep == -1) return -1;
    int sub = abs(ldep - rdep);
    if (sub > 1) return -1;
    return max(ldep, rdep) + 1;
}

最后只需要判断depth(root)返回的是否为-1,如果是-1,则不是,否则,则是。
代码如下:

class Solution {
public:
    int depth(TreeNode *root) {
        if (!root) return 0;
        int ldep = depth(root->left);
        if (ldep == -1) return -1;
        int rdep = depth(root->right);
        if (rdep == -1) return -1;
        int sub = abs(ldep - rdep);
        if (sub > 1) return -1;
        return max(ldep, rdep) + 1;
    }
    bool IsBalanced_Solution(TreeNode* root) {
        return depth(root) != -1;
    }
};

时间复杂度:O(N)
空间复杂度:O(N)

通过的代码

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null) {
            return true;
        }
        return Math.abs(maxDepth(root.left) - maxDepth(root.right)) <= 1 &&
            IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
    }
       
    private int maxDepth(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
    }
}

public class Solution {
    boolean flag = true ;
    public boolean IsBalanced_Solution(TreeNode root) {
        judge(root);
        return flag ;
    }
    public int judge(TreeNode r){
        if(r == null) return 0 ;
        int left = judge(r.left) ;
        int right = judge(r.right) ;
        if((left-right)>1 || (right-left)>1){
            flag = false ;
            return -1 ;
        }
        return left>right?left+1:right+1;
    }
}
public class Solution {
   public int maxDepth (TreeNode root) {
        // write code here
        if (root ==null){
            return 0;
        }
        int left = maxDepth(root.left);
        int right=maxDepth(root.right);
        return Math.max(left,right)+1;
    }
    public boolean IsBalanced_Solution(TreeNode root) {
        if (root ==null){
            return true;
        }
        int left = maxDepth(root.left);
        int right=maxDepth(root.right);
        int diff = Math.abs(left-right);
        if (diff>1){
            return false;
        }
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);   
}
}
public class Solution {
    public int depth(TreeNode root){
        if(root == null)return 0;
        int left = depth(root.left);
        if(left == -1)return -1; //如果发现子树不平衡之后就没有必要进行下面的高度的求解了
        int right = depth(root.right);
        if(right == -1)return -1;//如果发现子树不平衡之后就没有必要进行下面的高度的求解了
        if(left - right <(-1) || left - right > 1)
            return -1;
        else
            return 1+(left > right?left:right);
    }
  
    public boolean IsBalanced_Solution(TreeNode root) {
        return depth(root) != -1;
    }
}
public class Solution {
    public int depth(TreeNode root){
        if(root == null)return 0;
        int left = depth(root.left);
        if(left == -1)return -1;
        int right = depth(root.right);
        if(right == -1)return -1;
        if(left - right <(-1) || left - right > 1)
            return -1;
        else
            return 1+(left > right?left:right);
    }
   
    public boolean IsBalanced_Solution(TreeNode root) {
        return depth(root) != -1;
    }
}
public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root==null)return true;
        boolean a=Math.abs(depth(root.left)-depth(root.right))<=1;
        boolean b = IsBalanced_Solution(root.left);
        boolean c= IsBalanced_Solution(root.right);
        return a&&b&&c;
    }
     
    public int depth(TreeNode root){
        if(root==null)return 0;
        return Math.max(depth(root.left), depth(root.right))+1;
    }
}
public class Solution {
    boolean flag = true;
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null) {
            return true;
        }
        treeHeight(root);
        return flag;
    }
     
    public int treeHeight(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftH = treeHeight(root.left);
//         if (leftH == -1) {
//             return -1;
//         }
        int rightH = treeHeight(root.right);
//         if(rightH == -1) {
//             return -1;
//         }
        int temp = Math.abs(leftH - rightH);
        if(temp > 1) {
           flag = false;
           return -1;
        }
        return Math.max(leftH,rightH) + 1;
    }
}
import java.util.*;
 
public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null){
            return true;
        }
        int m = depth(root.left);
        int n = depth(root.right);
        if(Math.abs(m-n) > 1){
            return false;
        }
        return true;
    }
     
    public int depth(TreeNode root){
        if(root == null){
            return 0;
        }else{
            int m = depth(root.left);
            int n = depth(root.right);
            return m>n? m+1 : n+1;
        }
    }
}
 
// public boolean IsBalanced_Solution(TreeNode root) {
//         return getDepth(root) != -1;
//     }
      
//     private int getDepth(TreeNode root) {
//         if (root == null) return 0;
//         int left = getDepth(root.left);
//         if (left == -1) return -1;
//         int right = getDepth(root.right);
//         if (right == -1) return -1;
//         return Math.abs(left - right) > 1 ? -1 : 1 + Math.max(left, right);
//     }
// }

public class Solution {
     public boolean IsBalanced_Solution(TreeNode root) {
        return height2(root) >= 0 ;
    }
    private int height2(TreeNode root ){ // 1  2
       if(root == null) return 0;
        int left = height2(root.left);
        int right = height2(root.right);
        if( left== -1 || right ==-1 || Math.abs(left-right)>1 )
            return -1 ;
             
        return Math.max(left,right) +1 ;
    }
}
public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        return depth(root) != -1;
    }
     
    private int depth(TreeNode root){
        if(root == null) return 0;
        int left = depth(root.left);
        if(left == -1) return -1;
        int right = depth(root.right);
        if(right == -1) return -1;
        if(left - right < (-1) || left - right > 1){
            return -1;
        }else{
            return 1 + (left > right ? left : right);
        }
    }
}

不用加减乘除做加法

题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
示例1
输入
1,2
返回值
3

题解
描述
这是一篇针对初学者的题解
知识点:位运算
难度:二星

题解
题目描述:不使用加减乘除来实现两数加法、

方法:位运算
知识补充:
按位与&,按位或|, 按位异或^
在这里插入图片描述

补码
计算机中存整数n是用补码存的。

  • 如果n为正数,则原码=反码=补码
  • 如果n为负数,则补码=反码+1

本题是考察对位运算的运用,使用位运算来实现两数的加法。
设两数字的二进制形式 a,b ,其求和 s = a + b ,a(i) 代表 a 的二进制第 i 位,则分为以下四种情况:
在这里插入图片描述

观察发现,无进位和运算就是按位异或结果,进位就是与运算结果但是需要左移一位,因为进位影响下一位的运算。
所以s = a + b,其实就是无进位和+进位的结果。
算法步骤:

  1. 计算a和b的无进位和,和进位
  2. 如果进位不为0,则说明a+b的结果等于无进位和+进位,此时,把无进位和作为a,进位作为b,继续计算
  3. 如果进位等于0, 说明此时a+b的结果就等于无进位和,返回无进位和即可。

如图:
在这里插入图片描述

Q:你可能有疑问,如果是一个数为负数或者两个数都为负数怎么办?
A:上述补码的介绍,补码就是解决减法的问题,计算机把减法看做加法来运算。
所以代码如下:

class Solution {
public:
    int Add(int num1, int num2)
    {
        while (num2 != 0) {
            // 负数左移会在低位补1,所以转化为无符号整数
            int c = ((unsigned int)(num1 & num2)) << 1;
            num1 ^= num2;
            num2 = c;
        }
        return num1;
    }
};

时间复杂度:O(1)
空间复杂度:O(1)

通过的代码

public class Main {
    public int add(int num1,int num2) {
        int result;
        int ans;
        do{
            result = num1 ^ num2;
            ans = (num1 & num2) << 1;
            num1 = result;
            num2 = ans;
        }while(ans!=0);
        return result;
    }
    public static void main(String[] args){
        System.out.println(new Main().add(1,2));
        System.out.println(new Main().add(111,899));
        System.out.println(new Main().add(-1,2));
        System.out.println(new Main().add(1,-2));
        System.out.println(new Main().add(3,0));
        System.out.println(new Main().add(0,-4));
        System.out.println(new Main().add(-2,-8));
    }
 
}
public class Solution {
    public int Add(int num1,int num2) {
        return num2 == 0 ? num1 : Add(num1 ^ num2, (num1 & num2) << 1);
    }
}
public class Solution {
    public int Add(int num1,int num2) {
         
        int result = 0;
        int carry = 0;
        do{
            result = num1 ^ num2;       //不带进位的加法
            carry = (num1 & num2) << 1; //进位
            num1 = result;
            num2 = carry; 
        }while(carry != 0); // 进位不为0则继续执行加法处理进位
        return result;
    }
}
public class Solution {
    public int Add(int num1,int num2) {
        while (num2 != 0){
            int m = (num1 & num2) << 1;
            num1 ^= num2;
            num2 = m;
        }
        return num1;
    }
}

数组中重复的数字

题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
示例1
输入
[2,3,1,0,2,5,3]
返回值
2或3

题解
方法1:
对于重复性问题可以想到set,遍历数组依次加入集合,若集合中存在该元素则直接返回该元素,否则将该元素加入集合:

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] numbers) {
        // write code here
        Set<Integer> set = new HashSet<Integer>();
        for(int i : numbers){
            if(set.contains(i)){
                return i;
            }else{
                set.add(i);
            }
        }
        return -1;
    }
}

方法2:
对于题目所给的条件,可以开辟一个与给定数组等长的新数组,用于记录给定数组中出现的数字的个数(新数组的下标即表示给定数组中的数,数组存放的值,即为该数字出现的次数)一旦新数组中某个记录的数大于1,则返回新数组的下标
代码如下:

public static int duplicate (int[] numbers) {
       // write code here
       int[] countarray=new int[numbers.length];
       for(int i=0;i<numbers.length;i++) {
           countarray[numbers[i]]++;
           if(countarray[numbers[i]]>1) {
               return numbers[i];
           }
       }
       return -1;
   }

数组中重复的数字-替换法(O(n),O(1))

//解题思路
/*替换法(O(n),O(1))
数组存放原则:numbers[i] = i
遍历数组所有元素,交换不符合数组存放原则的元素:
    例如[2,3,1,0,2]
    遍历0位元素2:(交换0位元素2和2位元素1)->[1,3,2,0,2]
    遍历0位元素1:(交换0位元素1和1位元素3)->[3,1,2,0,2]
    遍历0位元素3:(交换0位元素3和3位元素0)->[0,1,2,3,2]
    依次遍历0、1、2、3位置元素,都符合存放原则numbers[i] = i,不做任何操作
    遍历末位元素2,此时末位元素2和2位元素2相等,出现了两次,即数字2位重复元素
 */
public int duplicate (int[] numbers) {
    for (int i = 0; i < numbers.length; i++) {
        if (numbers[i] != i){
            if (numbers[i] == numbers[numbers[i]]) return numbers[i];
            int temp = numbers[numbers[i]];
            numbers[numbers[i]] = numbers[i];
            numbers[i] = temp;
            i--;//遍历完0位元素以及交换完数字后,如果0位元素仍不符合数组存放原则:numbers[i] = i,那么还要重新遍历0位元素
        }
    }
    return -1;
}

剑指Offer 03. 数组中重复的数字-巧解:set
利用set的不重复特性。把数组中的元素一个个添加到set中,如果某一元素添加过后,set的长度没有改变,说明该元素重复!

let mySet = new Set();
    for (let i = 0; i < numbers.length; i++){
        let setLength = mySet.size;
        mySet.add(numbers[i]);
        if(setLength === mySet.size){
            return numbers[i];
        }
    }
return -1;

通过的代码

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] numbers) {
        int[] res = new int[numbers.length];
        for(int i = 0; i < numbers.length; i++){
            res[numbers[i]]++;
            if(res[numbers[i]]==2){
                return numbers[i];
            }
        }
        return -1;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] numbers) {
        // write code here
        if(numbers.length<=0)
            return -1;
        int len = numbers.length;
        int a[] = new int[len];
        for(int i = 0;i<len;i++){
            ++a[numbers[i]];
            if(a[numbers[i]]>1)
                return numbers[i];
        }
        return -1;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] numbers) {
        if(numbers.length==0){
            return -1;
        }
        int index=0;
        while(index<numbers.length){
                if(numbers[index]==index){
                    index++;
                }else{
                    int tmp=numbers[index];
                     if(numbers[index]==numbers[tmp]){
                         return numbers[index];
                     }
                    numbers[index]=numbers[tmp];
                    numbers[tmp]=tmp;
                }
        }
        return -1;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param numbers int整型一维数组
     * @return int整型
     */
    //public int duplicate (int[] numbers) {
    //    HashSet dict = new HashSet();
    //    for(int i = 0; i< numbers.length;i++)
    //    {
    //        if(dict.contains(numbers[i])){return numbers[i];}
    //        else{dict.add(numbers[i]);}
    //    }
    //    return -1;
     //   // write code here
    //}
    public int duplicate(int[] numbers)
    {
        int temp;
        for(int i = 0; i< numbers.length; i++)
        {
            while(i != numbers[i])
            {
                if(numbers[i] == numbers[numbers[i]])
                {
                    return numbers[i];
                }
                temp = numbers[i];
                numbers[i] = numbers[temp];
                numbers[temp] = temp;
            }
        }
        return -1;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] numbers) {
       int[] res=new int[numbers.length];
        for(int i=0;i<numbers.length;i++){
            res[numbers[i]]++;
            if(res[numbers[i]]==2){
                return numbers[i];
            }
        }
        return -1;
    }
     
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] numbers) {
        // write code here
        if(numbers == null || numbers.length == 0){
            return -1;
        }
        int[] nums = new int[numbers.length];
        for(int i = 0; i < numbers.length; i++){
            if(nums[numbers[i]] > 0){
                return numbers[i];
            }else{
                nums[numbers[i]]++;
            }
        }
        return -1;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] numbers) {
        // write code here
         
        int[] count = new int[numbers.length];
        for(int n:numbers){
            if(++count[n]>1) return n;
             
        }
         
        return -1;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] numbers) {
        // write code here
        /*if(numbers==null||numbers.length==0)
            return -1;
        HashSet set =new HashSet();
        for(int i=0;i<numbers.length;i++){
            if(numbers[i]>=numbers.length)
                return -1;
            else {
                if(!set.add(numbers[i])){
                    return numbers[i];
                }
            }
        }
        return -1;*/
         int[] countarray=new int[numbers.length];
       for(int i=0;i<numbers.length;i++) {
           countarray[numbers[i]]++;
           if(countarray[numbers[i]]>1) {
               return numbers[i];
           }
  
       }
       return -1;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * @param numbers int整型一维数组
     * @return int整型
     */
    public int duplicate (int[] nums) {
        // write code here
    int[] count = new int[nums.length];
    for(int i : nums){
        if(count[i] == 1){
            return i;
        }
        count[i] = 1;
    }
    return -1;
    }
}
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * @param numbers int整型一维数组
     * @return int整型
     */
    //把数字放到下标为该数字的地方,如果该位置的数字和下标已经对应,那么找到一个重复的数字
    public static int duplicate (int[] numbers) {
        // wri[te code here
        int n = numbers.length;
        for(int i=0;i<n;i++){
            while(i != numbers[i]){
                if(numbers[i] == numbers[numbers[i]]) return numbers[i];
                swap(numbers,i,numbers[i]);
            }
        }
        return -1;
    }
    public static void swap(int[] nums,int index1,int index2){
        int tmp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = tmp;
    }
}

构建乘积数组

题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)
对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。
示例1
输入
[1,2,3,4,5]
返回值
[120,60,40,30,24]

描述
这是一篇针对初学者的题解。
知识点:数组
难度:一星

题解
题目描述:给定一个长度为n的数组A,求数组B,B[i] = A[0]A[1]…A[i-1]*A[i+1]…A[n-1]。
要求不能使用除法。

方法:
根据题目描述,如果可以使用除法,就很简单。但是要求不能使用。

假设:

left[i] = A[0]*...*A[i-1]
right[i] = A[i+1]*...*A[n-1]

所以:

B[i] = left[i] * right[i]

这样就避免使用了除法。但是如果对每个B[i], 0<=i<n,都这么求,显然时间复杂度太高。

我们把整个结果画到下面图:
在这里插入图片描述

可知:

left[i+1] = A[0]*...A[i-1]*A[i]
right[i+1] = A{i+2]*...*A[n-1]

于是,

left[i+1] = left[i] * A[i]
right[i] = right[i+1] * A[i+1]

所以,我们可以先把所有的left[i]求出,right[i]求出。

代码如下:

class Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        vector<int> B(A.size(), 1);
        for (int i=1; i<A.size(); ++i) {
            B[i] = B[i-1] * A[i-1]; // left[i]用B[i]代替
        }
        int tmp = 1;
        for (int j=A.size()-2; j>=0; --j) {
            tmp *= A[j+1]; // right[i]用tmp代替
            B[j] *= tmp;
        }
        return B;
    }
};

时间复杂度:O(N)
空间复杂度: O(1)

通过的代码

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        /**暴力解法
        int[] B=new int[A.length];
        for(int i=0;i<A.length;i++){
            int tmp=1;
            for(int j=0;j<A.length;j++){
                if(j==i){
                    continue;
                }
                tmp=tmp*A[j];
            }
            B[i]=tmp;
        }
        return B;**/
        //elegant solution
        int[] B=new int[A.length];
        B[0]=1;
        for(int i=1;i<A.length;i++){
            B[i]=B[i-1]*A[i-1];
        }
        int temp=1;
        for(int i=A.length-2;i>=0;i--){
            temp=temp*A[i+1];
            B[i]=B[i]*temp;
        }
        return B;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] a) {
        int[] left = new int[a.length];
        int[] right = new int[a.length];
        int[] ret = new int[a.length];
         
        right[a.length-1] = 1;
        for(int i=a.length-2;i>=0;i--){
            right[i] = right[i+1] * a[i+1];
        }
        left[0]=1;ret[0]=right[0];
        for(int i=1;i<a.length;i++){
            left[i] = left[i-1] * a[i-1];
            ret[i] = left[i] * right[i];
        }
        return ret;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        int n = A.length;
        int[] b = new int[n];
        int l = 1;
        for(int i = 0 ; i < n ; i++){
            int right = 1;
            for(int j = i+1 ; j < n ;j++){
                right *= A[j];
            }
            b[i] = right*l;
            l *= A[i];
        }
        return b;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        if(A.length == 0)    return new int[2];
        int[] b = new int[A.length];
        b[0] = 1;
        for(int i=1;i<A.length;i++)
        {
            b[i] = A[i-1] * b[i-1];
        }
         
        int t = 1;
        for(int i=A.length-2;i>=0;i--)
        {
            t *= A[i+1];
            b[i] *= t;
        }
        return b;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        int[] B = new int[A.length];
        B[0]=1;
        B[1]=A[0];
        for(int i=2;i<A.length;i++){
            B[i] = B[i-1]*A[i-1];
        }
        int temp=1;
        for(int i=A.length-2;i>=0;i--){
            temp = temp*A[i+1];
            B[i] = temp*B[i];
        }
        return B;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        int len = A.length;
        int[] B = new int[len];
        int[] a = new int[len];
        int[] b = new int[len];
        a[0] = 1;
        b[0] = 1;
        for (int i = 1;i<len;i++){
            a[i] = a[i-1]*A[i-1];
            b[i] = b[i-1]*A[len-i];
        }
        for (int i = 0;i<len;i++){
            B[i] = a[i]*b[len-i-1];
        }
        return B;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        int len = A.length;
        int[] m = new int[len];
        int[] B = new int[len];
        m[0] = 1;
        B[len - 1] = 1;
        for(int i = 1; i < len; i++){
            m[i] = m[i - 1] * A[i - 1];
        }
        for(int j = len - 2; j >= 0; j--){
            B[j] = B[j + 1] * A[j + 1];
        }
        for(int i = 0; i < len; i++){
            B[i] = m[i] * B[i];
        }
        return B;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        int[] B = new int[A.length];
        int num = 1;
        for(int i = 0 ; i < A.length ; i++){
            num = 1;
            for(int j = 0 ; j < A.length ; j++){
                if(j != i){
                    num *= A[j];
                }
            }
            B[i] = num;
        }
        return B;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        if(A.length <= 1){
            return null;
        }
        int[] B = new int[A.length];
        for(int i = 0; i < A.length; i++){
            int result = 1;
            for(int j = 0; j < A.length; j++){
                if(j != i){
                    result *= A[j];
                }
            }
            B[i] = result;
        }
        return B;
    }
}
import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
    int[] B = new int[A.length];
    int temp = 0;
    for (int i = 0; i < A.length; i++) {
        temp = A[i];
        A[i] = 1;
        B[i] = 1;
        for (int j = 0; j < A.length; j++) {
            B[i] *= A[j];
        }
        A[i] = temp;
    }
    return B;
}
}

二叉搜索树的第k个结点

题目描述
给定一棵二叉搜索树,请找出其中的第k小的TreeNode结点。
示例1
输入
{5,3,7,2,4,6,8},3
返回值
{4}
说明
按结点数值大小顺序第三小结点的值为4
说明:本题目包含复杂数据结构TreeNode

《剑指offer》 第54题 二叉搜索树的第k个节点
题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)中,按结点数值大小顺序第三小结点的值为4。

题目给出的是该二叉树的层序遍历结果。实际如图
在这里插入图片描述

可以看到,4是按大小排序时,第3个节点。如果K=3,即4为所求的值。而对应大小排序的就是中序遍历,中序遍历结果是{2,3,4,5,6,7,8},所以本题核心需要使用到中序遍历。然后对中序遍历的结果中,取第K个元素即可。这里可以使用数字或者集合存储遍历结果,然后取第k个,但是会产生额外的空间复杂度,当然也可以使用一个变量记录遍历的节点数,当遍历到K个节点时即为所求。

写法1:
  使用数组存储,代码看起来非常的清晰。这个代码还有优化空间,因为完成了所有的遍历,事实上可以在遍历到K时,让遍历退出。

import java.util.ArrayList;
public class Solution {
    ArrayList<TreeNode> list = new ArrayList<>();
    TreeNode KthNode(TreeNode pRoot, int k) {
        OrderTraversal(pRoot);
        if(k<1 || list.size()<k)
            return null;
        return list.get(k-1);
    }
    // 中序遍历
    public void OrderTraversal(TreeNode node) {
        if(node != null) {
            OrderTraversal(node.left);
            list.add(node);
            OrderTraversal(node.right);
        }
    }
}
//优化:传入参数K,并在每次遍历前都判断下k是否符合要求。
//    public void OrderTraversal(TreeNode node,int k) {
//        if(node != null) {
//            if ( list.size() >= k ) //提前退出
//                return ;
//            OrderTraversal(node.left,k);
//            list.add(node);
//           OrderTraversal(node.right,k);
//       }
//    }

写法2:
  使用递归。

public class Solution {
    int index=0;//记录当前遍历的节点数量
    TreeNode KthNode(TreeNode pRoot, int k) {
        TreeNode pNode = null;
        if(pRoot==null || k<=0)//这里就进行了根节点为null的判断
            return pNode;
        pNode = getKthNode(pRoot,k);
        return pNode;
    }
 
    public TreeNode getKthNode(TreeNode pRoot, int k){
        TreeNode node = null;
        if(pRoot.left != null)
            node = getKthNode(pRoot.left,k);      
        //pRoot的某个子节点为null时进入下面代码
        index++;
        if(k == index)
            node = pRoot;  //满足条件就将pRoot赋值给node
        //当node赋值时已经找到时,不需要再遍历了,赋值后就不为null,所以要加上这个判断条件
        //写node == null或者k<index都是可以的。
        if(node == null && pRoot.right!=null)
            node = getKthNode(pRoot.right,k);      
        return node;
    }
}

写法3:
  使用栈。

import java.util.Stack;
public class Solution {
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        int count = 0;
        if(pRoot == null || k <= 0){
            return null;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode node = pRoot;
        //中序遍历
        while(!stack.isEmpty() || node != null){
            if(node != null){
                stack.push(node); //当前节点不为null,应该寻找左子节点,准备让左子节点入栈
                node = node.left;
            }else{
                node = stack.pop();//当前节点null则弹出栈内元素,相当于按顺序输出最小值。
                count++;//弹出时,才应该计数
                if(count == k){ //相等时就返回
                    return node;
                }
                node = node.right;//左边遍历完,还没到K,就得找右子节点
            }
        }
        return null;
    }
}

通过的代码

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
 
    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    int cout=0;
    TreeNode node;
    TreeNode KthNode(TreeNode pRoot, int k) {
        if (k==0) return null;
        NodePring(pRoot,k);
        return node;
    }
    void NodePring(TreeNode pRoot, int k){
        if(pRoot==null) return;
         
        if (k==cout) return;
        KthNode(pRoot.left,k);
        cout++;
        if(cout==k) node =pRoot;
        KthNode(pRoot.right,k);
    }
}
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
 
    public TreeNode(int val) {
        this.val = val;
    }
 
}
*/
public class Solution {
    ArrayList<TreeNode> list = new ArrayList<>();
    TreeNode KthNode(TreeNode pRoot, int k) {
        InOrder(pRoot);
        if(list.size()>=k&&k>0){
            return list.get(k-1);
        }
        return null;
    }
    public void InOrder(TreeNode pRoot){
        if(pRoot!=null){
            InOrder(pRoot.left);
            list.add(pRoot);
            InOrder(pRoot.right);
        }
    }
}
public class Solution {
    private TreeNode target = null;
    private int k1 = 0;
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        k1 = k;
        getKthNode(pRoot);
        return target;
    }
      
    private void getKthNode(TreeNode pRoot){
        if(pRoot == null || k1 <= 0){
            return;
        }
        getKthNode(pRoot.left);
        k1--;
        if(k1 == 0){
            target = pRoot;
            return;
        }
        getKthNode(pRoot.right);
    }
}
public class Solution {
    int cnt=0;
    TreeNode res;
    TreeNode KthNode(TreeNode pRoot, int k) {
        if(pRoot==null||k<1)return null;
        preOrder(pRoot,k);
        return res;
    }
    private void preOrder(TreeNode pRoot,int k){
        if(pRoot==null)return;
        preOrder(pRoot.left,k);
        cnt++;
        if(cnt==k){
            res=pRoot;
            return;
        }
        preOrder(pRoot.right,k);
    }
}
public class Solution {
    int index = 1;
    TreeNode ans = null;
    TreeNode KthNode(TreeNode pRoot, int k) {
        dfs(pRoot, k);
        return ans;
    }
     
    public void dfs(TreeNode pRoot, int k){
        if(pRoot == null){
            return;
        }
        dfs(pRoot.left, k);
        if(index > k){
            return;
        }
        if(index == k){
            ans = new TreeNode(pRoot.val);
        }
        index++;
        dfs(pRoot.right, k);
    } 
}
import java.util.*;
public class Solution {
    ArrayList<TreeNode> nodeList=new ArrayList<>();
    TreeNode KthNode(TreeNode pRoot, int k) {
        inOrder(pRoot);
        if(k<=0||k>nodeList.size()){
            return null;
        }
        else{           
            return(nodeList.get(k-1));
        }
         
    }
    public void inOrder(TreeNode pRoot){
        if(pRoot==null){
            return;
        }
        inOrder(pRoot.left);
        nodeList.add(pRoot);
        inOrder(pRoot.right);
    }
}
public class Solution {
    int count = 0;
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(pRoot != null){
            TreeNode node = KthNode(pRoot.left, k);
            if(node != null){
                return node;
            }
            count++;
            if(count == k){
                return pRoot;
            }
            node = KthNode(pRoot.right, k);
            if(node != null){
                return node;
            }
        }
        return null;
    }
}
public class Solution {
    TreeNode result;
    int count=0;
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        //二叉搜索树的中序遍历就是从小到大的
        if(pRoot==null){
            return null;
        }
        count=k;
        deal(pRoot);
        return result;
    }
    void deal(TreeNode pRoot)
    {
        //中序遍历
        if(pRoot.left!=null){
            deal(pRoot.left);
        }
        count--;
        if(count==0){
            result=pRoot;
        }
        if(pRoot.right!=null){
            deal(pRoot.right);
        }
    }
}
import java.util.*;
public class Solution {
    TreeNode KthNode(TreeNode pRoot, int k) {
        if(k <= 0 || pRoot == null) {
            return null;
        }
        Stack<TreeNode> stack = new Stack<> ();
        int count = 0;
        while(pRoot != null || !stack.isEmpty()) {
            if(pRoot != null){
                stack.push(pRoot);
                pRoot = pRoot.left;
            }else{
                TreeNode node = stack.pop();
                count++;
                if(count == k){
                    return node;
                }
                node = node.right;
                pRoot = node;
            }
        }
        return null;
    }
}
import java.util.*;
public class Solution {
    ArrayList<TreeNode> arr;
    TreeNode KthNode(TreeNode pRoot, int k) {
        if(k==0){
            return null;
        }
        arr=new ArrayList<TreeNode>();
        processM(pRoot);
        if(k<=arr.size()){
            return arr.get(k-1);
        }else{
            return null;
        }
    }
    void processM(TreeNode pRoot){
        if(pRoot==null){
            return;
        }
        processM(pRoot.left);
        arr.add(pRoot);
        processM(pRoot.right);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值