js leetcode算法

两数之和

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
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值