leetcode一起攻克二分查找

模板学习

首先来一个模板的文章,我的思路都是来源于这篇文章,建议仔细阅读该文章十分好用的二分查找法模板
我来总结下这篇文章的核心。

模板代码

以leetcode35题的代码来讲解。
在这里插入图片描述

 public int searchInsert(int[] nums, int target) {
        int l = 0;
        int r = nums.length;
        while(l<r)
        {
          //寻找大于等于它的最小值
        	int mid = (l+r)>>>1;
            if(nums[mid]<target)
                l = mid+1;
            else{
                r = mid;
            }
        }
        return l;
    }

核心思路

这套模板有三个好处
第一个好处就是while(l<r),我们每次结束循环时的值都是l。
第二个好处就是我们的分值没有判断等号,只有两个分支,分支的区分标准为:是否可以去除掉中位数(mid)
第三个好处是中位数mid的选取,普通的选取有:1.left+right,2.left+(right-left)/2,等等。第一种方法可能会超出范围,第二种方法当left为负数时,也可能会超过范围。所以我们使用如下方法,

//左中位数
int mid = (l+r)>>>1;
//右中位数
int mid = (l+r+1)>>>1

这套模板还有三个注意

  1. left 与 right的选取
    简单来说,left与right是我们能选到的范围。一般left会取0,right会取length-1。但什么时候我们right会取length呢,以本题为例,当【1,3,5,7】,且target=9时,应该返回4,也就是让该值插入到7的后面,所以right应该选择length
  2. mid的选取
    这个的选取与我们不去除中位数的分支有关。如下代码就会陷入死循环(【1,3】,target = 5)。就应该让mid去选取右中位数,具体该选取左中位数还是右中位数,我们应该选取用例只有两个的时候来判断,是否会陷入死循环。
			int mid = (l+r)>>>1;
            if(nums[mid]>target)
                r = mid-1;
            else{
                l = mid;
            }
  1. 去除中位数的分支与不去除中位数的分支
    我们尽量去找到能去除中位数的分支,这就是具体去分析我们的问题。
    比如说本题,我们需要选取的是大于等于target的最小值,所以如果nums【mid】比target小,肯定就不会取,直接去除该值就好了。即使反向思维也可以,比如当前mid的值比target大,我们不能去掉mid值,因为可能mid就是我们想要的那个值,所以不可以去掉mid。
  2. 是否要比较
    是否要比较是我们选择出来的是left是否需要跟题中的一些进行比较,判断是否是我们要的值之类的,这个需要根据具体的题来看,无论那一道题目都可以按照这四步去做。

推荐顺序

611.有效三角形的个数

题目链接

  //三角形成立的条件是:两边之和大于第三边,两边之差小于第三边
    //可以转换为:排序的数组,A[i]+A[i+1]>A[i+2]
     //尝试双指针
    // 固定最大的边A[i]
    // A[l]+A[r]>A[i],那么A[l+1]+A[r],A[l+2]+A[r],...A[r-1]+A[r]也会大于
    //一共有r-1-l+1个为r-l组
    // 此时r左移
    //如果A[l]+A[r]<=A[i],l右移
class Solution {
    public int triangleNumber(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        int res = 0;
        //i>=2是要留i=0,1两条边
        for (int i = n-1;i >= 2;i--)
        {
            int l = 0;
            int r = i-1;
            while(l<r)
            {
                if(nums[l]+nums[r]>nums[i])
                {
                  res += r-l;
                    r--;
                }else{
                    l++;
                }
            }
        }
        return res;
    }
}

35. 搜索插入位置

题目链接

class Solution {
    //target>nums最大值时,left会在nums.length-1,所以r为nums.length也可以,只要判断一下nums[l]是否小于target,小于就返回l+1
    public int searchInsert(int[] nums, int target) {
        int l = 0;
        int r = nums.length;
        while(l<r)
        {
          //寻找大于等于它的最小值
        int mid = (l+r)>>>1;
            if(nums[mid]<target)
                l = mid+1;
            else{
                r = mid;
            }
        }
        return l;
    }
}

704.二分查找

题目链接

class Solution {
    public int search(int[] nums, int target) {
        //使用万能模板
        //目标值不再nums范围内,应该再做一次判断
        int left = 0 ;
        int right = nums.length-1;
        while(left<right){
            int mid = (left+right+1) >>> 1;
            if(nums[mid]>target)
            {
                right = mid-1;
            }else{
                left = mid;
            }
        }
        if(nums[left]!=target)
            return -1;
        else{
            return left;
        }
    }
}

69. x 的平方根

题目链接

class Solution {
    public int mySqrt(int x) {
        //继续使用万能模板
        //判断去掉中位数的分支
        //如果mid*mid<x,不可以去掉当前mid,举例来说,8的平方根是2,而2*2=4,就小于8,但不能去掉当前的mid值(2)
        //所以如果mid*mid>x,就可以去掉当前mid,以8举例,假如mid=3,3*3=9,肯定不是平方根,可以去掉当前的mid值(3)
        //所以这样在不去掉中位数的分支就会是left=mid,这样当只剩下两个数字的时候,当mid为左边界时,且会进入不去掉中位数得分支时,这样left是不会变的,就会进入死循环,所以我们取得mid要用右中位数,让她只有两个数字的时候中位数取右边。
        //这样左边界就取到右边得数,此时left=right,就会跳出循环了。
        
        //要用long
        long left = 0;
        long right = x;
        while(left<right){
            long mid = (left+right+1) >>>1 ;
            if(mid*mid >x)
            {
                right = mid-1;
            }
            else{
                left = mid ;
            }
        }
        return (int)left;
    }
}

852.山脉数组的峰顶索引

题目链接

class Solution {
    public int peakIndexInMountainArray(int[] A) {
        //使用模板
        //本题的选到条件就是找到一个i值,满足A[i]>A[i-1],A[i]>A[i+1],不需要去考虑其他值的原因是
        //山脉只有一个峰顶,举例比如从0-(i-1)中存在一个i=2,也满足A[2]>A[1],且A[2]>A[3],此时2就应该是峰顶,但是存在这个i会让A[i]>A[i-1],因为从2一直到lenth-1都应该是降序,所以不满足条件。这样就没用山脉的峰顶了。因此只要找到一个值满足A[i]>A[i-1],A[i]>A[i+1],该i就是峰顶。那么就可以转换成二分查找去找i值。
        int left = 1;
        int right = A.length-2;
        while(left<right)
        {
            //本题依旧没有明确说出在那种情况需要去掉中位数,所以两种模板都可以。
            //这里选用左中位数,即:不去掉中位数的分支为right = mid;
            int mid = (left+right)>>>1;
             //此时处在上升阶段(去掉中位数)
            if(A[mid]>A[mid-1]&&A[mid]<A[mid+1])
            {
             left = mid+1;  
            }
            //此时处在下降或找到值
            else{
                right = mid;
            }
        }
        return left;
    }
}

222. 完全二叉树的节点个数

题目链接

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int res = 0;
    public int countNodes(TreeNode root) {
        //深度优先查找
        preorder(root);
        return res;
    }
    public void preorder(TreeNode root)
    {
        if(root==null)
            return;
        res++;
        preorder(root.left);
        preorder(root.right);
    }
}

153. 寻找旋转排序数组中的最小值

题目链接

class Solution {
    public int findMin(int[] nums) {
       //使用万能模板
        //其实就看中位数是在两个升序排序数组中的哪一个,继而去寻找最大值,最大值的后一个就是我们的
        //最小值,只是需要注意选择排除中位数的分支判断条件与中位数选择,以及要判断是否没有进行旋转
        if(nums.length==1)
            return nums[0];
        int left = 0 ;
        int right = nums.length-1;
          //如果没有旋转的话
        //例如12345
        if (nums[right] > nums[0]) 
            return nums[0];
        while(left<right)
        {
            int mid = (left+right+1)>>>1;
            //起初以为这个分支使用哪个(nums[mid]>nums[left]或者nums[mid]<nums[left])都可以
            //最初我用的是nums[mid]>nums[left]left=mid+1,举例为:23451,现在在第一个升序排序,
            //所以将left右移一位去寻找最大值。
            //不过当34512时,我们的确处于第一个升序排序,但此时已经在这个升序排序中的最后一位了,会排除最大值
            //反之,假如mid的值比左边的值小,说明mid在第二个升序数组,例如51234,这样,无论怎么样都可以让right左移,而且不怕中位数就是最大值
            if(nums[mid]<nums[left])
            {
                right = mid -1;
            }
            else{
                left = mid;
            }
        }
        return nums[left+1];
    }
}

154. 寻找旋转排序数组中的最小值 II

题目链接

class Solution {
   public int findMin(int[] nums) {
       //值得注意的就是如果有重复的值,就不要再去找最大值了,去直接找最小值吧。
        int left = 0, right = nums.length - 1;
       //只有一个数或者是未旋转
       if(nums.length==1||nums[0]<nums[right])
           return nums[0];
        while (left < right) {
            int mid = (left + right)>>>1;
            //mid 一定在第 1 个排序数组中
            //34512,所以left右移,最小值一定在右面(可以排除mid)
            if (nums[mid] > nums[right]) left = mid + 1;
            //mid 一定在第 2 个排序数组中
            //45123,最小值在左面(包括mid)
            else if (nums[mid] < nums[right]) right = mid;
            //此时应该缩小寻找范围。
            //[1, 1, 0, 1]此情况,right-1不会丢失最小值
            //[1, 0, 1, 1, 1]。此情况right-1也不会丢失最小值
            //[1, 1, 1, 1],假设nums[right]为最小值,则说明最小值为多个,假如都相等,肯定会找到最小值
            //假如为[1,1,0,0,0],也会在left到right-1中找到最小值,因为如果nums[mid]==nums[right],且他是最小值的话,nums数组从mid-right都为最小值,因为其中只要有一个比他大的就都不满足原始数组为升序排序数组,同时从最小值往后排列的话,必须是升序。
            else right = right - 1;
        }
        return nums[left];
    }
}

33. 搜索旋转排序数组

题目链接

class Solution {
      //先找最小值,之后根据nums[0](第一个升序数组的最小值)与target比较
        //然后判断target在第一个升序还是在第二个升序数组。
    public int search(int[] nums, int target) {
        if(nums==null||nums.length==0)
            return -1;
        //只有一个数
       if(nums.length==1)
           return nums[0]==target?0:-1;
       int minIndex = searchMin(nums);
        int left = 0;
        int right = nums.length-1;
        //这里判断时第一个升序还是第二个升序数组
        //[4,5,6,7,0,1,2],target=1(在第二个升序数组)
        if(nums[0]>target)
        {
            left = minIndex;
        }
        //在第一个升序数组,
        //[4,5,6,7,0,1,2],target=5
        else{
            //所以需要更改right
            //但有可能minIndex=0(也就是没有旋转),没有旋转right不变
            //如果旋转了就把right赋值到最小值的前一个位置(7的位置)
            if (minIndex!=0)
                right = minIndex-1;
        }
        while(left<right)
        {
            int mid = (left+right+1)>>>1;
            if(nums[mid]>target){
                right = mid-1;
            }
            else{
                left = mid;
            }
        }
        if(nums[left]==target)
            return left;
        else
            return -1;
        
    }
    /**
    * 返回最小值的下标(使用找最大值,最大值后一个值是最小值)(与153题类似)
    */
    public int  searchMin(int []nums)
    {
         //没有进行旋转
        if(nums[0]<nums[nums.length-1])
            return 0;
         int left = 0;
        int right = nums.length-1;
        while(left<right)
        {
            int mid = (left+right+1)>>>1;
            if(nums[mid]<nums[left])
            {
                right = mid-1;
            }else{
                left = mid;
            }
        }
        return left+1;
    }
}

34. 在排序数组中查找元素的第一个和最后一个位置

题目链接

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if(nums==null||nums.length==0)
            return new int[]{-1,-1};
        int left = 0;
        int right =nums.length-1;
        while(left<right)
        {
            int mid = (left+right+1)>>>1;
            if(nums[mid]>target)
            {
                right = mid -1;
                
            }else{
                left = mid;
            }
        }
        if(nums[left]!=target)
        {
            return new int[]{-1,-1};
        }
        //往左右找
        else{
            int start = left;
            int end = right;
            while(start-1>=0&&nums[start-1]==target)
            {
                start -- ;
            }
            while(end+1<nums.length&&nums[end+1]==target)
            {
                end++;
            }
            return new int[]{start,end};
        }
    }
}

374. 猜数字大小

题目链接

/* The guess API is defined in the parent class GuessGame.
   @param num, your guess
   @return -1 if my number is lower, 1 if my number is higher, otherwise return 0
      int guess(int num); */

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        // 二分
        int left = 0;
        int right = n;
        while(left<right)
        {
            int mid = (left+right)>>>1;
            if(guess(mid)==1)
            {
                left = mid+1;
            }
            else{
                right = mid;
            }
            
        }
        return left;
    }
}

744. 寻找比目标字母大的最小字母

题目链接

class Solution {
    public char nextGreatestLetter(char[] letters, char target) {
        int left = 0 ;
        int right = letters.length-1;
        while(left<right)
        {
            int mid = (left+right)>>>1;
            if(letters[mid]>target){
                right = mid;
            }
            else{
                left = mid+1;
            }
        }
        if(letters[left]>target)
            return letters[left];
        else
            return letters[0];
    }
}

441. 排列硬币

441. 排列硬币

class Solution {
    public int arrangeCoins(int n) {
        //高斯公式(1+2+3+4+...=n*(n+1)/2)
        if(n==0)
            return n;
        //有long型的
        long left = 1;
        long right = n;
        while(left<right)
        {
            long mid = (left+right+1)>>>1;
            if(((1+mid)*mid)>(long)2*n)
            {
                right = mid-1;
            }
            else{
                left = mid;
            }
        }
        return (int)left;
    }
}

475. 供暖器

475. 供暖器

class Solution {
    int max = 0; 
    public int findRadius(int[] houses, int[] heaters) {
        //思路:找到距离每个房子最近的供暖器(这个过程可以使用二分),记录他们的距离,最后
        //选取最大的这个距离即为结果。
        
        //竟然没排序
        Arrays.sort(houses);
        Arrays.sort(heaters);
        for(int i = 0;i<houses.length;i++)
        {
            find(houses[i],heaters);
        }
        return max;
    }
    public void find(int index,int[]heaters)
    {
        //可以相当于使用排序,意思就是index如果在heaters存在,就找到那个index
        //如果找不到那个index,就把它放在正常的排序位置去,然后和左右比较差值当作结果与max比较。
        int left = 0;
        //可以排到right,如果比heaters最大的还大的话
        int right = heaters.length;
        while(left<right)
        {
            //
            int mid  = (left+right)>>>1;
            //小于他,这个位置肯定不放index值
            if(heaters[mid]<index)
            {
                left = mid+1;
            }else{
                right = mid;
            }
        }
        //防止这个值比heaters中的值都大
        if(left==heaters.length)
        {
            max = max>index-heaters[heaters.length-1]?max:index-heaters[heaters.length-1];
            return ;
        }
        if(heaters[left] == index){
            max = max>0?max:0;
        }
        else {
            int dist;
            if(left==0)
                dist = heaters[left] - index;
            else
                dist = Math.min(index-heaters[left-1],heaters[left]-index);
            max = dist>max?dist:max;
        }
    }
}

1011. 在 D 天内送达包裹的能力

1011. 在 D 天内送达包裹的能力

class Solution {
    public int shipWithinDays(int[] weights, int D) {
        int left = 0;
        //right 最大等于weights的和也就可以了。肯定是可以在D天内完成。
        int right = 0;
        for(int weight:weights)
            right += weight;
        //寻找最低运载量的过程使用二分算法来查找。
        while(left<right)
        {
            int  mid = (left+right)>>>1;
            if(!Ok(weights,D,mid))
            {
                left = mid+1;
            }else{
                right = mid;
            }
        }
        return left;
    }
    /**
    * 判断当前的运载能力是否可以在D天内运载完毕
    **/
    public boolean Ok(int[]weights,int D,int capa)
    {
    
        if(capa<weights[weights.length-1])
            return false;
        int nowCapa = capa;
      for(int i=0;i<weights.length;i++)
      {
        //不能比最大容量大
        if(capa<weights[i])return false;
        if(weights[i]>nowCapa)
        {
            nowCapa = capa;
            D--;
        }
        nowCapa -= weights[i];  
      }
    return D>0?true:false;
    }
}

875. 爱吃香蕉的珂珂

875. 爱吃香蕉的珂珂
相当于从1-pilesMax中寻找可以在H小时内吃掉所有香蕉的最小速度,这个过程使用二分查找

class Solution {
    public int minEatingSpeed(int[] piles, int H) {
        int pilesMax = Integer.MIN_VALUE;
        for(int pile:piles)
            pilesMax = pilesMax>pile?pilesMax:pile;
        if(piles.length==H)
            return pilesMax;
        int left = 1;
        int right = pilesMax;
        while(left<right)
        {
            int mid = (left+right)>>>1;
            if(!canEat(piles,H,mid))
            {
                left = mid+1;
            }
            else{
                right = mid;
            }
        }
        return left;
    }
    public boolean canEat(int[]piles,int H,int speed)
    {
        int hours = 0;
        for(int pile:piles)
        {
            if(pile<=speed)
                hours++;
            else
                hours += pile/speed+(pile%speed!=0?1:0);
        }
        return hours<=H?true:false;
    }
}

436. 寻找右区间

436. 寻找右区间

import java.awt.Point;
class Solution {
    public int[] findRightInterval(int[][] intervals) {
        Map<Point,Integer>map = new HashMap<>();
        for(int i=0;i<intervals.length;i++)
        {
            Point point = new Point(intervals[i][0],intervals[i][1]);
            map.put(point,i);
        }
        int[][] copyIntervals = Arrays.copyOf(intervals,intervals.length);
        //排序没问题
        Arrays.sort(intervals,(a,b)->{
            return a[0]-b[0];} );
        int[]results = new int[intervals.length];
        for(int i=0;i<copyIntervals.length;i++)
        {
            int index = find(intervals,copyIntervals[i][1]);
            int result = -1;
            if(index!=-1)
            {
                Point p = new Point(intervals[index][0],intervals[index][1]);
                result = map.get(p);
            }
            results[i] = result;
        }
        return results;
    }
    public int  find(int[][]intervals,int target)
    {
        int l = 0;
        int r = intervals.length-1;
        while(l<r)
        {
            int mid = (l+r)>>>1;
            if(intervals[mid][0]<target)
            {
                l = mid+1;
            }else{
                r = mid;
            }
            
        }        
        if(intervals[l][0]>=target)
            return l;
        else
            return -1;
            
    }
}

658. 找到 K 个最接近的元素

658. 找到 K 个最接近的元素

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        //双指针,指着左和右,与x比较,每次可以根据与x得差值排除相差较大的值,直到只剩下k个值
        int left = 0;
        int right = arr.length-1;
        while(right-left+1>k)
        {
            if(Math.abs(arr[left]-x)>Math.abs(arr[right]-x))
            {
                left++;
            }
            else{
                right--;
            }
        }
        List<Integer>list = new ArrayList<>();
        for(int i=left;i<=right;i++)
        {
            list.add(arr[i]);
        }
        return list;
    }
}

300. 最长上升子序列

300. 最长上升子序列

class Solution {
    public int lengthOfLIS(int[] nums) {
       //换一个思路,使用动态规划,动态规划dp[i]为以nums[i]为结尾的最长上升子序列的长度。
        int []dp =new int[nums.length];
        //都赋值为1,因为只有自己一个长度也是1
        Arrays.fill(dp,1);
        for(int i = 0;i<nums.length;i++)
        {
            //一个一个往前看,去比较
            for(int j=0;j<i;j++)
            {
                //可以以nums[i]为结尾
                if(nums[i]>nums[j])
                {
                    dp[i] = Math.max(dp[j]+1,dp[i]);
                }
            }
        }
        int max = 0;
        for(int num:dp)
        {
            max = Math.max(num,max);
        }
        return max;
    }
}

1160. 拼写单词

1160. 拼写单词

class Solution {
    public int countCharacters(String[] words, String chars) {
        int[]lib = new int[26];
        for(int i=0;i<chars.length();i++)
        {
            lib[chars.charAt(i)-'a']++;
        }
        int nums = 0;
        for(String word:words)
        {
            boolean flag = true;
            int[]wordLib = new int[26]; 
            for(int i=0;i<word.length();i++)
            {
                wordLib[word.charAt(i)-'a']++;
            }
            for(int i=0;i<26;i++)
            {
                if(wordLib[i]>lib[i]){
                    flag=false;
                    break;
                }
            }
            if(flag)
                 nums+=word.length();
        }
        return nums;
    }
}

528. 按权重随机选择

528. 按权重随机选择

class Solution {
    //不知道为啥使用list去存不可以。。。。。
    int[]sum;
    Random r ;
    public Solution(int[] w) {
        r = new Random();
        sum = new int[w.length];
        sum[0] = w[0];
        for(int i=1;i<w.length;i++)
        {
            sum[i] += sum[i-1]+w[i]; 
        }
    }
    
    public int pickIndex() {
        //1~sum[sum.length-1]
        int index = r.nextInt(sum[sum.length-1])+1;
        int left = 0;
        int right = sum.length-1;
        //二分去找选取的数是哪个
        //理论上是去找sum数组中大于等于index值中的最小值
        //举例sum为【1,4,9,16】,也就是说random为1时,返回0,(1,4]返回1,(4,9]返回2,(9,16】返回3。
        while(left<right)
        {
            int mid = (left+right)>>>1;
            if(sum[mid]<index)
            {
               left  = mid +1;
            }
            //大于index可能是我们要找的,所以不能去掉中位数mid。
            else{
                right = mid;
            }
        }
        return left;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(w);
 * int param_1 = obj.pickIndex();
 */

275. H指数 II

275. H指数 II

class Solution {
    public int hIndex(int[] citations) {
        //更改下策略,我们返回选取的区间的长度
        //我们选取的区间要满足区间中的数大于等于所在区间的长度。
        //最大的都为0
        if(citations==null||citations.length==0||citations[citations.length-1]==0)
            return 0;
        int left = 0;
        int right = citations.length-1;
        while(left<right)
        {
            int mid = (left+right)>>>1;
            //比长度小,就得去掉该值
            if(citations[mid]<(citations.length-mid))
                left = mid+1;
            //比长度大是满足的,我们应该继续让mid往左走去尝试看有没有更小的mid值,可以满足mid对应的值
            //大于等于从[mid,length-1]的长度。
            else{
                right = mid;
            }
        }
        //返回选取区间的长度(这个区间里的数都大于等于区间的长度。)
        return citations.length-left;
    }
}

497. 非重叠矩形中的随机点

497. 非重叠矩形中的随机点

class Solution {
    //首先创建一个数组,该数组包含不同矩形所包含的点的个数
    //首先随机数判断位于哪个数组,之后再在数组中随机一个值即可。
    int[]nums;
    int[][]rects;
    private Random r;
    public Solution(int[][] rects) {
        r=new Random();
        this.rects = rects;
        nums = new int[rects.length+1];
        for(int i=1;i<=rects.length;i++)
        {
            int[]rect = rects[i-1];
            int x = rect[2]-rect[0]+1;
            int y = rect[3]-rect[1]+1;
            //面积阿
            nums[i] = nums[i-1]+x*y;
            //不是周长阿。
           // nums[i] = nums[i-1]+2*x+2*y;
          
        }
    }
    
    public int[] pick() {
        //1~nums[nums.length-1]
        int first  = 1+r.nextInt(nums[nums.length-1]);
        //i的范围,first<=nums[i],都为i。
        int left = 1;
        int right = nums.length-1;
        while(left<right)
        {
            //去找比first大的最小值
            int mid = (left+right)>>>1;
            if(nums[mid]<first)
            {
                left = mid+1;
            }else{
                right = mid;
            }
        }
        int []rect = rects[left-1];
        //需要区分一下大小。因为油负数
        //生成 “min <= 随机数 <= max ” 的随机数
        //int num = min + (int)(Math.random() * (max-min+1));
        int xmin = Math.min(rect[2],rect[0]);
        int xmax = Math.max(rect[2],rect[0]);
        int ymin = Math.min(rect[3],rect[1]);
        int ymax = Math.max(rect[3],rect[1]);
        int chosex = r.nextInt(xmax-xmin+1)+xmin;
        int chosey = r.nextInt(ymax-ymin+1)+ymin;
        int []result = new int[]{chosex,chosey};
       return result;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(rects);
 * int[] param_1 = obj.pick();
 */

50. Pow(x, n)

50. Pow(x, n)

class Solution {
    public double myPow(double x, int n) {
        //不需要有记忆功能的。因为只会计算一次。
        //之前计算过的
        //我之前的想法是return jisuan(x,n/2)*jisuan(x,n/2),超时后又打算使用记忆性的,但是使用记忆性的
        //也需要去访问数组(执行jisuan函数),也是耗时间的
        if(n>0)
            return jisuan(x,n);
        else
            return (1.0/jisuan(x,-n));
    }
    public double jisuan(double x,long n){
     //long 是专门为2^31准备,这个if(n==0)也是为2^31和0准备的。
        if(n==0)
            return 1.0;
       
        if(n==1)
        {
            return x;
            }
        double half = jisuan(x,n/2);
        if(n%2==0)
        {       
            return half*half;
        }
        else
        {
            return half*half*x;
         }
    }
}

911.在线选举

[911.在线选举](https://leetcode.com/problems/online-election/)

class TopVotedCandidate {
    //这个是可以通过的代码,
    //优化了下拥有了winners数组,不用每次都判断了,节省了时间。
    //这里采用的是相对复杂的代码,我们使用personTicket数组来存储票数
    //personTicket[i][j]代表,第i个person在times[j]时刻拥有的票数
    //这里比较耗时的操作在于对应每一个time,都要将所有的personTicket中每一个person的数值变化累加。
    //可以更改的是时刻去寻找当前最大的person,然后存储,使用Map来存储person,tickets。
    //每一次只需判断当前time,投票的那个人他得票数加上这一票是否能超过当前的最大票,超过,就把此时的获胜person
    //变成他即可。
    //本题还需要注意的是,在平局的情况下,最近获得投票的候选人将获胜。
    int[]persons;
    int[]times;
    int[][]personTicket;
    Set<Integer>set;
    int size;
    int []winners;
    public TopVotedCandidate(int[] persons, int[] times) {
        winners = new int[times.length];
        set = new HashSet<>();
        for(int person:persons)
            set.add(person);
        size = set.size();
        personTicket = new int[size][persons.length];
        this.times = times;
        this.persons = persons;
        for(int i=0;i<times.length;i++)
        {
           if(i>0){
               for(int k=0;k<personTicket.length;k++)
                    personTicket[k][i]=personTicket[k][i-1];
           }
            personTicket[persons[i]][i]++;
        }
        for(int i=0;i<winners.length;i++)
        {
            winners[i] = findWinner(i);
        }
    }
    public int findWinner(int timesIndex){
        int moment = timesIndex;
        int max = -1;
        int person = 0;
        for(int i = 0;i<personTicket.length;i++)
        {
            if(max<personTicket[i][moment])
            {
             max =  personTicket[i][moment];
                person = i;
            }else if(max == personTicket[i][moment])
            {
                 for(int k = moment;k>=0;k--)
                 {
                     if(persons[k]==i)
                     {
                         person = i;
                         break;
                     }
                     else if(persons[k]==person){
                         break;
                     }
                 }
            }
        }
        return person;
    }
    public int q(int t) {
        int left = 0;
        //设置一个超过的
        int right = times.length-1;
        //找到小于当前值的最大值
        while(left<right)
        {
            int mid = (left+right+1)>>>1;
            if(times[mid]>t)
            {
                right = mid -1;
                
            }
            else
                left = mid;
        }
        int moment = left;
        return winners[moment];
    }
}

/**
 * Your TopVotedCandidate object will be instantiated and called as such:
 * TopVotedCandidate obj = new TopVotedCandidate(persons, times);
 * int param_1 = obj.q(t);
 */

981.基于时间的键值存储

981.基于时间的键值存储

class TimeMap {
    //其实是可以通过的代码
    Map<String,List<Point>>map;
    /** Initialize your data structure here. */
    public TimeMap() {
        map = new HashMap<>();
    }
    
    public void set(String key, String value, int timestamp) {
        List<Point>list;
        if(!map.containsKey(key))
        {
            list = new ArrayList<>();
        }else{
            list = map.get(key);
        }
        list.add(new Point(timestamp,value));
        map.put(key,list);
    }
    
    public String get(String key, int timestamp) {
        if (!map.containsKey(key)) return "";
        List<Point>list = map.get(key);
        int left = 0;
        int right = list.size()-1;
        while(left<right)
        {
            //找到(小于等于)timestamp的最大值
            int mid = (left+right+1)>>>1;
            if(list.get(mid).x>timestamp)
            {
                right = mid-1;
            }else{
                left = mid;
            }
        }
        if(list.get(left).x>timestamp)
            return "";
        else{
            return list.get(left).value;
        }
    }
}
class Point{
    int x;
    String value;
    public Point(int x,String value)
    {
        this.x=x;
        this.value = value;
    }
}
/**
 * Your TimeMap object will be instantiated and called as such:
 * TimeMap obj = new TimeMap();
 * obj.set(key,value,timestamp);
 * String param_2 = obj.get(key,timestamp);
 */

702. 搜索长度未知的有序数组

702. 搜索长度未知的有序数组

class Solution {
    public int search(ArrayReader reader, int target) {
        int left = 0;
        int right = 10000;
        while(left<right)
        {
            int mid = (left+right+1)>>>1;
            //可能越界
            if(reader.get(mid)>target)
            {
                right = mid-1;    
            }else{
                left = mid;
            }            
        }
        return reader.get(left)==target?left:-1;
    }
}

1060. 有序数组中的缺失元素

1060. 有序数组中的缺失元素

class Solution {
    public int missingElement(int[] nums, int k) {
        //代表从nums[i]到nums[0]之间有多少个缺失值。
        int[]miss = new int[nums.length];
        for(int i=0;i<miss.length;i++)
        {
            if(i==0)
                miss[i]=0;
            else{
                miss[i] = miss[i-1]+nums[i]-nums[i-1]-1;
            }
        }
        int left = 0;
        int right = miss.length-1;
        //找到最小的大于等于k的miss[i]
        while(left<right)
        {
            int mid = (left+right)>>>1;
            if(miss[mid]<k)
            {
                left = mid+1;
            }else{
                right = mid;
            }   
        }
        //找到left之后要判断,可能left是最后一个值,如果该值拥有的缺失数据还小于k,
        //就说明缺失值在数组后面的值中。
        //否则找到的值是可以的,那么他处于nums[left-1],nums[left]之间,
        //所以该值为nums[left-1]+(k-miss[left-1])
        if(miss[left]<k)
            return nums[nums.length-1]+k-miss[left];
            else{
               //return left-1;
             return nums[left-1]+k-miss[left-1];
            }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值