【leetcode】 剑指 Offer学习计划(java版本含注释)(下)

目录

  • 前言
  • 第十六天(排序)
    • [剑指 Offer 45. 把数组排成最小的数(中等)](#剑指 Offer 45. 把数组排成最小的数(中等))
    • [剑指 Offer 61. 扑克牌中的顺子(简单)](#剑指 Offer 61. 扑克牌中的顺子(简单))
  • 第十七天(排序)
    • [剑指 Offer 40. 最小的k个数(简单)](#剑指 Offer 40. 最小的k个数(简单))
  • 第十八天(搜索与回溯算法)
    • [剑指 Offer 55 - I. 二叉树的深度(简单)](#剑指 Offer 55 - I. 二叉树的深度(简单))
    • [剑指 Offer 55 - II. 平衡二叉树(简单)*](#剑指 Offer 55 - II. 平衡二叉树(简单)*)
  • 第十九天(搜索与回溯算法)
    • [剑指 Offer 64. 求1+2+...+n(中等)](#剑指 Offer 64. 求1+2+…+n(中等))
    • [剑指 Offer 68 - I. 二叉搜索树的最近公共祖先(简单)](#剑指 Offer 68 - I. 二叉搜索树的最近公共祖先(简单))
    • [剑指 Offer 68 - II. 二叉树的最近公共祖先(简单)*](#剑指 Offer 68 - II. 二叉树的最近公共祖先(简单)*)
  • 第二十天(分治算法)
    • [剑指 Offer 07. 重建二叉树(中等)*](#剑指 Offer 07. 重建二叉树(中等)*)
    • 第二十一天(位运算)
    • [剑指 Offer 15. 二进制中1的个数(简单)](#剑指 Offer 15. 二进制中1的个数(简单))
    • [剑指 Offer 65. 不用加减乘除做加法(简单)*](#剑指 Offer 65. 不用加减乘除做加法(简单)*)
  • 第二十三天(数学)
    • [剑指 Offer 39. 数组中出现次数超过一半的数字(简单)](#剑指 Offer 39. 数组中出现次数超过一半的数字(简单))
    • [剑指 Offer 66. 构建乘积数组(中等)*](#剑指 Offer 66. 构建乘积数组(中等)*)
  • 第二十五天
    • [剑指 Offer 29. 顺时针打印矩阵(简单)](#剑指 Offer 29. 顺时针打印矩阵(简单))

前言

该链接的学习计划如下:
剑指 Offer学习计划

上一版本的博文链接如下:
【leetcode】 剑指 Offer学习计划(java版本含注释)(上)

此贴两年前写的,一直在草稿箱里,今天释放出来,后续有时间完善下!(= - =)

第十六天(排序)

剑指 Offer 45. 把数组排成最小的数(中等)

题目:

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例 1:

输入: [10,2]

输出: "102"

示例 2:

输入: [3,30,34,5,9]

输出: "3033459"

提示:

0 < nums.length <= 100

说明:

输出结果可能非常大,所以你需要返回一个字符串而不是整数

拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0


思路:

关于这部分的具体核心逻辑代码

可参考如下链接:

剑指 Offer 45. 把数组排成最小的数(自定义排序,清晰图解)

java 复制代码
class Solution {
    public String minNumber(int[] nums) {
        //新建一个字符数组
        String []s=new String[nums.length];
        //遍历每个数组到字符数组中,具体转类型,通过String.valueOf(nums[i]);
        for(int i=0;i<nums.length;i++){
            s[i]=String.valueOf(nums[i]);
        }
        //通过快排将其排好顺序,运用大小的转换
        quicksort(s,0,s.length-1);

        //通过StringBuilder() 添加数组的,之后输出其toString类型
        StringBuilder res = new StringBuilder();
        for(int i=0;i<s.length;i++){
            res.append(s[i]);
        }
        
        return res.toString();

    }
    public void quicksort(String [] s,int l,int r){
        if(l>=r)return ;
        
        int left=l;
        int right=r;

        //具体快排的方式,主要是通过该逻辑x+y>y+x  则x大于y。如果x+y<y+x  则x小于y
        // 330 大于 303 。也就是30要排在3的后面。用此逻辑将其快排
        while(left<right){
            while(left<right && (s[right]+s[l]).compareTo(s[l]+s[right]) >=0)right--;
            while(left<right && (s[left]+s[l]).compareTo(s[l]+s[left]) <=0)left++;
            swap(s,left,right);
        }
        swap(s,left,l);

        quicksort(s,l,left-1);
        quicksort(s,left+1,r);
    }

    public void swap(String [] s,int left,int right){
        String temp=s[left];
        s[left]=s[right];
        s[right]=temp;
    }
}

剑指 Offer 61. 扑克牌中的顺子(简单)

题目:

从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

示例 1:

输入: [1,2,3,4,5]

输出: True

示例 2:

输入: [0,0,1,2,5]

输出: True

限制:

数组长度为 5

数组的数取值为 [0, 13] .


思路:

java 复制代码
class Solution {
    public boolean isStraight(int[] nums) {

        Arrays.sort(nums);//将其数组进行排序
        int res=0;
        //只遍历到最后一个
        for(int i=0;i<4;i++){
            if(nums[i]==0){
                res++;//统计大小王的数量
            }
            //如果有重复的提前返回false
            else if(nums[i+1]==nums[i])
                
                return false;

        }
        //用最后一个值减去 最小值(除了0),如果小于5则是顺子
        return nums[4]-nums[res]<5;
    }
}

或者直接比较最大值最小值即可

java 复制代码
class Solution {
    public boolean isStraight(int[] nums) {

        Set<Integer> repeat=new HashSet<>();
        int max=Integer.MIN_VALUE;
        int min=Integer.MAX_VALUE;
        for(int i=0;i<5;i++) {
            if(nums[i] == 0) continue; // 跳过大小王
            max = Math.max(nums[i],max); // 最大牌
            min = Math.min(nums[i],min); // 最小牌
            if(repeat.contains(nums[i])) return false; // 若有重复,提前返回 false
            repeat.add(nums[i]); // 添加此牌至 Set
        }

        return max-min<5;
    }
}

注意这种写法:

java 复制代码
int max=Integer.MIN_VALUE;
int min=Integer.MAX_VALUE;

第十七天(排序)

剑指 Offer 40. 最小的k个数(简单)

题目:

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2

输出:[1,2] 或者 [2,1]

示例 2:

输入:arr = [0,1,2,1], k = 1

输出:[0]

限制:

0 <= k <= arr.length <= 10000

0 <= arr[i] <= 10000


思路:

可以通过排序,然后在弄,但是复杂度比较高

这道题是简单题

可以使用复杂度比较低一些的

例如

快排

java 复制代码
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        quicksort(arr,0,arr.length-1);
        //数组的截取前几个,通过这个函数
        return Arrays.copyOf(arr,k);

    }
    public void quicksort(int [] arr,int l,int r){
        if(l>=r)return;

        //记住左右的节点,因为l作为基准,到时候要替换变成有序的元素
        int left=l;
        int right=r;
        while(left<right){
            while(left<right&&arr[right]>=arr[l])right--;
            while(left<right&&arr[left]<=arr[l])left++;
            swap(arr,left,right);
        }
        //不要忘记更换基准的元素
        swap(arr,left,l);
        
        //中间节点已经有序,继续排左右两边的子数组就好
        quicksort(arr,l,left-1);
        quicksort(arr,left+1,r);
    }
    public void swap(int []arr,int left,int right){
        int temp=arr[left];
        arr[left]=arr[right];
        arr[right]=temp;
    }
}

或者使用大根堆或者小根堆的实现方式

以下使用的是PriorityQueue

大根堆(前 K 小) / 小根堆(前 K 大)

java 复制代码
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if (k == 0 || arr.length == 0) {
            return new int[0];
        }
        // 默认是小根堆,实现大根堆需要重写一下比较器。
        Queue<Integer> pq = new PriorityQueue<>((o1,o2)->(o2-o1));
        for (int num: arr) {
            if (pq.size() < k) {
                pq.offer(num);
            }else if(num<pq.peek()){
                pq.poll();
                pq.offer(num);
            }
        }
        
        // 返回堆中的元素
        int[] res = new int[pq.size()];
        int idx = 0;
        for(int num: pq) {
            res[idx++] = num;
        }
        return res;
    }
}

第十八天(搜索与回溯算法)

剑指 Offer 55 - I. 二叉树的深度(简单)

题目:略


思路:

递归:

java 复制代码
class Solution {
    public int maxDepth(TreeNode root) {
        //从下往上递归,通过递归左右子树,然后将其判断哪个子树大,最后加1。往上遍历即可
        if(root==null)return 0;
        int left=maxDepth(root.left);
        int right=maxDepth(root.right);
        return Math.max(left,right)+1;

    }
}

层次遍历,在每一层加1计数即可

java 复制代码
class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);

        //为了方便计数每一层size,也就是最大的深度
        int ans = 0;
        while (!queue.isEmpty()) {
            int n = queue.size();
            for(int i=0;i<n;i++) {
                TreeNode node = queue.poll();
                if (node.left != null)queue.offer(node.left);               
                if (node.right != null)  queue.offer(node.right);
                
                
            }
            ans++;
        }
        return ans;
    }
}

剑指 Offer 55 - II. 平衡二叉树(简单)*

题目:略


思路:

java 复制代码
class Solution {
    public boolean isBalanced(TreeNode root) {
        //返回的高度一定是非负整数,如果abs一直大于1,则会有负数的
        return heigh(root)>=0;

    }
    //重新定义一个int类型的函数,主要是为了返回左右子树的最大深度,在中间加一些判断条件
    public int heigh(TreeNode root){
        //终止条件判断,如果叶子节点为null,则直接返回0
        if(root==null)return 0;

        //从上往下递归遍历,一开始在最下面的一层次遍历
        int left=heigh(root.left);
        int right=heigh(root.right);

        //如果左子树或者右子树为-1.则往上层递归直接变为-1。还有一个abs直接大于1
        //不要忽略左右子树,某个数为-1的情况,主要是为了方便传递
        if(left==-1||right==-1||Math.abs(left-right)>1)return -1;

        //往上返回的左右子树最大的一个,直接加1。递归条件
        return Math.max(left,right)+1;
    }
}

第十九天(搜索与回溯算法)

剑指 Offer 64. 求1+2+...+n(中等)

题目:

求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

示例 1:

输入: n = 3

输出: 6

示例 2:

输入: n = 9

输出: 45

限制:

1 <= n <= 10000


思路:

除法不能使用(数学中的求和不可使用)

for不能使用

java 复制代码
class Solution {
    public int sumNums(int n) {
        int sum=0;
        for(int i=1;i<=n;i++){
            sum+=i;
        }
        return sum;

    }
}

那就通过异或或者递归的方式

该题解主要通过k神

具体解释如下:
面试题64. 求 1 + 2 + ... + n(逻辑符短路,清晰图解)

java 复制代码
class Solution {
    //定义一个临界值,放于外面,主要是不影响递归的调用
    int sum=0;
    public int sumNums(int n) {
        //x=n>1不可写成n>1因为boolean变量要修饰。
        //主要大于1,是因为1的时候是终止条件。具体的递归通过sumNums(n-1)
        boolean x=n>1&&sumNums(n-1)>1;
        sum+=n;
        return sum;

    }
}

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先(简单)

题目:略


思路:

总体通过root不为null,一个个值比较,如果有两者相等或者不统一,直接返回root节点即可

java 复制代码
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        
        while(root!=null){
            if(root.val>p.val&&root.val>q.val){
                root=root.left;
            }else if(root.val<p.val&&root.val<q.val){
                root=root.right;
            }else {
                return root;
            }
            
        }
        return null;
        
    }
}

剑指 Offer 68 - II. 二叉树的最近公共祖先(简单)*

题目:


思路:

关于该题解,可看k神比较全面的题解:
k神题解

初始条件以及递归条件以及边界值的返回条件是什么

搞清楚逻辑传递即可

java 复制代码
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null; // 如果树为空,直接返回null
        if(root == p || root == q) return root; // 如果 p和q中有等于 root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先)
        TreeNode left = lowestCommonAncestor(root.left, p, q); // 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
        TreeNode right = lowestCommonAncestor(root.right, p, q); // 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁
        if(left == null) return right; // 如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else if(right == null) return left; // 否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else return root; //否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root
    }
}

第二十天(分治算法)

剑指 Offer 07. 重建二叉树(中等)*

题目:

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。


思路:

第二十一天(位运算)

剑指 Offer 15. 二进制中1的个数(简单)

题目:leetcode:剑指 Offer 15. 二进制中1的个数


思路:

java 复制代码
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        
        int x=1;
        int sum=0;
        while(n!=0){
            //此处记得括号
            if((n&x)==1){
                sum++;
            }
			
			//可以将上面一步到位
			//  sum += n & 1;
				
            //本题要求把数字 n 看作无符号数,因此使用 无符号右移 操作
            //无符号的移动为>>>
            n=n>>>1;
        }
        return sum;
        
    }
}

剑指 Offer 65. 不用加减乘除做加法(简单)*

题目:leetcode:剑指 Offer 65. 不用加减乘除做加法


思路:

这道题的题解主要参考k神题解

java 复制代码
class Solution {
    public int add(int a, int b) {
        while(b != 0) { // 当进位为 0 时跳出
            int c = (a & b) << 1;  // c = 进位
            a ^= b; // a = 非进位和
            b = c; // b = 进位
        }
        return a;
    }
}

第二十三天(数学)

剑指 Offer 39. 数组中出现次数超过一半的数字(简单)

题目:leetcode:剑指 Offer 39. 数组中出现次数超过一半的数字


思路:

可以用哈希统计 计算

java 复制代码
class Solution {

    public int majorityElement(int[] nums) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int num : nums) {
            map.put(num,map.getOrDefault(num,0)+1);        
        }

//--
        //定义最大值
        int max=Integer.MIN_VALUE;
        //为了保留getKey的值
        int x=0;
        
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() > max) {
                max = entry.getValue();
                x=entry.getKey();
            }
        }

        return x;

//---或者使用如下进行
 Map.Entry<Integer, Integer> majorityEntry = null;
        for (Map.Entry<Integer, Integer> entry : counts.entrySet()) {
            if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) {
                majorityEntry = entry;
            }
        }

        return majorityEntry.getKey();


    }
}

以及另外一种做法:

题解如下:k神题解

java 复制代码
class Solution {

    public int majorityElement(int[] nums) {
        //相同票数则加1,不同票数减1,如果为0的票数,设置众数为当前
        int vote=0;
        int x=0;
        for(int num:nums){
            if(vote==0)x=num;
            vote+= x==num?1:-1;
        }

        return x;
    }
}

剑指 Offer 66. 构建乘积数组(中等)*

题目:leetcode:剑指 Offer 66. 构建乘积数组

大致意思如下:


思路:

java 复制代码
class Solution {
    public int[] constructArr(int[] a) {

        if(a.length==0)return new int [0];

        int []b=new int[a.length];
        //设置第一个初值为1
        b[0]=1;
        //遍历后边的初值条件
        int temp=1;

        //计算从前往后的值,维护b【i】这个数组
        for(int i=1;i<b.length;i++){
            b[i]=b[i-1]*a[i-1];
        }

        for(int i=b.length-2;i>=0;i--){
            //从后往前的遍历,计算另外一边的值
            temp *= a[i + 1];
            b[i] *= temp;
        }

        return b;

    }
}

第二十五天

剑指 Offer 29. 顺时针打印矩阵(简单)

题目:leetcode:剑指 Offer 29. 顺时针打印矩阵


思路:

螺旋 模拟矩阵

java 复制代码
class Solution {
    public int[] spiralOrder(int[][] matrix) {

        //可能是空数组,所以要多一个判断条件
        if(matrix.length == 0) return new int[0];

        int m=matrix.length;
        int n=matrix[0].length;
        
        //个数用来存放其res的总值
        int []res=new int[m*n];

        int left=0,right=n-1,top=0,bottom=m-1;

        //为了更好的定义,配合数组下标,所以统一减个1
        int num=0,sum=m*n-1;

        while(num<=sum){

            for(int i=left;i<=right && num<=sum;i++){
                res[num++]=matrix[top][i];

            }
            top++;

            for(int i=top;i<=bottom && num<=sum;i++){
                res[num++]=matrix[i][right];

            }
            right--;

            for(int i=right;i>=left && num<=sum;i--){
                res[num++]=matrix[bottom][i];

            }
            bottom--;

            for(int i=bottom;i>=top && num<=sum;i--){
                res[num++]=matrix[i][left];

            }
            left++;
        }

        return res;

    }
}
相关推荐
程序猿零零漆1 分钟前
SpringCloud 系列教程:微服务的未来(二)Mybatis-Plus的条件构造器、自定义SQL、Service接口基本用法
java·spring cloud·mybatis-plus
猿来入此小猿3 分钟前
基于SpringBoot在线音乐系统平台功能实现十二
java·spring boot·后端·毕业设计·音乐系统·音乐平台·毕业源码
愤怒的代码16 分钟前
Spring Boot对访问密钥加解密——HMAC-SHA256
java·spring boot·后端
带多刺的玫瑰17 分钟前
Leecode刷题C语言之切蛋糕的最小总开销①
java·数据结构·算法
巫师不要去魔法部乱说28 分钟前
PyCharm专项训练5 最短路径算法
python·算法·pycharm
栗豆包32 分钟前
w118共享汽车管理系统
java·spring boot·后端·spring·tomcat·maven
夜半被帅醒39 分钟前
MySQL 数据库优化详解【Java数据库调优】
java·数据库·mysql
万亿少女的梦1681 小时前
基于Spring Boot的网络购物商城的设计与实现
java·spring boot·后端
qystca1 小时前
洛谷 P11242 碧树 C语言
数据结构·算法
冠位观测者1 小时前
【Leetcode 热题 100】124. 二叉树中的最大路径和
数据结构·算法·leetcode