2021-6-2剑指笔记00

Posted 轻舟一曲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.以上是关于2021-6-2剑指笔记00的主要内容,如果未能解决你的问题,请参考以下文章

[剑指Offer][数组]数字在升序数组中出现的次数

快速排序

计算一个整数二进制表示中1的个数(剑指)

剑指offer(C++)-JZ53:数字在升序数组中出现的次数(算法-搜索算法)

剑指offer(C++)-JZ53:数字在升序数组中出现的次数(算法-搜索算法)

剑指offer(C++)-JZ53:数字在升序数组中出现的次数(算法-搜索算法)