LeetCode题解 javascript

经典LeetCode算法题解析
本文深入解析LeetCode上的经典算法题目,涵盖链表操作、二叉树处理、字符串匹配、数学运算、数组搜索及旋转、游戏策略分析、整数处理、峰值查找、最长子串求解等内容,提供高效算法思路及代码实现。
删除链表的倒数第n个节点

思路1:建立两个指针p1,p2 其中p1 先走n步,p2再开始走,等p1到末尾了,p2.next就是要删除的节点 ,注意特殊位置的考虑,删除的是头结点
思路2:用个数组,对链表每个节点值重新声明为节点对象,再存入数组,删除对应元素后,一个一个串起来

var removeNthFromEnd = function(head, n) {
  var p1 = head;
  // p1先走n步
  while (n > 0) {
    p1 = p1.next;
    n--;
  }
  // 删除的是头结点
  if (!p1) {
    return head.next;
  }
  var p2 = head;
  while (p1.next) {
    p1 = p1.next;
    p2 = p2.next;
  }
  p2.next = p2.next.next;
  return head;
};
不用乘除符,取模符,做除法运算

思路:通过减法计算,每减一次,结果加一,但是显然,当被除数很大时,会运行超时,所以采用位运算加减法来实现,每次除数左移一位,可以将问题规模变成原来的一半。

var divide = function(dividend, divisor) {
    var d1=Math.abs(dividend);
    var d2=Math.abs(divisor);
    var res=0;
    if(dividend==0){
        return 0;
    }
    var i=0;
    var res=0;
    if(d2==1){
        if((dividend>0&&divisor<0)||(dividend<0&&divisor>0)){
            return -d1;
        }
        else{
            if(d1>2147483647)
            {
                return 2147483647;
            }
            else{
                return d1;
            }
        }
    }
    //关键算法
    while(d1>=d2){
        var temp=d2;
        var cnt=1;
        while(temp<d1>>1){
            temp=temp<<1;
            cnt=cnt<<1;
        }
        d1=d1-temp;
        res+=cnt;
    }
    if((dividend>0&&divisor<0)||(dividend<0&&divisor>0)){
        return -res;
    }
    else{
        return res;
    }
};
搜索旋转数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。

var search = function(nums, target) {
    var left=0;
    var right=nums.length-1;
    while(left<=right){
   		//注意javascript的除法不是整除,需要自行向下取整
        var mid=Math.floor((left+right)/2);
        if(nums[mid]==target){
            return mid;
        }
        // mid 在左边递增数组中
        if(nums[mid]>=nums[left]){
            if(nums[left]==target){
                return left;
            }
            else if(nums[mid]>target&&nums[left]<target){
                right=mid-1;
            }
            else{
                left=mid+1;
            }
        }
        // mid在右边递增数组中
        else{
            if(nums[right]==target){
                return right;
            }
            else if(nums[mid]<target&&nums[right]>target){
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }
    }
    return -1;
};

旋转链表

如 1->2->3->4->5 k=2;
旋转后的链表为 4->5->1->2->3,注意k可以大于链表的长度

var rotateRight = function(head, k) {
    var p=head;
    var count=0;
    //计算链表长度
    while(p){
        count++;
        p=p.next;
    }
    if(count==0){
        return null;
    }
    if(k>=count){
        k=k%count;
    }
    if(k==0){
        return head;
    }
    var phead=new ListNode(0);
    var qhead=phead;
    var num=count-k;
    var i=1;
    var q=head;
    //获取旋转后的链表头结点q.next
    while(i<num){
        q=q.next;
        i++;
    }
    phead.next=q.next;
    q.next=null;
    while(phead.next){
        phead=phead.next;
    }
    //把原来的尾节点串上原来的头结点
    phead.next=head;
    return qhead.next;
};

二叉树最大深度
var maxDepth = function(root) {
    if(!root){
        return 0;
    }
    return 1+Math.max(maxDepth(root.left), maxDepth(root.right));
};

110 高度平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]
在这里插入图片描述

返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]
在这里插入图片描述

返回 false 。

var depth=function(root){
    if(!root){
        return 0;
    }
    return 1+Math.max(depth(root.left),depth(root.right));
}
var isBalanced = function(root) {
    if(!root){
        return true;
    }
    var temp1=depth(root.left);
    var temp2=depth(root.right);
    return Math.abs(temp1-temp2)<2&&isBalanced(root.left)&&isBalanced(root.right);
};

101 对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
在这里插入图片描述
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
在这里插入图片描述

var judge = function(t1, t2) {
  if (t1 == null && t2 == null) {
    return true;
  }
  if (t1 != null && t2 != null) {
    return (
      t1.val == t2.val && judge(t1.right, t2.left) && judge(t1.left, t2.right)
    );
  }
  return false;
};
var isSymmetric = function(root) {
  if (!root) {
    return true;
  }
  return judge(root.left, root.right);
};
226翻转二叉树

翻转一棵二叉树。
在这里插入图片描述
在这里插入图片描述

var invertTree = function(root) {
  if (!root) {
    return null;
  }
  var nodeleft = invertTree(root.left);
  var noderight = invertTree(root.right);
  root.left = noderight;
  root.right = nodeleft;
  return root;
};
654最大二叉树

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

二叉树的根是数组中的最大元素。
左子树是通过数组中最大值左边部分构造出的最大二叉树。
右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。
Example 1:

输入: [3,2,1,6,0,5]
输入: 返回下面这棵树的根节点:
在这里插入图片描述

var constructMaximumBinaryTree = function(nums) {
  if (nums.length == 0) {
    return null;
  }
  var max = nums[0];
  var maxpos = 0;
  for (var i = 0; i < nums.length; i++) {
    if (nums[i] > max) {
      max = nums[i];
      maxpos = i;
    }
  }
  var root = new TreeNode(max);
  root.left = constructMaximumBinaryTree(nums.slice(0, maxpos));
  root.right = constructMaximumBinaryTree(nums.slice(maxpos + 1));
  return root;
};
617 合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:
在这里插入图片描述输出:
合并后的树:
在这里插入图片描述

var mergeTrees = function(t1, t2) {
    if(!t1&&!t2){
        return null;
    }
    if(t1&&!t2){
        return t1;
    }
    if(!t1&&t2){
        return t2;
    }
    var tempVal=t1.val+t2.val;
    var newNode=new TreeNode(tempVal);
    newNode.left=mergeTrees(t1.left,t2.left);
    newNode.right=mergeTrees(t1.right,t2.right);
    return newNode;
};

55、跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。
示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。

示例 2:


输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃步数是 0 , 
所以你永远不可能到达最后一个位置。

思路,从后往前考虑,数组长度为 len,将len-2视为起点,判断其是否有希望到达终点len-1处,若有,仅需将len-2作为终点,再重复上面的思想即可,如果len-2处不可能有机会到len-1,也就是此时的目的地,那就继续考虑前一个元素 len-3处是否有希望跳转到目的地,若有,则将len-3作为下一阶段的目的地
超简短的代码:

var canJump = function(nums) {
    var len=nums.length;
    // 开始必须至少走一步
    var step=1;
    if(len===1){
        return true;
    }
    for(var i=len-2;i>=0;i--){
        // 当前位置可以跳到目的地,只需考虑是否能从之前的位置跳转到当前位置 将此时的i-1 视为循环起点,i视为终点,只需要1步即可达到,所以step归一
        if(nums[i]>=step){
            step=1;
        }
        // 当前位置跳转不到目的地,继续考虑i-1位置是否有可能,此时意味着需要跳的步数需要加 1
        else{
            step++;
        }
    }
    return step==1;
};
7 整数反转

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例 1:

输入: 123
输出: 321

示例 2:

输入: -123
输出: -321

示例 3:

输入: 120
输出: 21

注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

var reverse = function(x) {
    if(x==0){
        return 0;
    }
    //数字变字符串
    var num=x.toString();
    var res=[];
    for(var i=num.length-1;i>0;i--){
        res.push(num[i])
    }
    if (x > 0){
        res.push(num[0])
    }
    else{
        res.unshift('-');
    }
    //数组变字符串
    var ans="";
    for(var i=0;i<res.length;i++){
        ans+=res[i];
    }
    //字符串变数字
    var sol=ans-'0';
    //超出范围考虑
    if((sol+Math.pow(2,31)<0)||(sol-Math.pow(2,31)>=0)){
        return 0;
    }
    return sol;
};
162 寻找峰值

峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞。

示例 1:

输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,

示例 2:

输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5 
解释: 你的函数可以返回索引 1,其峰值元素为 2;
     或者返回索引 5,

说明:

你的解法应该是 O(logN) 时间复杂度的。

这题注意是 中等难度的,做之前感觉有点不合理,以为应该是 easy,做之后,发现确实应该是 medium ,看博客基本都说二分,感觉都没说到重点?为什么能用二分?
首先注意复杂度要求,会想到二分,然后又想到数组是无序的,不适合用二分,但是除了二分也不可能满足该复杂度要求了。所以确定用二分。
然后,这题考的是数学。
两端是最低点,所以这题一定有解,而且题目要求任意解即可,因此可以用二分。
这题只要理解了这一点,就能做出来核心思想:取mid值,只要nums[mid-1] 大于nums[mid] ,则mid左边一定有峰值 反之,mid-1右边一定存在峰值,因为两端无穷小,明白不了的画张图理解一下
然后,看代码注释

var findPeakElement = function(nums) {
    if(nums.length==1){
        return 0;
    }
    var l=0;
    var r=nums.length-1;
    //这里需要注意一下 js的 / 号不是整除
    var mid=Math.floor((l+r)/2);
    while(l<=r){
      //最后的结果必然是 l,r相邻,此时取两者之间大的一定是峰值
        if(mid==l){
            return nums[l]>nums[r]?l:r;
        }
        else if(nums[mid-1]>nums[mid]){
            r=mid;
        }
        else{
            l=mid;
        }
        mid=Math.floor((l+r)/2);
    }
};


leetcode3 无重复字符的最长子串

示例 1:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

注意。子串是连续的,子序列是不需要连续的

var lengthOfLongestSubstring = function(s) {
    if(s==''){
        return 0;
    }
    var len = s.length;
    var max=1;
    for(var i=0;i<len-max;i++){
        var temp=[];
        temp.push(s[i]);
        var maxTemp=1;
        for(var j=i+1;j<len;j++){
            if(temp.includes(s[j])){
                break;
            }
            else{
                temp.push(s[j]);
                maxTemp++;
            }
        }
        if(maxTemp>max){
            max=maxTemp;
        }
    }
    return max;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值