【js】leetcode-js

leetcode


1-two sum

给一个int型数组,要求找出其中两个和为特定值的数的坐标
注意点:

  • 返回的坐标一要比坐标二小

例子:
输入: numbers={2, 7, 11, 15}, target=9 输出: index1=0, index2=1
解题思路:
遍历,判断当前节点与之后每一个节点分别相加,若和为目标值,返回两个index

var twoSum = function(nums, target) {
    for(var i=0;i<nums.length;i++){
        for( var j=i+1;j<nums.length;j++){
            if(nums[i]+nums[j]==target)
                return [i,j];
        }
    }
};

2-Add Two Numbers

定义这样的一个链表,链表的每个节点都存有一个0-9的数字,把链表当成数字, 表头为低位,表尾为高位。如1->2->3表示321,现在要对两个这样的链表求和。

注意点:

  • 进位
  • 多种情况

例子:
输入: (2 ->4 ->3)+(5->6->4) 输出: 7->0->8

解题思路:
把两个链表当成是相同长度的,短的那个想象成后面都是0;并且在计算当前节点的值时要加上进位值flag。

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function(l1, l2) {
    var l3=new ListNode(0);//初始化
    var rst=l3;
    var temp=0,flag=0;//当前值,进位值
    if(l1==null&&l2==null) return rst;
    while(l1!=null||l2!=null){
        var num1=l1==null?0:l1.val;//空链表补0
        var num2=l2==null?0:l2.val;//空链表补0
        temp=num1+num2+flag;
        if(temp>9){
            flag=1;
            temp=temp%10;
        }else{
            flag=0;
        }
        l3.next=new ListNode(temp);//.next为节点,初始化一个新节点
        //处理下一位
        l3=l3.next;
        l1=l1==null?null:l1.next;//空链表补null
        l2=l2==null?null:l2.next;
    }
    if(flag==1){
        l3.next=new ListNode(1);
    }

    return rst.next;
};

3-Longest Substring Without Repeating Characters

找出一个字符串的最长字符串,要求该字符串中没有重复的字符。
注意点:

  • js字符转ASCII码.charCodeAt()
  • js数组初始化值.fill()

例子:
输入: “abcabcbb” 输出: 3
输入: “bbbbbb” 输出: 1
解题思路:
初始化数组为-1,用ASCII表示字符,遍历将字符下标(最近)记录到数组。动态规划,全局最长字符串长度len=len>=cur?len:cur,局部字符串长度为当前到开始位置距离cur=i-start,其中,若当前字符在当前字符串已出现过,也即当前字符上一次下标大于开始位置,更新开始位置为当前字符上一次下标。类似于滑动窗口。

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    //ascii码表示字符,初始赋值-1
    var arr=new Array(256);
    arr.fill(-1);
    if(s.length==0) return 0;
    if(s.length==1) return s.length;
    //len全局最长,cur局部最长
    var len=0,cur=0,start=-1;
    //s[i].charCodeAt()字符转ASCII码
    for(var i=0;i<s.length;i++){
        //若下标大于开始位置表示,该字符在此次局部范围已出现过
        if(arr[s[i].charCodeAt()]>start){
            start=arr[s[i].charCodeAt()];
        }
        arr[s[i].charCodeAt()]=i;//赋值最近下标
        cur=i-start;//局部长度
        len=len>=cur?len:cur;
    }
    return len;
};

5-Longest Palindromic Substring

找到一个字符串的最长回文子字符串,该字符串长度不超过1000,且只有唯一一个 最长回文子串。
注意点:

  • 回文字符串分奇数偶数两种情况

例子:
输入: s=“abae” 输出: aba
输入: s=“abbae” 输出: abba
解题思路:
首先,依次把每一个字符当做回文字符串的中间字符,找到以该字符为中间字符的回文串 的最大长度。其次,找回文串时分别对奇偶两种情况进行讨论,其中关键就是对边界的把握,上下标下都不能越界。最后,当得到当前字符回文串时, 下标分别是向两边多移一位的,需要补回来。

/**
 * @param {string} s
 * @return {string}
 */
 //最长回文子串开始位置以及长度
var l=0,len=0;
var longestPalindrome = function(s) {
    if(s.length<=1||s.length>1000) return s;
    //遍历处理每一个字符
    for(var i=0;i<s.length;i++){
        localPalindrom(s,i,i);//基数情况
        localPalindrom(s,i,i+1);//偶数情况
    }
    return s.substring(l,l+len)
};

function localPalindrom(s,j,k){
	//从中间向两边判断是否为回文串
    while(j>=0&&k<s.length&&s.charAt(j)==s.charAt(k)){
        k++;
        j--;
    }
    //k-j-1=(k-1)-(j+1)+1为当前最长回文串长度,j+1为当前最长回文串开始位置
    //len为全局最长回文串长度,l为全局最长回文串开始位置
    if(len<(k-j-1)){
        l=j+1;
        len=k-j-1;
    }
}

6-ZigZag Conversion

zigzag
注意点:

  • 什么是zigzag

例子:
输入: 0123456789,3 输出: 0481357926

解题思路:
定义一个长度为numRows的字符串数组存放每一行,定义flag作为判断方向,若正向则行数加,逆向则行数减,到达边界时换向并重置行数。

/**
 * @param {string} s
 * @param {number} numRows
 * @return {string}
 */
var convert = function(s, numRows) {
    if(s.length<=1||numRows<=1) return s;
    var arr=new Array(numRows);
    arr.fill('');
    var row=0,rst='',flag=true;
    for(var i=0;i<s.length;i++){
        arr[row]+=s.charAt(i);
        if(flag){
            row++;
            if(row==numRows){
                flag=false;
                row=numRows-2;
            }
        }else{
            row--;
            if(row<0){
                flag=true;
                row=1;
            }
        }
    }
    for(var j=0;j<numRows;j++){
        rst+=arr[j];
        console.log(arr[j]);
    }
    return rst;
};

8-String to Integer (atoi)

字符串转换为int整型
注意点:

  • 符号‘+’‘-’
  • 空格,.trim()
  • 32位是否溢出,INT_MAX (2147483647) , INT_MIN (-2147483648)
  • 非数字的其他字符,返回已得到整数
  • 其他,返回0
  • 单个数字字符转为整型,‘n’*1

例子:
输入: str=" +123" 输出: 123
输入: str=“-123fe2” 输出: -123
解题思路:
将所有条件和情况都考虑到

/**
 * @param {string} str
 * @return {number}
 */
var myAtoi = function(str) {
    var INT_MAX=2147483647,INT_MIN=-2147483648;//溢出边界
    var ispos=true;//符号
    var rst=0;
    str=str.trim();//去除空格
    for(var i=0;i<str.length;i++){
        var c=str.charAt(i);
        //若开始包含正负号
        if(i==0&&(c=='+'||c=='-')){
            ispos=c=='+'?true:false;
        }
        //处理数字字符
        else if(c>='0'&&c<='9'){
            //判断溢出
            if((rst+c)*1>INT_MAX){
                return ispos?INT_MAX:INT_MIN;//返回边界值
            }
            else{
                rst=(rst+c)*1;
            }
        }
        //若为非数字字符,直接返回结果
        else{
            return ispos?rst:-rst;
        }
    }
    return ispos?rst:-rst;
};

11-Container With Most Water

给定一组长短不一的隔板,挑其中的两块板,使得板子之间能装最多的水。
注意点:

  • 两块板之间能装多少水是由短的那块板决定的

例子:
输入: height=[1,1,1] 输出: 2
解题思路:
i,j可装水min(h[i],h[j])*(j-i)
若装水最多时,h[i]>h[<i],h[j]>h[>j],即两侧不会有比当前高的板子;
从两边往中间靠拢,较低的一边先往中间靠拢。

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    var l=0,r=height.length-1;
    var res=0,local=0;
    while(r>l){
        local=(r-l)*Math.min(height[r],height[l]);
        if(height[l]<=height[r]){
            l++;
        }else{
            r--;
        }
        res=res>local?res:local;
    }
    return res;
};

12-Integer to Roman

将一个int型的数字转化为罗马数字,范围在1-3999。下面是罗马数字的介绍及基本 规则: 罗马数字采用七个罗马字母作数字、即Ⅰ(1)、X(10)、C(100)、 M(1000)、V(5)、L(50)、D(500)。
记数的方法:

  • 相同的数字连写,所表示的数等于这些数字相加得到的数,如 Ⅲ=3
  • 小的数字在大的数字的右边,所表示的数等于这些数字相加得到的数,如 Ⅷ=8、Ⅻ=12
  • 小的数字(限于 Ⅰ、X 和 C)在大的数字的左边,所表示的数等于大数减小数 得到的数,如 Ⅳ=4、Ⅸ=9

注意点:

  • 罗马数字规则

例子:
输入: 3469 输出: “MMMCDLXIX”
解题思路:
法一:
最多有千百十个四位
每一位0-9

0      1-3   4   5   6-8   9
不表示  M..
不表示  C..   CD  D   DC..  CM 
不表示  X..   XL  L   LX..  XC 
不表示  I..   IV  V   VI..  IX 
/**
 * @param {number} num
 * @return {string}
 */
var intToRoman = function(num) {
    var rst='';

    if(num>3999||num<1) return rst;

    var q=Math.floor(num/1000);
    var b=Math.floor(num%1000/100);
    var s=Math.floor(num%1000%100/10);
    var g=Math.floor(num%1000%100%10);

    if(q>0){
        while(q){
            rst+='M';
            q--;
      }
    }
    if(b>0){
        if(b<=3){
            while(b){
                rst+='C';
                b--;
          }
        }else if(b==4){
            rst+='CD'
        }else if(b==5){
            rst+='D'
        }else if(b==9){
            rst+='CM'
        }else{
            rst+='D'
            while(b-5){
                rst+='C';
                b--;
          }
        }
    }
    if(s>0){
        if(s<=3){
            while(s){
                rst+='X';
                s--;
          }
        }else if(s==4){
            rst+='XL'
        }else if(s==5){
            rst+='L'
        }else if(s==9){
            rst+='XC'
        }else{
            rst+='L'
            while(s-5){
                rst+='X';
                s--;
          }
        }
    }
    if(g>0){
        if(g<=3){
            while(g){
                rst+='I';
                g--;
          }
        }else if(g==4){
            rst+='IV'
        }else if(g==5){
            rst+='V'
        }else if(g==9){
            rst+='IX'
        }else{
            rst+='V'
            while(g-5){
                rst+='I';
                g--;
          }
        }
    }

    return rst;

};

法二:
只需要依次找出数字中包含的最大的可转化为罗马数字的数字即可。

1000 900 500 400 100 90 50 40 10 9 5  4 1
  M  CM   D   CD  C  XC L  XL X IX V IV I 
/**
 * @param {number} num
 * @return {string}
 */
var intToRoman = function(num) {
    var rst='';
    var nums=[1000,900,500,400,100,90,50,40,10,9,5,4 ,1];
    var strings=["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"];	
    
    if(num>3999||num<1) return rst;
    
    for(var i=0;i<nums.length;i++){
        while(num>=nums[i]){
            rst+=strings[i];
            num-=nums[i];
        }
    }
    
    return rst;   
};

15-3Sum

找出一个列表中所有和为零的三元组。要求求出的三元组中没有重复。
注意点:

  • 子数组中的数字要按增序排列
  • 结果数组中没有重复的子数组
  • js的sort()函数是以字符串排序的,不能正确排序数字
  • 注意先排序

例子:
输入: nums=[-1, 0, 1, 2, -1, -4] 输出: [[-1, -1, 2], [-1, 0, 1]]
解题思路

  • 求一个列表中所有和为零的二元组的一种思路是先把列表排序,再用两个指针从两 头向中间移动。如果前后两个数的和小于0,则左指针右移;如果和大于0,则右指 针左移。
  • 求三元组时可以参考这种做法,第一个数a确定后,可以理解为求列表中 和为-a的二元组。
  • 由于不要考虑重复的元组,遇到重复的数可以直接跳过。
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    var rsts=[];
    nums=nums.sort(sortNum);//注意数字排序
    var i=0;
    while(i<nums.length-2){//注意边界条件
        var target=0-nums[i];
        var l=i+1,r=nums.length-1;
        while(l<r){
            if(nums[l]+nums[r]==target){
                var rst=[nums[i],nums[l],nums[r]];
                rsts.push(rst);
                l++;
                r--;
                //跳过重复
                while(nums[l]==nums[l-1]&&l<r) l++;
                while(nums[r]==nums[r+1]&&l<r) r--;
            }else if(nums[l]+nums[r]>target){
                r--;
            }else{
                l++;
            }
        }
        i++;
        //跳过重复
        while(nums[i]==nums[i-1]&&i<nums.length-2){
            i++;
        }
    }

    return rsts;
};

//从小到大
function sortNum(a,b){
    return a-b;
}

16-3Sum Closest

找出一个列表中三个元素之和与目标值最接近的情况,并返回这个值。假设整个列 表中只有一个最接近的值。
注意点:

  • js的sort()函数是以字符串排序的,不能正确排序数字
  • 注意先排序
  • 结果返回和
  • 只有唯一一组优解

例子:
输入: nums=[1, 1, 1, 1], target=-100 输出: 3
解题思路
思路与3Sum基本相同。不一样的是需要另外多实时记录最小的差值。当前和、当前差值、整体和、整体差值。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var threeSumClosest = function(nums, target) {
    if(nums.length<2) return null;
    nums.sort(sortNum);//排序
    //初始化全局变量
    var rst=nums[0]+nums[1]+nums[nums.length-1];
    var tmp=Math.abs(rst-target);
    for(var i=0;i<nums.length;i++){
        var l=i+1,r=nums.length-1;
        //二元组遍历方法
        while(l<r&&l>0&r<nums.length){//注意边界条件不可少
           //当前,局部变量
            var currst=nums[i]+nums[l]+nums[r];
            var curtmp=Math.abs(currst-target);
            //判断,用于循环
            if(currst==target){
                return currst;
            }else if(currst>target){
                r--;
            }else{
                l++;
            }
            //判断,是否更新全局变量
            if(curtmp<tmp){
                rst=currst;
                tmp=curtmp;
            }
        }
    }
    return rst;
};
function sortNum(a,b){
    return a-b;
}

17-Letter Combinations of a Phone Number

手机按键上每个数字都对应了多个字母,如2对应了"abc",现给出一个数字串,要 求把其中的每个数字都转化为对应的字母中的一个,列出所有的组合情况。
注意点:

  • 递归逻辑理清

例子:
输入: digits=“23” 输出: [“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”]
解题思路
把每个数字对应的字母都当做树的节点,如下图,则所求结果就是从根节点到叶节 点的所有的路径,采用深度优先遍历算法。
这里写图片描述
用递归Recursion来解,我们需要建立一个字典,用来保存每个数字所代表的字符串,然后我们还需要一个变量level,记录当前生成的字符串的字符个数

/**
 * @param {string} digits
 * @return {string[]}
 */
var letterCombinations = function(digits) {
    //数字对应字母
    var arr=['','','abc','def','ghi','jkl','mno','pqrs','tuv','wxyz'];
    //最终字符串数组
    var rst=[];
    if(digits.length==0) return rst;
    //digits输入数字,arr查询字典,level对应第几个数字,cur当前字符串,rst所有字符串
    letterCombinationsDFS(digits,arr,0,[],rst);
    return rst;
};
function letterCombinationsDFS(digits,arr,level,cur,rst){
    if(digits.length==level) rst.push(cur.join(""));//一遍数字循环完,得到一个字符串结果
    else{
        var str=arr[digits.charAt(level)];//当前数字对应的多个字母
        for(var i=0;i<str.length;i++){
            cur.push(str.charAt(i));//第level数字对应的第i(当前)字母
            letterCombinationsDFS(digits,arr,level+1,cur,rst);//递归,处理下一个数字
            cur.pop();
        }
    }
}

18-4Sum

找出一个列表中四个元素之和为目标值的情况,打印出所有的情况。
注意点:

  • 四元组中的数字要按增序排列(a<=b<=c) ,先排序sort()
  • 结果集中没有重复的四元组,.indexOf()
  • 注意边界条件

例子:
输入: nums=[1, 0, -1, 0, -2, 2] 输出: [[-1, 0, 0, 1], [-2, 0, 0, 2], [-2, -1, 1, 2]]
解题思路
继续延用2sum,3sum的思路,还是先做排序处理,然后固定2个两个数,求出他们的和,用sum = target-a-b来表示剩余2个元素的c,d要满足"target ",即tmp = c + d,这里用tmp和sum来比较差异,再利用两边向中间的方法遍历。
主要问题是如何保证结果中没有重复数组。增加一个用于判断是否已存在当前数组的数组,此数组存入的是字符串,再使用.indexOf()判断当前数组是否已存在,若存在不push到结果数组,若不存在push到结果数组并push字符串形式到判断数组。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    var res=[];
    if(nums.length<4) return res;
    nums.sort(sortNum);
    var sum=0;
    var tmp=0;
    var isExit=[];
    for(var i=0;i<nums.length-3;i++){
        for(var j=i+1;j<nums.length-2;j++){
            sum=target-nums[i]-nums[j];
            var l=j+1,r=nums.length-1;
            while(l<r){
                tmp=nums[l]+nums[r];
                if(sum==tmp){
                    var curstr=""+nums[i]+nums[j]+nums[l]+nums[r];
                    if(isExit.indexOf(curstr)==-1){
                        res.push([nums[i],nums[j],nums[l],nums[r]]);
                        isExit.push(curstr);
                    }
                    l++;
                    r--;
                }else if(sum>tmp){
                    l++;
                }else{
                    r--;
                }
            }
        }
    }
    return res;
};
function sortNum(a,b){
    return a-b;
}

19-Remove Nth Node From End of List

将一个链表中的倒数第n个元素从链表中去除。
注意点:

  • 不用考虑n是非法的情况
  • 尽量做到只遍历一次链表

例子:
输入: list = 1->2->3->4->5, n = 2. 输出: 1->2->3->5
解题思路
遍历链表获得链表长度,从而得知需要删除哪一个节点,然后删除此节点。注意先增加一个假的头结点,防止被删除的是头结点。

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    var res=new ListNode(-1);
    res.next=head;
    var cur=res.next;
    var len=0;
    while(cur!=null){
        len++;
        cur=cur.next;
    }
    var del=len-n;
    cur=res;
    while(del>0){
        cur=cur.next;
        del--;
    }
    var tmp=cur.next.next;
    cur.next=tmp;
    return res.next;   
};

22-Generate Parentheses

罗列出n组括号的所有合法的排列组合。
注意点:

  • 只有一种括号形式"()"

例子:
输入: n = 3 输出: [‘((()))’, ‘(()())’, ‘(())()’, ‘()(())’, ‘()()()’]

解题思路
合法的情况是,任意一时刻,左(“(”)括号数要大于等于右(“)”)括号数。
使用迭代,在某次的调用中:

  1. .left大于right(left和right分别表示剩余左右括号的个数),即,临时变量中右括号的数大于左括号的数,则说明出现了“)(”,这是非法情况,返回即可;
  2. left和right都等于0说明,临时变量中左右括号数相等,所以将临时变量中的值存入res中;
  3. 其余的情况是,先放左括号,然后放右括号,然后递归。注意参数的更新。
/**
 * @param {number} n
 * @return {string[]}
 */
var generateParenthesis = function(n) {
    var res=[];
    generate(n,n,"",res);
    return res;
};

function generate(left,right,str,res){
    if(left>right) return;
    if(left==0&right==0){
        res.push(str);
    }else{
        if(left>0) generate(left-1,right,str+"(",res);
        if(right>0) generate(left,right-1,str+")",res);
    }
}

24-Swap Nodes in Pairs

将链表中相邻的两个节点交换位置,注意第一个节点与第二个节点要交换位置,而 第二个节点不用与第三个节点交换位置。
注意点:

  • 不允许修改节点的值
  • 只能用常量的额外空
  • 注意处理节点为空时的情况

例子:
输入: head = 1->2->3->4 输出: 2->1->4->3

解题思路
比较常见的链表操作。下面看一下典型情况,如要交换链表中A->B->C->D中的B和 C需要做如下操作,一定注意顺序:

  1. 将A指向C
  2. 将B指向D
  3. 将C指向B
    在头节点之前加一个假节点就可以使所有的交换都符合上面的情况。
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    var res=new ListNode(-1);
    res.next=head;
    var temp=res;
    while(temp!=null){
        if(temp.next==null) break;
        var node1=temp.next;
        if(node1.next==null) break;
        var node2=node1.next;
        temp.next=node2;
        node1.next=node2.next;
        node2.next=node1;
        temp=node1;
    }

    return res.next;
};

26-Remove Duplicates from Sorted Array

从一个有序的数组中去除重复的数字,返回处理后的数组长度。
注意点:

  • 只能用常量的额外空间
  • 不重复的数字移到数组前部,剩余的部分不需要处理

例子:
输入: nums = [1, 1, 2] 输出: 2
解题思路
注意数组是有序的,所以重复的数字是相邻的。i表示当前数值下标,j表示之后数字下标,遍历,若nums[i]与nums[j]不相等,i后移,nums[i]为下一个即为nums[j],继续遍历;否则直接继续遍历。最后返回i+1(i为下标,从0开始)即为长度,此时不重复的数字移到数组前部。
注意处理数组为空时的情况

var removeDuplicates = function(nums) {
    if(nums.length==0) return 0;
    var i=0;
    for(var j=1;j<nums.length;j++){
        if(nums[i]!=nums[j]){
            i++;
            nums[i]=nums[j];
        }
    }
    
    return i+1;
};

##27-Remove Element
删除一个数组中某一特定数值的元素,返回删除后的数组长度。
注意点:

  • 操作结束后的数字排列顺序不需要与之前相同
  • 超出返回长度的部分不需要处理

例子:
输入: nums [1, 2, 3, 4, 3, 2, 1],val = 1 输出: 5
解题思路
num表示当前值下标,遍历,若当前遍历值等于val,当前值更新为当前遍历值的下一个值,继续遍历;若不等于,当前值等于当前遍历值,当前值下标加1,继续遍历。最后返回num即为删除后数组长度
注意处理数组为空时的情况

var removeElement = function(nums, val) {
    if(nums.length==0) return 0;
    
    var num=0;
    for(var i=0;i<nums.length;i++){
        if(nums[i]!=val){
            nums[num]=nums[i];
            num++;
        }
        else{
            nums[num]=nums[i+1];
        }
    }
    
    return num;
};

29-Divide Two Integers

实现两个int型数字的除法,不可以使用乘法、除法和模操作。
注意点:

  • 如果结果溢出int类型,返回MAX_INT
  • 注意负数情况
  • 注意位操作(左移)溢出状况

例子:
输入: dividend = 5, divisor = -1 输出: -5
解题思路
这道题让我们求两数相除,而且规定我们不能用乘法,除法和取余操作,那么可以用另位操作Bit Operation,思路是,如果被除数大于或等于除数,则进行如下循环,定义变量t等于除数,定义计数p,当t的两倍小于等于被除数时,进行如下循环,t扩大一倍,p扩大一倍,然后更新res和m。这道题的OJ给的一些test case非常的讨厌,因为输入的都是int型,比如被除数是-2147483648,在int范围内,当除数是-1时,结果就超出了int范围,需要返回INT_MAX,所以对于这种情况我们就在开始用if判定,将其和除数为0的情况放一起判定,返回INT_MAX。然后我们还要根据被除数和除数的正负来确定返回值的正负,最后返回值乘以符号即可。
此外,由于JavaScript语言特性,左移会溢出成为负数,注意左移操作后及时进行判断,若溢出位负数,则跳出循环。

/**
 * @param {number} dividend
 * @param {number} divisor
 * @return {number}
 */
var divide = function(dividend, divisor) {
  var INT_MAX=2147483647,INT_MIN=-2147483648;
  var sign=(dividend>0)^(divisor>0)?-1:1;//结果符号
  if(divisor==0) return INT_MAX;//除数为零
  if((dividend==INT_MIN)&&(divisor==-1)) return INT_MAX;//特殊溢出情况
  var m=Math.abs(dividend),n=Math.abs(divisor),res=0;
  while(m>=n){
    var t=n,p=1;
    var judge=t<<1;//用于判断t左移后是否溢出,为负数时表示溢出,不进入循环
    if(judge>0){
      while(m>judge){
        t=t<<1;
        p=p<<1;
        judge=t<<1;
        if(judge<0) break;
      }
    }
    res+=p;
    m-=t;
  }
  return sign==1?res:-res;
};

31-Next Permutation

全排列算法。找出一个数组按字典序排列的后一种排列。 如abc,这个序列有六个可能的排列组合:abc,acb,bac,bca,cab,cba。这些排列组合根据less-than操作符做字典顺序(lexicographical)的排序。也就是说,abc名列第一,因为每一个元素都小于其后的元素。

注意点:

  • 如果原来就是字典序排列中最大的,将其重新排列为字典序最小的
  • 排列 不要申请额外的空间
  • 函数没有返回值,直接修改数组
  • javascript数组的reverse()不能对数组特定字段逆序

例子:
输入: [1,2,3] 输出: [1,3,2]
输入: [3,2,1] 输出: [1,2,3]

解题思路

  1. 首先从最尾端开始往前寻找两个相邻元素,令第一元素为i,第二元素为j,且满足*i<*j。
  2. 找到这样一组相邻元素后,再从最尾端开始往前检验,找出第一个大于i的元素,令为k,将i,k元素对调(swap)。
  3. 再将j之后的所有元素颠倒(reverse)排序。

详解见https://blog.csdn.net/c18219227162/article/details/50301513

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var nextPermutation = function(nums) {
  if(nums.length<2) return;
  var l=nums.length;
  //找到i<j,i、j相邻
  while(l--){
    var j=l;
    var i=j-1;
    if(nums[i]<nums[j]) {
      var k=nums.length;
      //找到k>i,交换
      while(k--){
        if(nums[k]>nums[i]){
          var temp=nums[i];
          nums[i]=nums[k];
          nums[k]=temp;
          //逆序
          var temp2=nums.slice(j,nums.length).reverse();
          for(var t=0,j;j<nums.length;j++,t++){
            nums[j]=temp2[t];
          }
          return;
        }
      }
    }
  }
  nums.reverse();
};

35-Search Insert Position

在一个有序数组中,如果目标数字存在,则返回它的下标,否则返回它应该插入位置的下标值。
注意点:

  • 数组中没有重复数字

例子:
输入: nums = [1, 3, 5, 6], target = 5 输出: 2
输入: nums = [1, 3, 5, 6], target = 2 输出: 1
解题思路
二分搜索。首先左边下标不能大于右边下标。当前中间值等于目标值,返回当前中间值下标;大于目标值,右边下标为中间值下标减1;小于目标,左边下标为中间下标值加一。若左边下标大于右边,停止循环,目标数字不存在,返回左边下标。

var searchInsert = function(nums, target) {
    var l=0,r=nums.length-1,mid;
    //左下标小于等于右下标
    while(l<=r){
        mid=Math.floor((l+r)/2);//二分,中间值
        if(target==nums[mid]){
            return mid;
        }
        else if(target<nums[mid]){
            r=mid-1;//目标在[l,mid-1]之间
        }
        else{
            l=mid+1;//目标在[mid+1,r]之间
        }
    }
    return l;
};

53-Maximum Subarray

求一个数组中和最大的子数组。
注意点:

  • 需要考虑负数的情况

例子:
输入: nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
输出: 6(数组[4, -1, 2, 1]的和)
解题思路
动态规划,局部最优和全局最优解法。在每一步,我们维护两个变量,一个是全局最优,就是到当前元素为止最优的解,一个是局部最优,就是必须包含当前元素的最优的解。
动态规划的递推式。假设我们已知第i步的global[i](全局最优)和local[i](局部最优),那么第i+1步的表达式是:
local[i+1]=Math.max(A[i], local[i]+A[i])
有了当前一步的局部最优,那么全局最优就是当前的局部最优或者还是原来的全局最优:
global[i+1]=Math(local[i+1],global[i])
(所有情况都会被涵盖进来,因为最优的解如果不包含当前元素,那么前面会被维护在全局最优里面,如果包含当前元素,那么就是这个局部最优)
时间复杂度是O(n),空间复杂度是两个变量(local和global),即O(2)=O(1)

var maxSubArray = function(nums) {
    //if(nums.length==0) return 0;
    //局部最优初始
    var local=nums[0];
    //全局最优初始
    var global=nums[0];
    for(var i=1;i<nums.length;i++){
        local=Math.max(local+nums[i],nums[i]);
        global=Math.max(global,local);
    }
    return global;
};

66-Plus One

给一个由包含一串数字的列表组成的非负整数加上一。
注意点:

  • 列表前面的数字表示高位
  • 注意最高位也可能进位

例子:
输入: [1, 2, 3, 4, 9]
输出: [1, 2, 3, 5, 0]
解题思路
最低位加一,若大于9,进位为1,否则进位为0;遍历高一位,加进位,若大于9,进位为1,否则进位为0;继续遍历,直至最高位,若有进位,最高位前插入1

var plusOne = function(digits) {
    var temp=0;//进位
    var sum=digits[digits.length-1]+1;
    var result=[];//结果
    //先处理最后一位
    if(sum>=10){
        temp=1;
        result.unshift(sum%10);
    }
    else{
        temp=0;
        result.unshift(sum);
    }
    //在处理前面高位
    for(var i=digits.length-2;i>=0;i--){
        sum=digits[i]+temp;
        if(sum>=10){
            temp=1;
            result.unshift(sum%10);
        }
        else{
            temp=0;
            result.unshift(sum);
        }
    }
    //最后处理最高位进位
    if(temp==1){
        result.unshift(1);
    }
    return result;
};

88-Merge Sorted Array

将两个有序数组合并成为一个。
注意点:

  • 第一个数组有充足的空间来存放第二个数组中的元素
  • 第一个数组的有效长度为m,第二个的有效长度为n
  • 在原数组上修改,没有返回值

例子:
输入: nums1 = [1, 1, 2, 2, 4, 0, 0, 0, 0], m = 5, nums2 = [0, 0, 2, 3], n = 4
输出: 无(nums1变为[0, 0, 1, 1, 2, 2, 2, 3, 4])
解题思路
知道两个数组原先的长度,就可以知道合并后的长度,倒叙遍历两个数组,大的数优先放到合并后的数组对应下标处。如果第一个数组先遍历完,那应该把第二个数组剩下的元素复制过来;如果第二个先遍历玩,就不用变化了,因为第一个数组剩余的元素已经在目标位置。

var merge = function(nums1, m, nums2, n) {
    var l=m+n-1;//下标从0开始
    m--;n--;//下标从0开始
    //倒序遍历,两个数组都没有遍历完
    while(m>=0&&n>=0){
	    //当前位应该为哪个值
        if(nums1[m]>nums2[n]){
            nums1[l]=nums1[m];
            m--;
        }
        else{
            nums1[l]=nums2[n];
            n--;
        }
        //处理前一位
        l--;
    }
    
    //若第二个数组有剩余,复制到第一个数组
    while(n>=0){
        nums1[l]=nums2[n];
        n--;
        l--;
    }
};

118-Pascal’s Triangle

要求得到一个n行的杨辉三角。
注意点:

例子:
输入: numRows = 5
输出:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]
解题思路
杨辉三角的特点是每一行的第一和最后一个元素是1,其它元素是上一行它左右两个元素之和:
r[i][j]=r[i-1][j-1]+r[i-1][j];

var generate = function(numRows) {
    var r=[];//结果数组
    //总共有numRows行
    for(var i=0;i<numRows;i++){
        //分别处理每一行
        var t=[];//一维数组
        r.push(t);//二维数组
        for(var j=0;j<=i;j++){
	        //当前行第一位和最后一位为1
            if(i==j||j==0) r[i][j]=1;
            //其他位为上一行i-1的前一列和当前列
            else{
                r[i][j]=r[i-1][j-1]+r[i-1][j];
            }
        }
    }   
    return r;
}

119-Pascal’s Triangle II

用O(k)的空间得到杨辉三角第k行的数值。
注意点:

  • 从0开始计算行数,即第0行为[1]

例子:
输入: k = 3
输出: [1,3,3,1]
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]
解题思路
只取一行,并且需要节省空间;用一位数组,防止覆盖,每行从后往前倒着赋值,首末位赋值仍为1,其他位赋值仍为上一行前一列和上一行当前列。
r[j]=r[j]+r[j-1];

var getRow = function(rowIndex) {
    var r=[];
    for(var i=0;i<=rowIndex;i++){
        for(var j=i;j>=0;j--){
            if(j==0||i==j) r[j]=1;
            else r[j]=r[j]+r[j-1];
        }
    }
    return r;
};

欢迎查看我的Github (https://github.com/Lorogy/leetCode.git) 来获得相关源
码。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值