• LeetCode刷题第4周小结


     

    目录

    1.X的平方根

    2.赎金信

    3.可被5整除的二进制前缀

    4. 2的幂

    5.最大子数组和

    6.多数元素

    7.二叉树的最大深度

    关键字

    分治算法、二分查找、字符统计、模运算法则、快速幂算法、递归、二叉树、秦九韶算法


    1.X的平方根

    难度:★  链接:力扣

     解题思路:

    使用二分查找法,在0~x的区间内不断二分地去查找平方和可能等于x的数字,其中二分的区间中点mid的平方如果小于等于x,那么其可能是x的算术平方根,将其保存在一个变量res中,并且调整区间的左端点left=mid+1;如果mid的平方大于x,则说明mid不可能是x的平方根,调整区间的右端点right=mid-1;最终返回一个无限趋近于x的平方根或者直接得到的就是x的平方根的变量res

    解题代码:

    1. class Solution {
    2. public int mySqrt(int x) {
    3. //二分查找法,在0~x范围内查找平方小于等于x的数字,符合这一条件的都是可能解
    4. int left=0,right=x,res=-1;
    5. while(left<=right){
    6. //这种方式可以避免数据过大而溢出
    7. int mid=left+(right-left)/2;
    8. if((long)mid*mid<=x){
    9. res=mid;
    10. left=mid+1;
    11. }else{
    12. right=mid-1;
    13. }
    14. }
    15. return res;
    16. }
    17. }

     


     

    2.赎金信

    难度★  链接:力扣

     

    解题思路:

    本题就是一个关于字符串的简单题,思路很简单,建立一个大小为26的整型数组a来存放字母的频次,先遍历magazine里面的每一个字母统计其频次通过ASCII码值相减得到对应的数组下标将其存放到数组中,然后再遍历randomNote,将其出现的字母频次从数组中减去,最后遍历数组a,如果存在某个值小于0的情况则返回false,说明randomNote中出现了某个字母的频次大于magazine中出现的频次,显然不可能由它构成。

    解题代码:

    1. class Solution {
    2. public boolean canConstruct(String ransomNote, String magazine) {
    3. int []a=new int[26];
    4. for(int i=0;i
    5. a[magazine.charAt(i)-'a']++;
    6. }
    7. for(int j=0;j
    8. a[ransomNote.charAt(j)-'a']--;
    9. }
    10. for(int k=0;k<26;k++){
    11. if(a[k]<0)
    12. return false;
    13. }
    14. return true;
    15. }
    16. }

     


     

    3.可被5整除的二进制前缀

    难度:★   链接:力扣

     

    解题思路:

    蛮力法,依次求出每一组二进制前缀对应的十进制数字,然后对5进行取模来判断。值得一提的是,计算高次幂的时候,这里使用了秦九韶算法,将一个一元n次幂的式子转换成n个一次式来计算,这样大大降低了时间复杂度,比普通累乘运算提高了一个数量级!

    解题代码:

    1. class Solution {
    2. public List prefixesDivBy5(int[] nums) {
    3. Listans=new ArrayList<>();
    4. int remain=0;
    5. for(int i=0;i
    6. //只需保留每次计算后对5进行模运算留下的余数,防止后面数据较大而溢出
    7. remain=(remain*2+nums[i])%5;//关键
    8. if(remain==0){
    9. ans.add(true);
    10. }else{
    11. ans.add(false);
    12. }
    13. }
    14. return ans;
    15. }
    16. }

     


     

    4. 2的幂

    难度: ★  链接:力扣

     

    解题思路:二分法

    本题问题规模巨大,如果纯使用蛮力法一定会被判超时,所以自然想到了使用二分法来做,一个数的平方根不会超过它本身的一半,所以二分区间的右边界初始值为n/2,下面将其中点处的平方和与n进行比较,通过不断缩小搜索的区间,直到最终返回结果。

    解题代码:

    1. class Solution {
    2. public boolean isPowerOfTwo(int n) {
    3. int left=0,right=n/2;
    4. while(left<=right){
    5. int mid=left+(right-left)/2;
    6. if(Math.pow(2,mid)==n){
    7. return true;
    8. }else if(Math.pow(2,mid)
    9. left=mid+1;
    10. }else{
    11. right=mid-1;
    12. }
    13. }
    14. return false;
    15. }
    16. }

     


     

    5.最大子数组和

    难度:★ ★  链接:力扣

     

    解题思路:分治算法

    因为最大子序列可能是如下三种情况:第一种,在数组的左半区,第二种在数组的右半区,第三种就是最大子序列横跨过中间点,一部分在左半区一部分在右半区构成了最大子序列。对于前两种情况可以使用分治法,递归地求解左半区和右半区的最大子序列和,第三种情况则需要自己去求解,最后将三者进行比较,返回其最大值。这里有一个细节,就是s1,s2的初始值要设置成大负整数(只需要稍微大于数据规模即可),例如[-2,-1] ,如果s1,s2初始值为0,则最终数组最大的子数组和为-1,但是按照代码思路应该是0,所以应该将其设置成一个大的负整数从而让其返回正确的结果。

    解题代码:

    1. class Solution {
    2. public int maxSubArray(int[] nums) {
    3. return maxSum(nums,0,nums.length-1);
    4. }
    5. public int maxSum(int[]nums,int left,int right){
    6. int leftSum=0,rightSum=0,midSum=0,s1=-10005,s2=-10005;
    7. int lefts=0,rights=0,sum=0;
    8. //边界条件
    9. if(left==right){
    10. return nums[left];
    11. }
    12. int mid=left+(right-left)/2;
    13. //递归求解左半区和右半区的最大子序列和
    14. leftSum=maxSum(nums,left,mid);
    15. rightSum=maxSum(nums,mid+1,right);
    16. //下面求第三种情况,横跨中间点的最大子序列
    17. for(int i=mid;i>=left;i--){
    18. lefts+=nums[i];
    19. if(lefts>s1)s1=lefts;
    20. }
    21. for(int j=mid+1;j<=right;j++){
    22. rights+=nums[j];
    23. if(rights>s2)s2=rights;
    24. }
    25. midSum=s1+s2;
    26. sum=Math.max(leftSum,midSum);
    27. sum=Math.max(sum,rightSum);
    28. return sum;
    29. }
    30. }


     

    6.多数元素

    难度:★   链接:力扣

     

    思路1:蛮力法

    直接暴力循环匹配,寻找频次大于n/2的数字

    代码:

    1. class Solution {
    2. public int majorityElement(int[] nums) {
    3. int n=nums.length;
    4. if(n==1){
    5. return nums[0];
    6. }
    7. for(int i=0;i1;i++){
    8. int now=nums[i];
    9. int count=0;
    10. for(int j=i+1;j
    11. if(nums[j]==now){
    12. count++;
    13. }
    14. }
    15. if(count>=n/2){
    16. return nums[i];
    17. }
    18. }
    19. return -1;
    20. }
    21. }

     

    思路2:分治法

    分别将问题规模为n的数组不断划分,分解成若干个与原问题解法类似的子问题,分别寻找左区间与右区间出现频次最多的元素,如果二者相等则说明它是整个数组出现次数最多的元素则直接返回即可;如果不相等,则要将两个数分别在数组中进行遍历统计频次,返回频次较大者。正因为合并这一步需要如此复杂且耗时,所以针对本题而言,分治法不是一种很好的解法,但需要体会这种分治的解题思想。

    代码:

    1. class Solution {
    2. public int majorityElement(int[] nums) {
    3. //分治算法解决
    4. return maxCountNumber(nums,0,nums.length-1);
    5. }
    6. //求解众数
    7. public int maxCountNumber(int nums[],int left,int right){
    8. if(left==right){
    9. return nums[left];
    10. }
    11. int mid=left+(right-left)/2;
    12. int low=maxCountNumber(nums,left,mid);
    13. int high=maxCountNumber(nums,mid+1,right);
    14. if(low==high){
    15. return low;
    16. }
    17. int leftCount=count(nums,low,left,right);
    18. int rightCount=count(nums,high,left,right);
    19. return leftCount>rightCount?low:high;
    20. }
    21. //计算某个数在[left,right]区间内的频次
    22. public int count(int nums[],int number,int left,int right){
    23. int count=0;
    24. for(int i=0;i
    25. if(nums[i]==number){
    26. count++;
    27. }
    28. }
    29. return count;
    30. }
    31. }

     

    思路3:巧解法

    通过观察发现,题目要求返回一个众数,即出现的次数大于50%,例如在100个数字中则众数至少要出现51次,非众数则最多出现49次,所以可以从这一点出发思考:可以将数组升序排列,返回中间的那个数即为众数。比如a[1,2,2,2,3,4,5] 返回a[a.length/2]即a[3]=2 ,则2即为该数组中的众数。这个方法属于比较巧妙的方法,性能也不错。我直呼NB

    代码:

    1. class Solution {
    2. public int majorityElement(int[] nums) {
    3. Arrays.sort(nums);
    4. return nums[nums.length/2];
    5. }
    6. }


     

    7.二叉树的最大深度

    难度:★   链接:力扣

     

    解题思路:遇到树,百分之八十使用递归来解题,分别遍历其左右子树的最大深度最后加一

    解题代码:

    1. /**
    2. * Definition for a binary tree node.
    3. * public class TreeNode {
    4. * int val;
    5. * TreeNode left;
    6. * TreeNode right;
    7. * TreeNode() {}
    8. * TreeNode(int val) { this.val = val; }
    9. * TreeNode(int val, TreeNode left, TreeNode right) {
    10. * this.val = val;
    11. * this.left = left;
    12. * this.right = right;
    13. * }
    14. * }
    15. */
    16. class Solution {
    17. public int maxDepth(TreeNode root) {
    18. //递归的终止条件
    19. if(root==null)return 0;
    20. else{
    21. //分别递归计算根节点两颗子树的最大深度
    22. int leftDepth=maxDepth(root.left);
    23. int rightDepth=maxDepth(root.right);
    24. //加1,那个1就是第一层根节点的深度
    25. return Math.max(leftDepth,rightDepth)+1;
    26. }
    27. }
    28. }

    参考资料:

    秦九韶算法

    全网最细快速幂算法讲解

  • 相关阅读:
    Linux驱动开发(十)---树莓派输入子系统学习(红外接收)
    python学习2
    QODBC查询Oracle中文乱码问题
    Facebook广告投放技巧
    医疗HIS行业短信发送解决方案
    华为数通方向HCIP-DataCom H12-831题库(单选题:261-280)
    发布Android库至MavenCentral详解
    基于微服务、Java、Springcloud、Vue、MySQL开发的智慧工地管理系统源码
    KMP算法(求解字符串匹配)
    Python 设计模式之单例模式
  • 原文地址:https://blog.csdn.net/qq_52487066/article/details/126931187