剑指offer:数字序列中某一位的数字、把数字翻译成字符串、礼物的最大价值、最长不含重复字符的子串、丑数、第一个只出现一次的字符、数组中的逆序对、两个链表的第一个公共节点、在排序数组中查找数字

数字序列中某一位的数字

题目

在这里插入图片描述

题解

代码:

 public int findNthDigit(int n) {

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

把数字翻译成字符串

题目

在这里插入图片描述

题解

【方法一】:时间复杂度O(N),空间复杂度O(N)
思路:先将数字转成字符串,遍历字符串统计可能得翻译种数
dp[i]的含义为以第i个字符结尾的子串可能的翻译种数
转移方程:

(1)dp[i]=dp[i-1]+dp[i-2] 此情况为第i-1个字符和第i个字符构成的数字在10和25之间
(2)dp[i]=dp[i-1] 此情况为第i-1个字符和第i个字符构成的数字不在10和25之间

因为i位置只与i-1和i-2位置有关,因此我们使用有限个变量迭代的过程替代dp数组

  public int translateNum(int num) {

        String str=String.valueOf(num);
        int b=1;
        int a=1;
        for(int i=2;i<=str.length();i++){
            int tmp=(str.charAt(i-1)-'0')+(str.charAt(i-2)-'0')*10;
            int c=tmp>=10&&tmp<=25?a+b:a;
            b=a;
            a=c;
        }
        return a;
    }

【方法二】:时间复杂度O(N),时间复杂度O(1)
思路:从后往前遍历,省去了将数字转成String类型的空间

public int translateNum(int num) {
       
      int a=1;
      int b=1;
      int x;
      int y=num%10;
      while(num!=0){
          num/=10;
          x=num%10;
          int tmp=x*10+y;
          int c=tmp>=10 && tmp<=25?a+b:a;
          b=a;
          a=c;
          y=x;
      }
       return a;
    }

礼物的最大价值

题目

在这里插入图片描述

题解

代码:

    public int maxValue(int[][] grid) {
        
        int n=grid.length;
        int m=grid[0].length;
        int[][] dp=new int[n][m];
        dp[0][0]=grid[0][0];
        for(int i=1;i<n;i++){
            dp[i][0]=dp[i-1][0]+grid[i][0];
        }
        for(int j=1;j<m;j++){
            dp[0][j]=dp[0][j-1]+grid[0][j];
        }
        for(int i=1;i<n;i++){
            for(int j=1;j<m;j++){
                dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[n-1][m-1];
    }

最长不含重复字符的子串

题目

在这里插入图片描述

题解

方法一:双指针+滑动窗口

    public int lengthOfLongestSubstring(String s) {
        int l=0;
        int r=0;
        int max=0;
        Set<Character> set=new HashSet<>();
        while(r<s.length()){
            if(!set.contains(s.charAt(r))){
                set.add(s.charAt(r));
                r++;
                max=Math.max(max,r-l);
            }else {
                while(set.contains(s.charAt(r))){
                    set.remove(s.charAt(l));
                    l++;
                }
                set.add(s.charAt(r));
                r++;
            }
        }
        return max;
    }

方法二:动态规划+哈希表

dp[j]含义为以j位置字符结尾的不含重复字符的子串长度
转移方程:对于j位置字符来说,通过map得到离j位置最近的相同字符s[i]
(1)如果i小于0,说明s[j]左边不含与s[j]相同的字符
(2)dp[j-1]<j-i,说明s[i]在dp[j-1]所包含的子串之外,dp[j]=dp[j-1]+1
(3)dp[j-1]>j-i,说明在dp[j-1]包含的字串中存在于j位置相同的重复字符,因此应以j-i位置为准,dp[j]=j-i

由于dp[j]只与dp[j-1]位置有关,因此使用有限个变量迭代来代替dp数组

    public int lengthOfLongestSubstring(String s) {
       
      int ret=0;
      int tmp=0;
       HashMap<Character,Integer> map=new HashMap<>();
      for(int j=0;j<s.length();j++){
          int i=map.getOrDefault(s.charAt(j),-1);
          map.put(s.charAt(j),j);
          tmp=tmp<j-i?tmp+1:j-i;
          ret=Math.max(tmp,ret);
      }
      return ret;
    }

丑数

题目

在这里插入图片描述

题解

dp[i]的含义为第i个丑数

定义三个指针,p2,p3,p5,表示下一个丑数是当前指针指向的质因子,初始都为1,dp[i]=min(dp[p2]*2,dp[p3]*3,dp[p5]*5),然后分别比较 dp[i]和dp[p2]*2,dp[p3]*3,dp[p5]*5是否相等,相等的话将对相应的指针+1

 public int nthUglyNumber(int n) {
         int[] dp=new int[n+1];
         dp[1]=1;
         int p2=1;
         int p3=1;
         int p5=1;
         for(int i=2;i<=n;i++){
             int num2=dp[p2]*2;
             int num3=dp[p3]*3;
             int num5=dp[p5]*5;
             dp[i]=Math.min(Math.min(num2,num3),num5);
             if(dp[i]==num2) p2++;
             if(dp[i]==num3) p3++;
             if(dp[i]==num5) p5++;
         }
         return dp[n];
    }

第一个只出现一次的字符

题目

在这里插入图片描述

题解

思路:使用有序的哈希表统计字符串中每个字符的出现次数,由于有序哈希表默认是按照插入顺序进行排序的,因此只需再次遍历哈希表就能得到结果
代码:

  public char firstUniqChar(String str) {
          
          //LinkedHashMap 是有序的哈希表,默认顺序为插入顺序
          Map<Character,Boolean> map=new LinkedHashMap<>();
          char[] s=str.toCharArray();
          for(char c:s){
              map.put(c,!map.containsKey(c));
          }
          for(Map.Entry<Character,Boolean> entry:map.entrySet()){
              if(entry.getValue()) return entry.getKey();
          }
       return ' ';
    }

数组中的逆序对

题目

在这里插入图片描述

题解

代码:

    private int count=0;
    public int reversePairs(int[] nums) {
          
          mergeSort(nums,0,nums.length-1);
          return count;
    }

    private void mergeSort(int[] nums,int left,int right){
       if(left>=right) return;
       int mid=(left+right)/2;
       mergeSort(nums,left,mid);
       mergeSort(nums,mid+1,right);
       merge(nums,left,mid,right);
    }
    private void merge(int[] nums,int left, int mid ,int right){
        int[] tmp=new int[right-left+1];
        int k=0;
        int i=left;
        int j=mid+1;
        while(i<=mid && j<=right){
            if(nums[i]<=nums[j]){
                tmp[k++]=nums[i++];
            }else {
                tmp[k++]=nums[j++];
                count+=(mid-i+1);
            }
        }
        while(i<=mid){
            tmp[k++]=nums[i++];
        }
        while(j<=right){
            tmp[k++]=nums[j++];
        }
        for(int val:tmp){
            nums[left++]=val;
        }
    }

两个链表的第一个公共节点

题目

在这里插入图片描述

题解

代码:

ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         
         ListNode cur1=headA;
         ListNode cur2=headB;
         while(cur1!=cur2){
             cur1=cur1==null?headB:cur1.next;
             cur2=cur2==null?headA:cur2.next;
         }
         return cur2;
    }

在排序数组中查找数字

题目

在这里插入图片描述

题解

思路:通过二分查找target和target-1的右边界,相减后得到值为target的元素个数

代码:

 public int search(int[] nums, int target) {
     
     return helper(nums,target)-helper(nums,target-1);
    }

   public int helper(int[] nums,int target){

       int i=0;
       int j=nums.length-1;
       while(i<=j){
           int mid=(i+j)/2;
           if(nums[mid]>target) j=mid-1;
           else if(nums[mid]<=target) i=mid+1;
       }
       return i;
   } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值