LeetCode热题100JS(69/100)第十三天|34|33|153|4|20
34. 在排序数组中查找元素的第一个和最后一个位置
题目链接:34. 在排序数组中查找元素的第一个和最后一个位置
难度:中等
刷题状态:2刷
新知识:
解题过程
思考
示例 1:
输入:nums = [5,7,7,8,8,10]
, target = 8
输出:[3,4]
题解分析
参考题解链接:【视频讲解】二分查找总是写不对?三种写法,一个视频讲透!(Python/Java/C++/C/Go/JS)
放下1刷过程
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange = function(nums, target) {
let start=startPos(nums, target)
let end=startPos(nums, target+1)-1
// console.log('start',start)
// console.log('end',end)
if(nums[start]!=target){
return [-1,-1]
}
return [start,end]
};
function startPos(nums, target){
const n=nums.length
let left=0
let right=n-1
while(left<=right){
const mid=Math.floor((left+right)/2)//2/4
if(nums[mid]>=target){
right=mid-1
}else{
left=mid+1
}
}
return left
}
手搓答案(无非废话版)
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange=function(nums,target){
let n=nums.length,start,end
function startPos(tar){
let left=0,right=n-1
while(left<=right){
let mid=((right-left)>>1)+left
if(nums[mid]<tar){
left=mid+1
}else{
right=mid-1
}
}
return left
}
start=startPos(target)
end=startPos(target+1)-1
if(nums[start]!=target) return [-1,-1]
return [start,end]
}
总结
注意while(left<=right)这里要判断=的情况也要循环一次
33. 搜索旋转排序数组
题目链接:33. 搜索旋转排序数组
难度:中等
刷题状态:2刷
新知识:
解题过程
思考
示例 1:
输入:nums = [4,5,6,7,0,1,2]
, target = 0
输出:4
题解分析
参考题解链接:搜索旋转排序数组
放下1刷过程
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
//[4,5,6,7,0,1,2] 5
//[5,6,7,0,1,2,4] 5
const n=nums.length
let min=Math.min(...nums)
let max=Math.max(...nums)
let left=0
let right=n-1
let res=-1
if(nums[left]==target){
return 0
}
if(nums[right]==target){
return n-1
}
if(n==1){
return nums[0]==target?0:-1
}
while(left+1<right){
let mid=((right-left)>>1)+left
// console.log('mid',mid)
//nums[0] target nums[mid] max 左边,right=mid
//nums[0] nums[mid] target max 左边,left=mid
//min target nums[mid] nums[n-1] 右边,right=mid
//min nums[mid] target nums[n-1] 右边,left=mid
//nums[0] target max min nums[mid] nums[n-1] 两边,right=mid
//nums[0] nums[mid] max min target nums[n-1] 两边,left=mid
let targetLr=(nums[0]<=target)&&(target<=nums[mid])&&(nums[0]<=max)
let targetLl=(nums[0]<=nums[mid])&&(nums[mid]<=target)&&(target<=max)
let targetRr=(min<=target)&&(target<=nums[mid])&&(nums[mid]<=nums[n-1])
let targetRl=(min<=nums[mid])&&(nums[mid]<=target)&&(target<=nums[n-1])
let targetTr=(nums[0]<=target)&&(target<=max)&&(min<=nums[mid])&&(nums[mid]<=nums[n-1])&&(nums[0]!=min)&&(nums[n-1]!=max)
let targetTl=(nums[0]<=nums[mid])&&(nums[mid]<=max)&&(min<=target)&&(target<=nums[n-1])&&(nums[0]!=min)&&(nums[n-1]!=max)
// console.log(targetLr,targetLl,targetRr,targetRl,targetTr,targetTl)
if(nums[mid]==target){
res=mid
break
}else if(targetLr||targetRr||targetTr){
right=mid
}else if(targetLl||targetRl||targetTl){
left=mid
}else{
break
}
// console.log('left',left)
// console.log('right',right)
}
return res
};
手搓答案(无非废话版)
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search=function(nums,target){
let n=nums.length
if(n==0) return -1
if(n==1) return nums[0]==target?0:-1
let left=0,right=n-1
while(left<=right){
//console.log(left,right)
let mid=((right-left)>>1)+left
if(nums[mid]==target) return mid
if(nums[0]<=nums[mid]){
if(nums[0]<=target&&target<=nums[mid]){
right=mid-1
}else{
left=mid+1
}
}else{
if(nums[mid]<=target&&target<=nums[n-1]){
left=mid+1
}else{
right=mid-1
}
}
}
return -1
}
总结
应该用手搓答案里面的,保证时间复杂度为 O(log n)
153. 寻找旋转排序数组中的最小值
题目链接:153. 寻找旋转排序数组中的最小值
难度:中等
刷题状态:2刷
新知识:
解题过程
思考
示例 1:
输入:nums = [3,4,5,1,2] 输出:1 解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
题解分析
参考题解链接:和最后一个数比大小,简洁二分(Python/Java/C++/C/Go/JS/Rust)
放下1刷过程
/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
//[3,4,5,1,2]
//[4,5,1,2,3]
const n=nums.length
let left=-1
let right=n-1//4
while(left+1<right){
let mid=((right-left)>>1)+left//2
if(nums[mid]>nums[n-1]){
left=mid
}else{
right=mid
}
// console.log('nums[left]',nums[left])
// console.log('nums[right]',nums[right])
}
return nums[right]
// return Math.min(...nums)
};
手搓答案(无非废话版)
/**
* @param {number[]} nums
* @return {number}
*/
var findMin=function(nums){
let n=nums.length,res=nums[0]
let left=0,right=n-1
while(left<=right){
let mid=((right-left)>>1)+left
if(nums[mid]>nums[n-1]){
left=mid+1
}else{
right=mid-1
}
}
return nums[left]
}
总结
拿下
4. 寻找两个正序数组的中位数
题目链接:4. 寻找两个正序数组的中位数
难度:困难
刷题状态:1刷
新知识:
解题过程
思考
示例 1:
输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:合并数组 = [1,2,3] ,中位数 2
想不出来,直接看答案
题解分析
参考题解链接:详细通俗的思路分析,多解法
详细分析如下
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function(nums1, nums2) {
let m=nums1.length,n=nums2.length,A=nums1,B=nums2
if(m>n) return findMedianSortedArrays(B,A)
//初始化二分查找的边界
let imin=0,imax=m
while(imin<=imax){
//计算当前的分割点i j
//i 是在数组 A 中的分割点索引。它表示在数组 A 中,有 i 个元素位于左半部分,剩下的 m - i 个元素位于右半部分(其中 m 是数组 A 的长度)。j同理
let i=Math.floor((imin+imax)/2)
// i + j = m - i + n - j + 1也就是 j = ( m + n + 1) / 2 - i
let j=Math.floor((m+n+1)/2)-i
//检查分割是否有效,如果B[j-1]>A[i],说明要增大i
if(j&&i!=m&&B[j-1]>A[i]){imin=i+1}
//如果A[i-1]>B[j],说明要减小i
else if(i&&j!=n&&A[i-1]>B[j]){imax=i-1}
//分割有效,处理边界条件
else{
let macLeft=0
//左半部分最大值在B[j-1]
if(i==0){maxLeft=B[j-1]}
//左半部分最大值在A[i-1]
else if(j==0){maxLeft=A[i-1]}
//否则,左半部分的最大值是A[i-1],B[j-1]的较大值
else{maxLeft=Math.max(A[i-1],B[j-1])}
//奇数,返回maxLeft
if((m+n)%2==1) return maxLeft
//偶数
let minRight=0
//右半部分最小值在B[j]
if(i==m) {minRight=B[j]}
//右半部分最小值在A[i]
else if(j==n) {minRight=A[i]}
//否则,右半部分最大值是A[i],B[j]的较小值
else{minRight=Math.min(A[i],B[j])}
return (maxLeft+minRight)/2.0
}
}
};
手搓答案(无非废话版)
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays=function(nums1,nums2){
let m=nums1.length,n=nums2.length,A=nums1,B=nums2
if(m>n) return findMedianSortedArrays(nums2,nums1)
let imin=0,imax=m
while(imin<=imax){
let i=Math.floor((imin+imax)/2)
let j=Math.floor((m+n+1)/2)-i
if(j&&i!=m&&B[j-1]>A[i]) {imin=i+1}
else if(i&&j!=n&&A[i-1]>B[j]) {imax=i-1}
else{
let maxLeft=0
if(i==0){maxLeft=B[j-1]}
else if(j==0){maxLeft=A[i-1]}
else{maxLeft=Math.max(A[i-1],B[j-1])}
if((m+n)%2==1) return maxLeft
let minRight=0
if(i==m) {minRight=B[j]}
else if(j==n) {minRight=A[i]}
else{minRight=Math.min(A[i],B[j])}
return (maxLeft+minRight)/2.0
}
}
}
总结
不好理解,其实有更简单的方法,但这种方法是字节笔试考过的
20. 有效的括号
题目链接:20. 有效的括号
难度:简单
刷题状态:1刷
新知识:
解题过程
思考
示例 1:
输入:s = "()"
输出:true
用数组模拟栈
括号必须以正确的顺序闭合:这意味着括号不能交叉匹配。例如,([)]
是无效的
题解分析
参考题解链接:有效的括号
详细分析如下
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
let n=s.length,arr=[]
if(n%2==1) return false
for(let i=0;i<n;i++){
if(s[i]=='(') arr.push(')')
else if(s[i]=='{') arr.push('}')
else if(s[i]=='[') arr.push(']')
//核心思想
//用数组来模拟栈的表达方式
else if(arr.length==0||arr[arr.length-1]!=s[i]){return false}
else{
arr.pop()
}
}
//如果arr没pop完,说明不符合
return arr.length==0
};
手搓答案(无非废话版)
/**
* @param {string} s
* @return {boolean}
*/
var isValid=function(s){
let n=s.length,arr=[]
for(let i=0;i<n;i++){
let c=s[i]
if(c=='(') {arr.push(')')}
else if(c=='{') {arr.push('}')}
else if(c=='[') {arr.push(']')}
else if(arr.length==0||arr[arr.length-1]!=c){return false}
else{ arr.pop()}
}
return arr.length==0
}
总结
拿下