两数之和
1.map
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
//使用map 一次遍历
var twoSum = function(nums, target) {
var map=new Map()
for(var i=0;i<nums.length;i++){
if(map.has(target-nums[i])){
return [map.get(target-nums[i]),i]
}
map.set(nums[i],i)
}
};
遍历的过程中,将值和下标存储在map中,每次遍历都去map中查找是否有(target-当前遍历的值),如果有,返回下标数组,如果没有则存放进map中
!!map本身就是用来存储的,存储的形式是键值对的形式
2.双指针法
//双指针法
if(!Array.isArray(nums)||nums.length==0) return []
var valueAndIndex =nums.map((item,index)=>{
return {value:item,
index}
}).sort((numsOne,numTwo)=>numsOne.value-numTwo.value) //sort-- 前-后 升序 ,返回数组
var left=0
var right=valueAndIndex.length-1
while(left<right){
var sum=valueAndIndex[left].value+valueAndIndex[right].value
if(sum>target){
right--
}else if(sum<target){
left++
}else{
return [valueAndIndex[left].index,valueAndIndex[right].index]
}
}
return []
有效的括号
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
if(s.length%2!=0) return false
var map=new Map([
[')','('],
["}","{"],
["]","["]
])
var stack=[]
for(let ch of s){
if(map.has(ch)){
if(stack[stack.length-1]!==map.get(ch))
return false
else {stack.pop()}
}else{
stack.push(ch)
}
}
return !stack.length
};
1.当s长度为奇数时,直接返回false
2.定义map 右括号为键,左括号为值,定义数组(栈)
3.for of 遍历字符串 并判断是否map中有此键值对(判断是否为右括号),不为右括号则push进去
为右括号则判断栈顶元素是否和map中的相应值匹配(左括号进行匹配),如果匹配则pop(消去)
最后,返回!stack.length
stack可能为[ (((( ],这种情况还是需要判断的,不能直接返回true
合并两个有序链表
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/
var mergeTwoLists = function(list1, list2) {
const prehead=new ListNode(-1) //定义一个头指针
let pre=prehead
while(list1!=null&&list2!=null){
if(list1.val<list2.val){
pre.next=list1
list1=list1.next
}else{
pre.next=list2
list2=list2.next
}
pre=pre.next //不要忘记每次list1或者list2指向下一个后,pre也要指向下一个
}
pre.next=list1==null?list2:list1
return prehead.next
};
每一个节点都要往后移动!!
定义虚拟节点!!
最后返回虚拟节点的next
二叉树的中序遍历
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var inorderTraversal = function(root) {
//递归
// const res=[]
// const traversal=(root)=>{
// if(!root) return //当root为空时 直接返回
// traversal(root.left) //左遍历
// res.push(root.val)
// traversal(root.right)
// }
// traversal(root)
// return res
//迭代
// const res=[]
// const stk=[]
// while(root||stk.length){ //当root有值(未遍历结束)或者 栈不为空时
// while(root){ //当root不为kong
// stk.push(root)
// root=root.left //不停的向左节点遍历
// }
// root=stk.pop() //如果根节点为空 则弹出
// res.push(root.val)
// root=root.right //如果右节点也为空,则继续弹出
// }
// return res
};
链接: 题解
//morris
const res=[]
var predecessor=null
while(root){
if(root.left){
predecessor=root.left //先向左一步
while(predecessor.right&&predecessor.right!=root){ //然后不断向右,但是不能==root
predecessor=predecessor.right
}
if(!predecessor.right){ //当predecessor.right不存在时
predecessor.right=root //前驱节点指向root
root=root.left
}else{
res.push(root.val) //如果右子树存在 则push
predecessor.right=null //切断联系
root=root.right //并指向右子树
}
}
else{
res.push(root.val) //当没有左子树时,push
root=root.right //root.right有可能是predecessor.right建立的
}
}
return res
对称二叉树
var isSymmetric = function(root) {
//递归
//递归三部曲 1.确定参数和返回值 2.确定终止条件 3.确定每层递归的逻辑
// if(root==null) return true
// function compareNode(left,right){
// if(left==null&&right!=null||left!=null&&right==null) return false
// else if(left==null&&right==null) return true
// else if(left.val!=right.val) return false
// return compareNode(left.left,right.right)&&compareNode(left.right,right.left)
// }
// return compareNode(root.left,root.right)
//使用队列
const queue=[] //队列使用数组
if(root==null) return false
queue.push(root.left) //入队使用数组方法 push
queue.push(root.right)
while(queue.length){
var leftNode=queue.shift() //出队使用数组方法 shift 并返回相应节点
var rightNode=queue.shift()
if(leftNode==null&&rightNode==null){ //当节点都是null时,下面的都不执行
continue //跳出本次循环 否则null.null会报错
}
if(leftNode==null||rightNode==null||leftNode.val!=rightNode.val){ //此时只能是某一个为null
return false
}
queue.push(leftNode.left)
queue.push(rightNode.right)
queue.push(leftNode.right)
queue.push(rightNode.left)
}
return true
};
二叉树的最大深度
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var maxDepth = function(root) {
//深度优先所有 dfs
// if(!root){
// return 0 //当root为空时 返回0 (下面的return 会加1)
// }else{
// let leftHeight=maxDepth(root.left)
// let rightHeight=maxDepth(root.right)
// return Math.max(leftHeight,rightHeight)+1 //别忘记 +1 !!!!
// }
//广度优先搜索 bfs bfs模板!!!!
if(!root) return 0;
var queue=[root]
var depth=1
while(queue.length){ //当队列中还有值的时候
const size=queue.length //获取长度 注意这里使用const保存当前queue的长度
for(let i=0;i<size;i++){ //此处使用size保证循环次数不变 如果使用queue.lenght 则循环体内会改变length导致错误
var cur=queue.shift() //移除当前项,如果当前项有左右子树,则加入队列
if(cur.left) queue.push(cur.left)
if(cur.right) queue.push(cur.right)
}
if(queue.length) depth++ //因为加入了队列 所以深度+1
}
return depth
};
只出现一次的数字
var singleNumber = function(nums) {
//异或
// var res=0
// nums.forEach(item=>{
// res^=item //异或具有结合律和分配律 [4,1,2,2,1] 会再减下去
// })
// return res
//set
var set=new Set()
nums.forEach(item=>{
if(set.has(item)){
set.delete(item)
}else{
set.add(item)
}
})
return [...set] //set展开,并返回数组
};
环形列表
var hasCycle = function(head) {
//快慢指针
if(head==null||head.next==null) return false //如果头节点为空或者只有头节点则一定没有循环
var slow=head
var fast=head.next //快指针要快一步,否则while循环不会执行
while(slow!=fast){
if(fast==null || fast.next==null){
return false
}
slow=slow.next
fast=fast.next.next
}
return true
};
相交链表
var getIntersectionNode = function(headA, headB) {
//集合
// const aSet=new Set() //遍历A创建集合
// while(headA!=null){
// aSet.add(headA)
// headA=headA.next
// }
// while(headB!=null){
// if(aSet.has(headB)){ //遍历b,判断b中节点是否在a中存在,若存在则返回当前节点(不是数值相等,而是指针相等)
// return headB
// }
// headB=headB.next
// }
// return null
//双指针
if(headA==null || headB==null) return null //因为a和b长度不一样,所以双指针不能齐头并进
var pA=headA,pB=headB
while(pA!=pB){
pA=pA==null?headB:pA.next
pB=pB==null?headA:pB.next
}
return pB
};
多数元素
var majorityElement = function(nums) {
// let target=0; //投票法(互相抵消---同归于尽)
// let count=0
// for(let i=0;i<nums.length;i++){
// if(count==0){ //==0时,target==当前项,count++
// target=nums[i]
// count++
// }else if(target==nums[i]){ //如果接下来还是和target相等的值(一队的),那就再加
// count++
// }else{
// count-- //如果不是一队的,那就抵消掉一个
// }
// }
// return target //最后剩下的就是多数元素
//排序
// nums.sort((a,b)=>a-b) //排序默认使用字符串码进行排序,会不准确,所以需要加入比较函数
// return nums[Math.floor(nums.length/2)] //3/2==1.5 需要使用math.floor
};
反转链表
var reverseList = function(head) {
//双指针 迭代
// var pre=null
// var cur=head
// while(cur){
// var next=cur.next //需要使用变量暂存下一个节点
// cur.next=pre //很关键 让当前节点指向前一个节点(与后一个节点之间的联系也断开了)
// pre=cur //pre节点向后移动(指向当前节点)
// cur=next //cur节点向后移动(指向下一个节点)
// }
// return pre
};
讲解: link
//递归
if(head==null||head.next==null) return head
var newHead=reverseList(head.next) //一直压栈 参数(1 2 3 4 ),当为4时(实际上参数是4.next==5,此时head=4),5.next==null,返回newhead=5
head.next.next=head //4.next.next=4---> 5.next=4
head.next=null //4-->5 4--X-->5
return newHead //一直返回 5
反转二叉树
var invertTree = function(root) {
//递归--先压栈,从叶子节点进行反转---自下而上
//边界条件
// if(!root){
// return null
// }
// var left=invertTree(root.left) //递归函数传参
// var right=invertTree(root.right)
// root.left=right //递归内部逻辑 交换左右子树
// root.right=left
// return root
//递归第二种 自上而下
if(root==null) return null
var tmp=root.right //先访问root,再访问左右节点
root.right=root.left
root.left=tmp
invertTree(root.left)
invertTree(root.right)
return root
};
回文链表
//反转列表---迭代
var reverseList=function(head){
var pre=null
var cur=head
while(cur!=null){ //终止条件是cur==null
var tmp=cur.next
cur.next=pre
pre=cur
cur=tmp
}
return pre
}
//找中点--快慢指针
var findHalf=function(head){
var slow=head
var fast=head
while(fast.next!=null && fast.next.next!=null){ //中间是 &&
slow=slow.next
fast=fast.next.next
}
return slow //返回前半部分的尾指针
}
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function(head) {
//复制到数组中后使用双指针判断
// var valArr=[]
// while(head){
// valArr.push(head.val)
// head=head.next
// }
// for(let i=0,j=valArr.length-1;i<j;i++,j--){
// if(valArr[i]!=valArr[j]) return false
// }
// return true
if(head==null) return true
var half=findHalf(head)
var p2=reverseList(half.next) //从前半部分尾指针的下一个开始反转
var p1=head
var result=true
while(result&&p2!=null){
if(p1.val!=p2.val) result=false
p1=p1.next
p2=p2.next
}
half.next=reverseList(p2) //恢复反转的链表
return result
};
移动零
var moveZeroes = function(nums) {
//双指针
var left=0,right=0
for(let i=0;i<nums.length;i++){
if(nums[right]!=0){ //当右指针遇到不为零的数就交换,同时左侧指针加1,准备和下一个数交换
[nums[left],nums[right]]=[nums[right],nums[left]]
left++
}
right++ //nums[right]等于或者不等于0时右指针都要加一
}
return nums
};
比特位计数
var countBits = function(n) {
let bits=new Array(n+1).fill(0)
for(let i=0;i<=n;i++){
bits[i]=oneBit(i)
}
return bits
};
var oneBit=function(x){
let bitOne=0
while(x>0){
x&=x-1 //3&2==10(2)=2 2&1==0 &两次,所以bitOne==2==3的二进制中1的个数
bitOne++
}
return bitOne
}
找到所有数组中消失的数字
var findDisappearedNumbers = function(nums) {
let n=nums.length
nums.forEach(item=>{
let x=(item-1)%n //记得取模
nums[x]+=n
})
let res=[]
for(let [i,num] of nums.entries()){ //数组也能使用.entries()方法
if(num<=n) //不要忘记=
res.push(i+1) //i 要 +1
}
return res
};
[ 1,1,3 ]去带入
题解: link
汉明距离
var hammingDistance = function(x, y) {
// x=x.toString(2) //toString 方法能够转换进制
// y=y.toString(2)
// let maxLen=Math.max(x.length,y.length)
// x=x.padStart(maxLen,0)
// y=y.padStart(maxLen,0)
// let count=0
// for(let i=0;i<maxLen;i++){
// if(x[i]!=y[i]) count++
// }
// return count
//右移 计数
let count=0
let s=x^y // ^异或,相同的为0,不同的为1
while(s!=0){
count+=s&1 // 1101 & 1 =1 1100&1=0 &1可以判断最后一位是否是1,如果是,则=1,不是=0
s>>=1 //s向右循环 1 位。左边补零
}
return count
};
二叉树的直径
var diameterOfBinaryTree = function(root) {
let ans=1 //ans=1 树根的高度和而路径==1
function deep(root){
if(root==null){ //当遇到叶子节点左/右子树为null时,返回0
return 0
}
let L=deep(root.left)
let R=deep(root.right)
ans=Math.max(ans,L+R+1) //找出根的左右子树的最大节点数
return Math.max(L,R)+1 //计算的是高度,但最终使用的是ans。只在递归的过程中使用
}
deep(root)
return ans-1
};
合并二叉树
var mergeTrees = function(root1, root2) {
//深度优先搜索
if(!root1) return root2 //递归的终止条件
if(!root2) return root1
var merge=new TreeNode(root1.val+root2.val) //新建一棵树,并最终返回这棵树(递归的每一次都会新建--节点)
merge.left=mergeTrees(root1.left,root2.left) //对左右子树做同样的操作
merge.right=mergeTrees(root1.right,root2.right)
return merge
};
两数相加
var head=null,tail=null
var carry=0
while(l1||l2){
var val1=l1?l1.val:0 //谁的长度不够,在谁的后面补0
var val2=l2?l2.val:0
var sum=val1+val2+carry
carry=Math.floor(sum/10) //记得使用floor 3/2=1.5!!!是有小数的
if(!head){ //只有当head 不存在时,才执行,让tail和head指向新创建的节点
tail=head=new ListNode(sum%10)
// tail=new ListNode(sum%10) 不能再创建了,因为会指向不同的节点,所以连=就可以指向同一个节点了
}else{
tail.next=new ListNode(sum%10) //tail.next指向新节点,tail需要向后移动
tail=tail.next
}
if(l1){
l1=l1.next
}
if(l2){
l2=l2.next
}
}
if(carry>0==1){
tail.next=new ListNode(1) //记得进位!! 9+9=18 最大进位==1
// tail=tail.next
}
return head //返回头节点
无重复字符的最长子串
var right=0;
var maxLen=0
var set =new Set() //set 不存咋重复的数组
for(let i=0;i<s.length;i++){ //i此时为左指针
if(i!=0){
set.delete(s.charAt(i-1)) //当左指针移动到index=1时 删除index=0的元素 所以i-1
}
while(right<s.length&&!set.has(s.charAt(right))){
//当右指针没有超过长度 且 set中不存在右指针指向的char字符,则添加到set中
set.add(s.charAt(right))
right++ //右指针加1 因为当最后一次不符合条件时推出循环时,right依旧+1
}
maxLen=Math.max(maxLen,right-i) //所以此时 abc right此时超出边界 为3 3-0=3即可
//此处也证明 right=0 right=-1 都是一样的,注意边界问题即可
}
return maxLen
关键就是双指针,左指针向右移动删除,右指针向右移动添加
链接: 题解
最长回文子串
var longestPalindrome = function(s) {
if(s.length<2) return s
var res=''
var ifPalindrom=function(left,right){
while(left>=0&&right<s.length&&s[left]==s[right]){ //左指针>=0 右指针<长度 且左右相等
left-- //由中心向两边扩散
right++
}
if((right-1)-(left+1)+1>res.length){//因为while中left--,right++,此时left,right为边界,不可取
res=s.substring(left+1,right) //subString 为[ )
}
}
for(let i=0;i<s.length;i++){ //for循环很关键,从头开始遍历字符串,将字符串每一个字符都当作中心
ifPalindrom(i,i) //回文串为奇数
ifPalindrom(i,i+1) //回文串为偶数
}
return res
};
三数之和
var threeSum = function(nums) {
var res=[]
if(nums.length<3||nums==null) return res
if(nums.length==3&&nums[0]+nums[1]+nums[2]==0) return [nums]
nums.sort((a,b)=>a-b) //注意排序!!!
for(let i=0;i<nums.length;i++){ //对nums进行for循环,每一个元素都作为最开始的元素
if(nums[i]>0) break //因为已经排好序了,所以当当前元素大于0,后面的都是大于0,所以必不可能
if(i>0&&nums[i]==nums[i-1]) continue //当nums[i]==nums[i-1],当前元素需要调过
var L=i+1,R=nums.length-1 //L=i+1,R=nums.length-1,双指针加上nums【i】
while(L<R){
var sum=nums[i]+nums[L]+nums[R]
if(sum==0){
res.push([nums[i],nums[L],nums[R]]) //把这三个元素放入
while(L<R&&nums[L]==nums[L+1]) L++ //如果L<R&&nums[L]==nums[L+1],下一个结果就是重复的
while(L<R&&nums[R]==nums[R-1]) R--
L++ //!!!别忘记l++,r-- !!!一定要分清l是加,r是减
R--
}else if(sum<0){
L++ //当sum值小,L++
}else if(sum>0){
R--
}
}
}
return res
};
电话号码的字母组合
var letterCombinations = function(digits) {
if(digits.length==0) return []
const map = { '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz' }; //首先需要建立一个数字和英文字母的映射
var res=[]
var dfs=function(curStr,i){ //定义一个函数,参数为当前的字符串,和控制digits下标的i
if(i>digits.length-1){ //只有当i>digits.length-1,才push(终止条件)
res.push(curStr)
return
}
for(char of map[digits[i]]){ //在函数中需要遍历当前digits对应的英文
dfs(curStr+char,i+1) //循环调用函数
}
}
dfs('',0) //起始条件
return res
};
删除链表的倒数第 N 个结点
var removeNthFromEnd = function(head, n) {
//建立虚拟节点
var dummy=new ListNode(0,head)
var fast=dummy,slow=dummy //让快慢指针均指向虚拟节点
while(n--) fast=fast.next //n就是倒数第几个节点 n=2 2-- 1-- 会执行
while(fast.next){ //当fast.next存在时,快慢指针均指向下一个
slow=slow.next
fast=fast.next
}
slow.next=slow.next.next //慢指针跳过删除的指针
return dummy.next
};
括号生成
var generateParenthesis = function(n) {
var res=[]
if(n==0) return []
var str=''
var backtrack=function(res,str,left,right){ //定义回溯函数 !!注意参数
if(right<left) return //做剪枝,留正确结果
if (left < 0 || right < 0) return;
if(left==0&&right==0){ //回溯的边界条件
res.push(str) //到达边界添加进去,return
return
}
str=str+"(" //做出选择
backtrack(res,str,left-1,right) //进行回溯
str = str.slice(0, -1) //回退
str=str+")" //做出选择
backtrack(res,str,left,right-1) //回溯
str = str.slice(0, -1) //回退
}
backtrack(res,str,n,n) //初始回溯
return res
};
下一个排列
var nextPermutation = function(nums) {
let i=nums.length-2
while(i>=0&&nums[i]>=nums[i+1]) i-- //从右往左循环,找到第一个小于右邻数的数 此时退出循环时i已经减一
if(i>=0){
let j=nums.length-1
while(j>=0 && nums[j]<=nums[i]) j-- //从右往左循环,找到第一个大于等于num【i】的数
[nums[i],nums[j]]=[nums[j],nums[i]]
}
let l=i+1
let r=nums.length-1
while(l<r){
[nums[l],nums[r]]=[nums[r],nums[l]]
l++
r--
}
};
搜索旋转排序数组
var search = function(nums, target) {
//二分搜索
let n=nums.length
if(n==0) return -1
if(n==1) return nums[0]==target?0:-1
let l=0,r=n-1
while(l<=r){
let mid=Math.floor((r+l)/2)
if(nums[mid]==target) return mid
if(nums[0]<=nums[mid]){
if(nums[0]<=target&&target<nums[mid]){
r=mid-1
}else{
l=mid+1
}
}else{
if(nums[mid]<target&&target<=nums[n-1]){
l=mid+1
}else{
r=mid-1
}
}
}
return -1
};
在排序数组中查找元素的第一个和最后一个位置
var binarySearch=function(nums,target,lower){
var idx=nums.length,left=0,right=nums.length-1
while(left<=right){
const mid=Math.floor((left+right)/2)
if(nums[mid]>target||(lower&&nums[mid]>=target)){
right=mid-1
idx=mid
}else{
left=mid+1
}
}
return idx
}
var searchRange = function(nums, target) {
let ans=[-1,-1]
const leftIdx=binarySearch(nums,target,true)
const rightIdx=binarySearch(nums,target,false)-1
if(leftIdx<=rightIdx&&rightIdx<nums.length&&nums[leftIdx]==target&&nums[rightIdx]==target){
ans=[leftIdx,rightIdx]
}
return ans
};