《剑指offer第2版》Java题解总结(下)

03-19《剑指offer第2版》Java题解总结(上)_科技小猎豹的博客-CSDN博客
20-39《剑指offer第2版》Java题解总结(中)_科技小猎豹的博客-CSDN博客
40-68

40-68

40. 最小的 k 个数

4种解法秒杀TopK(快排/堆/二叉搜索树/计数排序)力扣

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if (k >= arr.length) return arr;
        return quickSort(arr, k, 0, arr.length - 1);
    }
    private int[] quickSort(int[] arr, int k, int l, int r) {
        int i = l, j = r;
        while (i < j) {
            while (i < j && arr[j] >= arr[l]) j--;
            while (i < j && arr[i] <= arr[l]) i++;
            swap(arr, i, j);
        }
        swap(arr, i, l);
        if (i > k) return quickSort(arr, k, l, i - 1);
        if (i < k) return quickSort(arr, k, i + 1, r);
        return Arrays.copyOf(arr, k);
    }
    private void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

41.数据流中的中位数困难

与主站 295 题相同:https://leetcode-cn.com/problems/find-median-from-data-stream/

42.连续子数组的最大和dp,空间1

public int maxSubArray(int[] nums) {
        int pre = 0, maxAns = nums[0];
        for (int x : nums) {
            pre = Math.max(pre + x, x);
            maxAns = Math.max(maxAns, pre);
        }
        return maxAns;
    }

43.整数中出现1的次数(LCR162数字1的个数)

困难与主站 233 题相同:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

44.数字序列中某一位的数字(LCR163找到第K位数字)

与主站 400 题相同:https://leetcode-cn.com/problems/nth-digit/

https://leetcode.cn/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof/solutions/219252/mian-shi-ti-44-shu-zi-xu-lie-zhong-mou-yi-wei-de-6/

public int findKthNumber(int k) {
        int digit = 1;
        long start = 1;
        long count = 9;
        while (k > count) { // 1.
            k -= count;
            start *= 10;
            digit += 1;
            count = digit * start * 9;
        }
        long num = start + (k - 1) / digit; // 2.
        return Long.toString(num).charAt((k - 1) % digit) - '0'; // 3.
    }

45.把数组排列成最小的数(LCR164)

快排,,力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

具体做法:

step 1:优先判断空数组的特殊情况。
step 2:将数组中的数字元素转换成字符串类型。
step 3:重载排序比较为字符串类型的x + y < y + x,然后进行排序。
step 4:将排序结果再按照字符串拼接成一个整体。

import java.util.*;
public class Solution {
    public String PrintMinNumber(int [] numbers) {
        //空数组的情况
        if(numbers == null || numbers.length == 0)
            return "";
        String[] nums = new String[numbers.length];
        //将数字转成字符
        for(int i = 0; i < numbers.length; i++)
            nums[i] = numbers[i] + "";
        //按照重载排序
        Arrays.sort(nums, new Comparator<String>() {
            public int compare(String s1, String s2) {
                return (s1 + s2).compareTo(s2 + s1);
            }
        });
        StringBuilder res = new StringBuilder();
        //字符串叠加
        for(int i = 0; i < nums.length; i++)
            res.append(nums[i]);
        return res.toString();
    }
}
 

46.把数字翻译成字符串(165)中等

状态定义: 设动态规划列表 dp,dp[i] 代表以x i为结尾的数字的翻译方案数量。

转移方程: 若x_i 和 xi组成的两位数字可被整体翻译,

则 dp[i]=dp[i−1]+dp[i−2],否则 dp[i]=dp[i−1]。

47.礼物的最大价值166

dp

48.最长不含重复字符的子字符串167

与主站 3 题相同:https://leetcode.cn/problems/longest-substring-without-repeating-characters/

滑动窗口/动规 + 哈希表

. - 力扣(LeetCode)

49.丑数168

与主站 264 题相同:https://leetcode-cn.com/problems/ugly-number-ii/

存优先队列。O(N)
 int a = 0, b = 0, c = 0;
        int[] res = new int[n];
        res[0] = 1;
        for(int i = 1; i < n; i++) {
            int n2 = res[a] * 2, n3 = res[b] * 3, n5 = res[c] * 5;
            res[i] = Math.min(Math.min(n2, n3), n5);
            if (res[i] == n2) a++;
            if (res[i] == n3) b++;
            if (res[i] == n5) c++;
        }
        return res[n - 1];

50.第一个只出现一次的字符169

哈希表。简化为true标记。

51.数组中的逆序对困难170

. - 力扣(LeetCode)

52.两个链表的第一个公共节点171

双指针

53.在排序数组中查找数字172、173

与主站 34 题相同(仅返回值不同):https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/

O(logN)

53 - II. 0 ~ n-1 中缺失的数字

排序数组中的搜索问题,首先想到 二分查找 解决.减少冗余。

54.二叉搜索树的第K大的节点174

测试用例
功能测试(各种形态不同的二叉搜索树)
边界值测试(输入k为0、1、二叉搜索树的节点数、二叉树搜索树的节点数+1)
特殊输入测试(指向二叉搜索树根节点的指针为空指针)
题目考点
考察应聘者的只是迁移能力,利用中序遍历解题。
考察应聘者对二叉搜索树和中序遍历的特点的理解。
解题思路
按照中序遍历的顺序遍历一个二叉搜索树。倒序,找到第k提前结束。

55.二叉树的深度175,- II平衡二叉树176

与主站 110 题相同:https://leetcode-cn.com/problems/balanced-binary-tree/

题目二:对二叉树做后序遍历,从底至顶返回子树深度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。

56.- I. 数组中数字出现的次数177

. - 力扣(LeetCode)异或位运算

56 - II. 数组中数字出现的次数 II178

统计所有数字的各二进制位中 111 的出现次数,并对 333 求余,结果则为只出现一次的数字。
有限状态自动机
class Solution {
    public int trainingPlan(int[] actions) {
        int ones = 0, twos = 0;
        for(int action : actions){
            ones = ones ^ action & ~twos;
            twos = twos ^ action & ~ones;
        }
        return ones;
    }
}

57.和为 s 的两个数字179

解题思路:
利用 HashMap 可以通过遍历数组找到数字组合,时间和空间复杂度均为 O(N) ;
注意本题的 nums是 排序数组 ,因此可使用 双指针法 将空间复杂度降低至 O(1) 。

提醒一下,判断条件最好不要用相加后的结果,应该用target - nums[i] 跟 nums[j]比较,这样保证不会溢出。虽然这题中不会出错。同样的例子还有二分查找,(left + right) / 2 可以用left + ((rigth - left) >> 1))代替

Krahets
@Little boy 哈喽,感谢指出,非常细节! 感觉严格说来 target - nums[i] 也可能越界,因为 nums[i] 可能是负数,因此我觉得用 long 去比可能是最安全~

57 - II. 和为 s 的连续正数序列180

滑动窗口的重要性质是:窗口的左边界和右边界永远只能向右移动,而不能向左移动。这是为了保证滑动窗口的时间复杂度是 O(n)。

toArray(new T[0])能够运行是因为java做了优化,能动态生成对应大小的数组,是官方建议的书写方式,能避免某些并发问题并且效率更高

力扣求和公式 / 滑动窗口

58.翻转字符串181

分割+倒序

58 - II. 左旋转字符串182

字符串拼接substring( ,)

59.- I. 滑动窗口的最大值183困难

与主站 239 题相同:https://leetcode.cn/problems/sliding-window-maximum/

算法流程:
初始化: 双端队列 deque ,结果列表res ,数组长度 n ;
滑动窗口: 左边界范围 i∈[1−limit,n−limit],右边界范围 j∈[0,n−1];
若 i>0 且 队首元素 deque[0] = 被删除元素 heights[i - 1] :则队首元素出队;
删除 deque内所有 <heights[j] 的元素,以保持 deque 递减;
将 heights[j]添加至 deque 尾部;
若已形成窗口(即 i≥0i \geq 0i≥0 ):将窗口最大值(即队首元素 deque[0] )添加至列表 res;
返回值: 返回结果列表 res

59 - II. 队列的最大值184

空间换时间,双头队列递减。
class Checkout {
    Queue<Integer> queue;
    Deque<Integer> deque;
    public Checkout() {
        queue = new LinkedList<>();
        deque = new LinkedList<>();
    }
    public int get_max() {
        return deque.isEmpty() ? -1 : deque.peekFirst();
    }
    public void add(int value) {
        queue.offer(value);
        while(!deque.isEmpty() && deque.peekLast() < value)
            deque.pollLast();
        deque.offerLast(value);
    }
    public int remove() {
        if(queue.isEmpty()) return -1;
        if(queue.peek().equals(deque.peekFirst()))
            deque.pollFirst();
        return queue.poll();
    }
}

60.n个骰子的点数185

考察应聘者的数学建模能力。对递归和循环的性能的理解。

使得n-1点数概率数组和1点数概率数组元素两两相乘,并将乘积结果加到n点数概率数组上。
运算完成后就得到了最终的n点数概率数组。

基本思路如上,然后我们可以根据动态规划的套路:
1.构造dp数组:tmp[]为n个骰子的点数概率数组,pre[]为n-1个骰子的点数概率数组,一个骰子的点数概率数组显然是6个六分之一,不需要另设数组。
2.初始化dp数组:pre[]={1/6d,1/6d,1/6d,1/6d,1/6d,1/6d}
3.构造状态转移方程:tmp[x+y]+=pre[x]*num[y];
Java代码如下:

    public double[] twoSum(int n) {
        double pre[]={1/6d,1/6d,1/6d,1/6d,1/6d,1/6d};
        for(int i=2;i<=n;i++){
            double tmp[]=new double[5*i+1];
            for(int j=0;j<pre.length;j++)
                for(int x=0;x<6;x++)
                    tmp[j+x]+=pre[j]/6;
            pre=tmp;
        }
        return pre;
    }

61.顺子186

62. 圆圈中最后剩下的数字187

迭代,避免递归使用栈空间。
class Solution {
    public int iceBreakingGame(int num, int target) {
        int f = 0;
        for (int i = 2; i != num + 1; ++i) {
            f = (target + f) % i;
        }
        return f;
    }
}
复杂度分析

时间复杂度:O(num),需要求解的函数值有 num 个。

空间复杂度:O(1),只使用常数个变量。

63.股票的最大利润188

与主站 121 题相同:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

class Solution {
    public int maxProfit(int[] prices) {
        int cost = Integer.MAX_VALUE, profit = 0;
        for(int price : prices) {
            cost = Math.min(cost, price);
            profit = Math.max(profit, price - cost);
        }
        return profit;
    }
}

64.求1+2+...+n189

解题思路
反正肯定是用递归,然后我们需要的就是怎么把返回条件确定好。

条件与 && 具有短路原则,即在第一个条件语句为 false 的情况下不会去执行第二个条件语句。利用这一特性,将递归的返回条件取非然后作为 && 的第一个条件语句,递归的主体转换为第二个条件语句,那么当递归的返回条件为 true 的情况下就不会执行递归的主体部分,递归返回。

class Solution {
    public int sumNums(int n) {
        boolean x = n > 1 && (n += sumNums(n - 1)) > 0;
        return n;
    }
}

65.不用加减乘除做加法190

解题思路---计算机二进制位运算

1.利用位异或运算不考虑进位地加两个数
2.利用位与运算以及左移运算得到进制代表的值
3.将上面1,2的结果相加(即重复1,2运算,直到进位为0)

66.构建乘积数组191

【双向遍历】具体做法:

step 1:初始化数组B,第一个元素为1.
step 2:从左到右遍历数组A,将数组B的前一个数与数组A的前一个数相乘就得到了下三角中数组B的当前数。
step 3:再从右到左遍历数组A,用一个数字记录从右到左上三角中的累乘,每次只会乘上一个数,同时给数组B对应部分也乘上该累乘。

整体思路,结果集中任何一个元素 = 其左边所有元素的乘积 * 其右边所有元素的乘积。一轮循环构建左边的乘积并保存在结果集中,二轮循环 构建右边乘积的过程,乘以左边的乘积,并将最终结果保存

class Solution {
    public int[] constructArr(int[] a) {
        int len = a.length;
        if(len == 0) return new int[0];
        int[] b = new int[len];
        b[0] = 1;
        int tmp = 1;
        for(int i = 1; i < len; i++) {
            b[i] = b[i - 1] * a[i - 1];
        }
        for(int i = len - 2; i >= 0; i--) {
            tmp *= a[i + 1];
            b[i] *= tmp;
        }
        return b;
    }
}

67.把字符串转换成整数192

与主站 8 题相同:https://leetcode-cn.com/problems/string-to-integer-atoi/

不使用库函数的字符串转整数(数字越界处理)。

68.I二叉搜索树的最近公共祖先193

与主站 235 题相同:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

迭代一层层排除O(N)。

68 - II. 二叉树的最近公共祖先194

与主站 236 题相同:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

递归O(N),四种情况终止条件、递推分类。。

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值