文章目录
- 模板学习
- 推荐顺序
- 611.有效三角形的个数
- 35. 搜索插入位置
- 704.二分查找
- 69. x 的平方根
- 852.山脉数组的峰顶索引
- 222. 完全二叉树的节点个数
- 153. 寻找旋转排序数组中的最小值
- 154. 寻找旋转排序数组中的最小值 II
- 33. 搜索旋转排序数组
- 34. 在排序数组中查找元素的第一个和最后一个位置
- 374. 猜数字大小
- 744. 寻找比目标字母大的最小字母
- 441. 排列硬币
- 475. 供暖器
- 1011. 在 D 天内送达包裹的能力
- 875. 爱吃香蕉的珂珂
- 436. 寻找右区间
- 658. 找到 K 个最接近的元素
- 300. 最长上升子序列
- 1160. 拼写单词
- 528. 按权重随机选择
- 275. H指数 II
- 497. 非重叠矩形中的随机点
- 50. Pow(x, n)
- 911.在线选举
- 981.基于时间的键值存储
- 702. 搜索长度未知的有序数组
- 1060. 有序数组中的缺失元素
模板学习
首先来一个模板的文章,我的思路都是来源于这篇文章,建议仔细阅读该文章十分好用的二分查找法模板
我来总结下这篇文章的核心。
模板代码
以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
这套模板还有三个注意
- left 与 right的选取
简单来说,left与right是我们能选到的范围。一般left会取0,right会取length-1。但什么时候我们right会取length呢,以本题为例,当【1,3,5,7】,且target=9时,应该返回4,也就是让该值插入到7的后面,所以right应该选择length - mid的选取
这个的选取与我们不去除中位数的分支有关。如下代码就会陷入死循环(【1,3】,target = 5)。就应该让mid去选取右中位数,具体该选取左中位数还是右中位数,我们应该选取用例只有两个的时候来判断,是否会陷入死循环。
int mid = (l+r)>>>1;
if(nums[mid]>target)
r = mid-1;
else{
l = mid;
}
- 去除中位数的分支与不去除中位数的分支
我们尽量去找到能去除中位数的分支,这就是具体去分析我们的问题。
比如说本题,我们需要选取的是大于等于target的最小值,所以如果nums【mid】比target小,肯定就不会取,直接去除该值就好了。即使反向思维也可以,比如当前mid的值比target大,我们不能去掉mid值,因为可能mid就是我们想要的那个值,所以不可以去掉mid。 - 是否要比较
是否要比较是我们选择出来的是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. 排列硬币
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. 供暖器
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 天内送达包裹的能力
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. 寻找右区间
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 个最接近的元素
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. 最长上升子序列
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. 拼写单词
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. 按权重随机选择
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
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. 非重叠矩形中的随机点
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)
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.基于时间的键值存储
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. 搜索长度未知的有序数组
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. 有序数组中的缺失元素
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];
}
}
}