剑指Offer错题集(20题)

变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路:比如5层台阶的跳法,

  • 从第4层跳到5层,加上之前第1层到第4层的解法
  • 从第3层跳到5层,加上之前第1层到第3层的解法
  • 从第1层跳到5层
public class Solution {
    public int JumpFloorII(int target) {
        int memo[] = new int[target+1];
        memo[0] = 1;
        memo[1] = 1;
        
        for(int i = 2; i <= target; ++i){
            for(int j = 0; j <= i; ++j){
                memo[i] = memo[i] + memo[i-j];
            }
        }
        
        return memo[target];
    }
}
丑数

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

解题思路:对于每个丑数,它都要乘以2,3和5,然后形成新的丑数

public class Solution {
    private int min_3(int x, int y, int z){
        return Math.min(x, Math.min(y, z));
    }
    public int GetUglyNumber_Solution(int index) {
        if(index == 0){
            return 0;
        }
        
        int []memo = new int[index+1];
        
        memo[0] = 1;
        int a = 0;
        int b = 0;
        int c = 0;
        
        for(int i = 1; i <= index; ++i){
            memo[i] = min_3(memo[a]*2, memo[b]*3, memo[c]*5);
            if(memo[i] == memo[a]*2){
                ++a;
            }
            if(memo[i] == memo[b]*3){
                ++b;
            }
            if(memo[i] == memo[c]*5){
                ++c;
            }
        }
        return memo[index-1];
    }
}
全排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba

解题思路:

  • 使用visited数组进行记忆搜索,确保每个字符访问过一次
  • 对char数组进行排序,是确保每个重复的字符,在每次递归只使用一次
import java.util.*;
public class Solution {
    private ArrayList<String> A;
    private boolean []visited;
    
    public void tryPermutation(char []chars, String s){
        if(s.length() == chars.length){
            A.add(s);
            return;
        }
        for(int i = 0; i < chars.length; ++i){
            if(i != 0 && chars[i-1] == chars[i] && visited[i-1]){
                continue;
            }
            if(!visited[i]){
                visited[i] = true;
                s += chars[i];
                tryPermutation(chars, s);
                s = s.substring(0, s.length()-1);
                visited[i] = false;
            }
        }
    }
    
    public ArrayList<String> Permutation(String str) {
        A = new ArrayList<String>();
        if(str.length() == 0){
            return A;
        }
        
        visited = new boolean[str.length()];
        char []chars = str.toCharArray();
        Arrays.sort(chars);
        tryPermutation(chars, "");
        
        return A;
    }
}
二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

解题思路:二进制位中,每次n减1,其对应的最高位会置0;并且(n-1)&n不影响其二进制中低位中的1。

public class Solution {
    public int NumberOf1(int n) {
        int cnt = 0;
        while(n != 0){
            ++cnt;
            n = (n-1) & n;
        }
        
        return cnt;
    }
}
旋转数组的最小数字

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

解题思路:二分搜索

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length == 0){
            return 0;
        }
        
        int lo = 0;
        int hi = array.length - 1;
        while(lo <= hi){
            int mid = lo + (hi - lo) / 2;
            if(array[mid] == array[hi]){
                hi -= 1;
            }
            else if(array[mid] < array[hi]){
                hi = mid;
            }
            else if(array[mid] > array[hi]){
                lo = mid+1;
            }
        }
        
        return array[lo];
    }
}
滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

解题思路:一个列队,保存窗口期内最大值的下标。

import java.util.*;

public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size){
        ArrayList<Integer> res = new ArrayList<>();
        LinkedList<Integer> maxIndex = new LinkedList<>();
        
        if(size <= 0){
            return res;
        }
        
        for(int i = 0; i < num.length; ++i){
            while(!maxIndex.isEmpty() && num[i] >= num[maxIndex.getLast()]){
                maxIndex.removeLast();
            }
            maxIndex.addLast(i);
            if(i + 1 >= size){
                res.add(num[maxIndex.getFirst()]);
            }
            if(i - size + 1 >= maxIndex.getFirst()){
                maxIndex.removeFirst();
            }
        }
        return res;
    }
}
栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

解题思路:

  • 新建一个栈,模拟数据的压入和弹出
  • 栈中未弹出的数据,与popA进行比较
  • 判断数据有残留的数据
import java.util.*;

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack<Integer> stack = new Stack<Integer>();
        
        int cursor = 0;
        for(int i = 0; i < pushA.length; ++i){
            stack.push(pushA[i]);
            while(!stack.isEmpty() && stack.peek() == popA[cursor]){
                stack.pop();
                ++cursor;
            }
        }
        for(int i = cursor; i < popA.length; ++i){
            if(!stack.isEmpty() && stack.peek() == popA[i]){
                stack.pop();
            }
        }
        
        return stack.isEmpty();
    }
}
数值的整数次方

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

解题思路:分治

public class Solution {
    private double tryPower(double base, int p){
        if(p == 0){
            return 1.0;
        }
        if(p == 1){
            return base;
        }
        
        double ans = 0;
        if(p % 2 == 0){
            ans = tryPower(base, p/2) * tryPower(base, p/2);
        }
        else{
            ans = base * tryPower(base, p/2) * tryPower(base, p/2);
        }
        return ans;
    }
    
    public double Power(double base, int exponent) {
        if(exponent < 0){
            return 1 / tryPower(base, exponent);
        }
        else{
            return tryPower(base, exponent);
        }
  }
}

解题思路:简单快速幂

举个例子,

2的5次方,指数5的二进制为101,那么2的5次方为(2*2*2*2) * (1) * (2)
同理可得,
n的5次方为,(n*n*n*n) * (1) * (n)
n的9(1001)次方为, (n*n*n*n*n*n*n*n)*(1)*(1)*(n)
public class Solution {
    public double Power(double base, int exponent) {
        int p = Math.abs(exponent);
        double ans = 1.0;
        while(p != 0){
            if((p & 1) == 1){
                ans *= base;
            }
            base *= base;
            p >>= 1;
        }
        return exponent < 0 ? 1.0/ans : ans;
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值