JavaScript
欢迎查看我的Github (https://github.com/Lorogy/leetCode.git) 来获得相关源
码
给一个int型数组,要求找出其中两个和为特定值的数的坐标
注意点:
例子:
输入: numbers={2, 7, 11, 15}, target=9 输出: index1=0, index2=1
解题思路:
遍历,判断当前节点与之后每一个节点分别相加,若和为目标值,返回两个index
var twoSum = function(nums, target) {
for(var i=0;i
定义这样的一个链表,链表的每个节点都存有一个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;
};
找出一个字符串的最长字符串,要求该字符串中没有重复的字符。
注意点:
例子:
输入: “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;istart){
start=arr[s[i].charCodeAt()];
}
arr[s[i].charCodeAt()]=i;//赋值最近下标
cur=i-start;//局部长度
len=len>=cur?len:cur;
}
return len;
};
找到一个字符串的最长回文子字符串,该字符串长度不超过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=0&&k
例子:
输入: 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
字符串转换为int整型
注意点:
例子:
输入: 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='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;
};
给定一组长短不一的隔板,挑其中的两块板,使得板子之间能装最多的水。
注意点:
例子:
输入: height=[1,1,1] 输出: 2
解题思路:
i,j可装水min(h[i],h[j])*(j-i)
;
若装水最多时,h[i]>h[,
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;
};
将一个int型的数字转化为罗马数字,范围在1-3999。下面是罗马数字的介绍及基本 规则: 罗马数字采用七个罗马字母作数字、即Ⅰ(1)、X(10)、C(100)、 M(1000)、V(5)、L(50)、D(500)。
记数的方法:
注意点:
例子:
输入: 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[i]){
rst+=strings[i];
num-=nums[i];
}
}
return rst;
};
找出一个列表中所有和为零的三元组。要求求出的三元组中没有重复。
注意点:
例子:
输入: nums=[-1, 0, 1, 2, -1, -4] 输出: [[-1, -1, 2], [-1, 0, 1]]
解题思路
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
var rsts=[];
nums=nums.sort(sortNum);//注意数字排序
var i=0;
while(itarget){
r--;
}else{
l++;
}
}
i++;
//跳过重复
while(nums[i]==nums[i-1]&&i
找出一个列表中三个元素之和与目标值最接近的情况,并返回这个值。假设整个列 表中只有一个最接近的值。
注意点:
例子:
输入: 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;i0&rtarget){
r--;
}else{
l++;
}
//判断,是否更新全局变量
if(curtmp
手机按键上每个数字都对应了多个字母,如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
找出一个列表中四个元素之和为目标值的情况,打印出所有的情况。
注意点:
例子:
输入: 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;itmp){
l++;
}else{
r--;
}
}
}
}
return res;
};
function sortNum(a,b){
return a-b;
}
将一个链表中的倒数第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;
};
罗列出n组括号的所有合法的排列组合。
注意点:
例子:
输入: n = 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);
}
}
将链表中相邻的两个节点交换位置,注意第一个节点与第二个节点要交换位置,而 第二个节点不用与第三个节点交换位置。
注意点:
例子:
输入: head = 1->2->3->4 输出: 2->1->4->3
解题思路
比较常见的链表操作。下面看一下典型情况,如要交换链表中A->B->C->D中的B和 C需要做如下操作,一定注意顺序:
/**
* 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;
};
从一个有序的数组中去除重复的数字,返回处理后的数组长度。
注意点:
例子:
输入: 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
##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
实现两个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;
};
全排列算法。找出一个数组按字典序排列的后一种排列。 如abc,这个序列有六个可能的排列组合:abc,acb,bac,bca,cab,cba。这些排列组合根据less-than操作符做字典顺序(lexicographical)的排序。也就是说,abc名列第一,因为每一个元素都小于其后的元素。
注意点:
例子:
输入: [1,2,3] 输出: [1,3,2]
输入: [3,2,1] 输出: [1,2,3]
解题思路
详解见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;
//找到ii,交换
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 = [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 = [-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
给一个由包含一串数字的列表组成的非负整数加上一。
注意点:
例子:
输入: [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;
};
将两个有序数组合并成为一个。
注意点:
例子:
输入: 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--;
}
};
要求得到一个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
##119-Pascal’s Triangle II
用O(k)的空间得到杨辉三角第k行的数值。
注意点:
例子:
输入: 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) 来获得相关源
码。