2021-6-2剑指笔记00

笔记00

_01_最小的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
package LeetCode;

import org.junit.Test;

import java.util.Arrays;
import java.util.PriorityQueue;

public class _01_最小的k个数 {

    /*
    对于经典的TopK问题,有四种通用解决方法:
    1.快排
    2.堆。Java中有现成的PriorityQueue
    3.二叉搜索树
    4.数据范围有限的话直接计数
    * */


    //1.快排   O(N)
    //(int)(Math.random()*(end-start+1)+start);  [start,end]
    public int quick(int[] nums,int s,int e){
//        if (s>=e) return s;
        int i=s;
        int j=e;
        int index=nums[e];
        int temp=0;
        while (i<j){
            while (nums[i]<index&&i<j) i++;
            while (nums[j]>=index&&i<j) j--;//犯错点:少了等于号
            temp=nums[i];
            nums[i]=nums[j];
            nums[j]=temp;
        }
        //while接触,ij相遇,找到最后一个值的正确位置
        nums[e]=nums[i];
        nums[i]=index;
        return i;
    }
    public int TopK;
    public void quickSearch(int[] nums,int s,int e){
        int par=quick(nums,s,e);
        if (par==TopK-1) return;
        if (par>TopK-1)
            quickSearch(nums,s,par-1);
        else
            quickSearch(nums,par+1,e);
    }

    public int[] getLeastNumbers(int[] arr, int k) {
        arr= new int[]{3, 2, 1};
        k=2;
        TopK=k;
        quickSearch(arr,0,arr.length-1);
       return Arrays.copyOf(arr,k);
    }


    //2.堆。Java中有现成的PriorityQueue  O(NlogK)
    //用一个容量为k的大根堆,该法适合大数据处理
    public int[] getLeastNumbers2(int[] arr, int k) {
        if (k==0||arr.length==0) return new int[0];
        PriorityQueue<Integer> queue = new PriorityQueue<>((v1,v2)->v2-v1);
        for (int num : arr) {
            if(queue.size()<k)
                queue.offer(num);
            else if (num < queue.peek()){
                queue.poll();
                queue.offer(num);
            }
        }
        int[] res=new int[k];
        int index=0;
        for (Integer integer : queue) {
            res[index++]=integer;
        }
        return res;
    }


}

_02_数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例 1:

输入:
[“MedianFinder”,“addNum”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:

输入:
[“MedianFinder”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

限制:

最多会对 addNum、findMedian 进行 50000 次调用。

package LeetCode;

import org.junit.Test;

import java.util.PriorityQueue;

public class _02_数据流中的中位数 {

    public PriorityQueue<Integer> min=null;
    public PriorityQueue<Integer> max=null;
    public void init(){
        min=new PriorityQueue<>();
        max=new PriorityQueue<>((v1,v2)->v2-v1);
    }
    //平均分成两堆,左边最大值堆(都小于)右边最小值堆
    public _02_数据流中的中位数() {
       init();
    }
    //[最大堆,最小堆]
    public void addNum(int num) {
        //为实现平均分配,数据总数是偶数时插入最小堆,否则插入最大堆
        if (((max.size()+min.size())&1)==0){//最小堆
            //如果此时num比最大堆的一些数据要小怎么办?
            if (max.size()>0&&num<max.peek()){//与最大堆根值交换,如果有
                min.offer(max.poll());
                max.offer(num);
            }
            else {//最大堆空
                min.offer(num);
            }
        }
        else {//插入最大堆
            //如果此时num比最小堆的一些数据还要大怎么办?
            if (min.size()>0&&num>min.peek()){
                max.offer(min.poll());
                min.offer(num);
            }
            else {
                max.offer(num);
            }
        }


    }

    public double findMedian() {
        int lSize=max.size();
        int rSize=min.size();
        if (rSize==0&&lSize==0) return -1;
        else if (((rSize+lSize)&1)==1) return min.peek();
        else return (double)(max.peek()+min.peek())/2;
    }


}

_03_1到n整数中1的次数

输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例 1:

输入:n = 12
输出:5
示例 2:

输入:n = 13
输出:6

限制:

1 <= n < 2^31

package LeetCode;

import org.junit.Test;

import java.util.Arrays;

public class _03_1到n整数中1的次数 {

    //方法一:求余  O(NlogN) 对于每个数字n,n有logN位
    public int countDigitOne1(int n) {
        int times=0;
        int num=0;
        for (int i = 1; i <= n; i++) {
            num=i;
            while (num!=0){
                if (num%10==1) times++;
                num/=10;
            }
        }
        return times;
    }

    @Test
    public void test(){
        System.out.println(countDigitOne(12));
    }

    /*
    方法二数学规律:主要理解每次去掉最高位进行递归,而且每次是隔一位而已
    首先求最高位1的个数:
    去掉最高位:求剩余位1的个数用排列组合
    * */

    public int countDigitOne(int n){
        if (n<=0) return 0;
        strN=Integer.toString(n).toCharArray();
        len=strN.length;
        return numberOf1(0);
    }
    public char[] strN;
    public int len;
    public int numberOf1(int curr){
        //后序遍历
        if (curr>len-1)
            return 0;
        int first=strN[curr]-'0';
        //考虑个位数
        if (curr==len-1&&first==0) return 0;
        if (curr==len-1&&first>0) return 1;
        //假设当前统计数是21345
        //数字1,0000到1,9999中1出现在第一位的次数
        int numFirstDigit=0;
        if (first>1)
            numFirstDigit= (int) Math.pow(10,len-1-curr);
        else if (first==1){
            int parLen=len-1-(curr+1)+1;//剩下的位数
            for (int i = curr+1; i <= len-1; i++) {
                numFirstDigit+=(strN[i]-'0')*Math.pow(10,parLen-1);
                parLen--;
            }
            numFirstDigit+=1;
        }
        //计算1346-21345中1除了在最高位之外出现的次数,排列组合即可
        //可以划分为两段,1346-11345(相当于0-9999)  和  11346-21345(相当于0-9999)
        int numOtherDigits= (int) (first*(len-1-curr)*Math.pow(10,len-2-curr));
        //统计1-1345中的1的个数,后序遍历,递归
        int numRecursive=numberOf1(curr+1);
        return numFirstDigit+numOtherDigits+numRecursive;
    }
}

_04_数字序列中某一位的数字

数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。

请写一个函数,求任意第n位对应的数字。

示例 1:

输入:n = 3
输出:3
示例 2:

输入:n = 11
输出:0

限制:

0 <= n < 2^31

package LeetCode;

import org.junit.Test;

public class _04_数字序列中某一位的数字 {
    @Test
    public void test(){
        int n=findNthDigit(1000000000);
        System.out.println((long) (9*Math.pow(10,9)));
        System.out.println((long) (9*Math.pow(10,10)));//900 0000 0000

        System.out.println((int) (9*Math.pow(10,9)));
        System.out.println((int) (9*Math.pow(10,10)));//2147483647
        long a=9000000000L;
        System.out.println((long) 3 *a );
    }

    public int findNthDigit(int n) {
        if (n<0) return -1;
        int digits=1;//位数
        while (true){
            //统计m位数有几个
            long numsDig=countDig(digits);

            //结束条件:知道要找的那个数在m位数之中
            if (n<(long)digits*numsDig)
               return find(n,digits);

            n-=digits*numsDig;
            digits++;
        }
    }
    /*
    0-9:10个
    10-99 :90个
    100-999:900个
    1000-9999:9000个
    * */
    public long countDig(int digits){
        if (digits==1) return 10;
        else return (long) (9*Math.pow(10,digits-1));
    }
    //811=3*270+1  100后的第270位370中的7
    public int find(int n,int digits){
        int number=firstDig(digits)+n/digits;
        int toRight=digits-n%digits;//右数第xx位
        for (int i=1;i<toRight;i++)
            number/=10;
        return number%10;
    }
    /*
    m位数字的第1个数:
    0
    10
    100
    1000
    * */
    public int firstDig(int digits){
        if (digits==1) return 0;
        else return (int) Math.pow(10,digits-1);
    }

}

_05_把数组排成最小的数

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

示例 1:

输入: [10,2]
输出: “102”
示例 2:

输入: [3,30,34,5,9]
输出: “3033459”

提示:

0 < nums.length <= 100

package LeetCode;

import com.sun.deploy.util.StringUtils;
import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;

public class _05_把数组排成最小的数 {
    @Test
    public void test(){
        String[] res={"43","12","2"};
        System.out.println();
    }

    /*
    解题思路很简单:就是定义一个新的比大小规则,然后进行升序就好了 复杂度是O(NlogN)
    证明则比较复杂
    * */

    public String minNumber(int[] nums) {
        if (nums==null||nums.length==0) return null;
        String[] res=new String[nums.length];
        int i=0;
        for (int num : nums) {
            res[i++]=Integer.toString(num);
        }
        Arrays.sort(res,new Comparator<String>(){
            @Override
            public int compare(String o1, String o2) {
                String s1=o1.concat(o2);
                String s2=o2.concat(o1);
                return s1.compareTo(s2);
            }
        });
        StringBuilder builder=new StringBuilder();
        for (String re : res) {
            builder.append(re);
        }
        return builder.toString();
    }
}

_06_把数字翻译成字符串

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

示例 1:

输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”

提示:

0 <= num < 231

package LeetCode;

import org.junit.Test;

import java.lang.annotation.Target;

public class _06_把数字翻译成字符串 {
    /*
    每次都有两个选择,单翻或双翻:f(i)=表示从第i位数字开始不同的翻译数
    f(i)=f(i+1)+g(i,i+1)*f(i+2) i,i+1数字在10-25时g(i,i+1)为1,否则为0
    自顶向下的递归存在大量重复子问题;
    所以采用自底向向的解决方式,从末尾开始翻译
    * */

    public int translateNum(int num) {
        if (num<0) return 0;
        char[] chars = Integer.toString(num).toCharArray();
        int len=chars.length;
        int[] f=new int[len+1];
        f[len-1]=1;
        f[len]=1;//f[len-2]=f[len-1]+1 如果有
        for (int i= len-2; i>=0;i--){
            int digit1=chars[i]-'0';
            int digit2=chars[i+1]-'0';
            int sum=digit1*10+digit2;
            if (sum>=10&&sum<=25)
                f[i]+=f[i+1]+f[i+2];
            else
                f[i]+=f[i+1];
        }
        return f[0];
    }
    @Test
    public void test(){
        System.out.println(translateNum(25));
    }

}

_07_礼物的最大值

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

示例 1:

输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

提示:

0 < grid.length <= 200
0 < grid[0].length <= 200

package LeetCode;

public class _07_礼物的最大值 {
    /*
    dp[i][j]表示第i行第j列的最大值,那么它可以由上,或者左得来
    状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i][j-1])+grid[i][j]
    * */
    public int maxValue01(int[][] grid) {
        if (grid==null||grid.length==0||grid[0].length==0) return -1;
        int m=grid.length;
        int n=grid[0].length;
        int[][] dp=new int[m+1][n+1];
        //多增加第0行第0列,作为初始化
        for (int i=1;i<=m;i++){
            for (int j=1;j<=n;j++){
                dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])+grid[i-1][j-1];
            }
        }
        return dp[m][n];
    }
    /*
    优化:dp[i][j]只依赖(i-1,j)和(i,j-1)两个格子,即只依赖当前行和上一行而已,前面的数据没用
    当前行的dp[j]=max(当前行的dp[j-1],上一行的dp[j])+grid[i][j]
    * */

    public int maxValue(int[][] grid) {
        if (grid==null||grid.length==0||grid[0].length==0) return -1;
        int m=grid.length;
        int n=grid[0].length;
        int[] dp=new int[n+1];
        //多增加第0行第0列,作为初始化
        for (int i=1;i<=m;i++){
            for (int j=1;j<=n;j++){
                dp[j]=Math.max(dp[j],dp[j-1])+grid[i-1][j-1];
            }
        }
        return dp[n];
    }


}

_08_最长不含重复字符的子字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

示例 1:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

提示:

s.length <= 40000

package LeetCode;

import org.junit.Test;

import java.util.Arrays;

public class _08_最长不含重复字符的子字符串 {
    /*
      f(i)表示以第i的字符结尾不包含重复的最大长度
      f(i)=:
      之前没出现过s[i]:  f(i-1)+1
      之前出现过,计算和上次出现的距离d: d<=f(i-1) f(i)=d;
                                   d>f(i-1) f(i)=f(i-1)+1
    * */
    public int lengthOfLongestSubstring(String s) {
        if (s==null) return -1;
        int[] position=new int[128];//可表示字符一共128个
        Arrays.fill(position,-1);
        char[] chars=s.toCharArray();
        int currLength=0;
        int maxLength=0;
        int index=0;
        int preIndex=0;
        int count=0;
        for (int i = 0; i < chars.length; i++) {
            index = chars[i]- ' ';//空格是ASCII最小的
            preIndex = position[index];//该字符上次出现在字符串中的下标
            //之前没出现过或者d>f(i-1)
            if (preIndex==-1||i-preIndex>currLength){
                currLength++;
                maxLength=Math.max(currLength,maxLength);
            }
            else currLength=i-preIndex;
            position[index]=i;
        }
        return Math.max(currLength,maxLength);
    }
    @Test
    public void test(){
        System.out.println('\0');
    }
}

_09_丑数

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

示例:

输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:

1 是丑数。
n 不超过1690。

package LeetCode;

public class _09_丑数 {

    /*
    判断一个数是否为丑数:丑数只能被2,3,5整除
    如果能被2整除,就连续除2
    如果能被3整除,就连续除3
    如果能被5整除,就连续除5
    最后结果是1则为丑数

    优化:只判断丑数,而不在非丑数上浪费时间
    根据定义每个丑数应该是另一个丑数乘以2,3,5的结果

    每个丑数分别成2,3,5取第一个大于M的丑数,在取这三个丑数的最小值

    优化:丑数不用每次全部都乘2,3,5,及时更新2,3,5第一个大的丑数
    * */

    public int nthUglyNumber(int n) {
        if (n<=0) return 0;
        int[] ugly=new int[n];
        ugly[0]=1;
        int min=0;
        int multiIndex2=0;
        int multiIndex3=0;
        int multiIndex5=0;
        for (int i=1;i<n;i++){
            min=Math.min(Math.min(2*ugly[multiIndex2],3*ugly[multiIndex3]),5*ugly[multiIndex5]);
            ugly[i]=min;
            if (2*ugly[multiIndex2]==ugly[i]) multiIndex2++;
            if (3*ugly[multiIndex3]==ugly[i]) multiIndex3++;//3*ugly[multiIndex3]>ugly[i]在上面已经说了
            if (5*ugly[multiIndex5]==ugly[i]) multiIndex5++;
        }
        return ugly[n-1];
    }
}

_10_第一个只出现一次的字符

在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例:

s = “abaccdeff”
返回 “b”

s = “”
返回 " "

限制:

0 <= s 的长度 <= 50000

package LeetCode;

public class _10_第一个只出现一次的字符 {
    /*
    字符是长度为8的数据类型,因此可以创建256大小的数组,即1kb大小空间
    第一次扫描并记录
    第二次扫描,break输出
    * */

    public char firstUniqChar(String s) {
        if (s==null) return '\0';//字符串结束标志位
        char[] chars = s.toCharArray();
        int[] hashFlag=new int[256];
        for (char aChar : chars) {
            hashFlag[aChar]++;
        }
        char ch = ' ';//输入s为空的时候输出空格
        for (char aChar : chars) {
            if (hashFlag[aChar]==1){
                ch=aChar;
                break;
            }
        }
        return ch;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值