03 数组中重复的数字
解法1 使用数据结构Set
,利用其不会储存相同元素的特性来解决,不断往里面添加元素,看看Set大小有没有变,没变说明添加的就是重复的元素
var findRepeatNumber = function (nums ) { let mySet = new Set (); let size = mySet.size; let result = -1 ; nums.some(e => { mySet.add(e); if (mySet.size === size) { result = e; return true ; } size+=1 ; }) return result; };
需要注意的是,在遍历数组nums
的时候,不能采用forEach
里return结果的方式,因为这样并不会跳出循环,break
也是一样的;可以使用some
和every
方法来实现可跳出的循环,前者return true时跳出,后者return false跳出
解法2 可以使用原地交换的方式,因为题目中说长度为n的数组,元素大小在[0,n-1],有重复元素
,那么在建立索引和值的对应关系上,一定会出现某个索引对应了两个值的情况,该值就是重复的元素
var findRepeatNumber = function (nums ) { let i=0 ; while (true ){ if (nums[i] !== i){ if (nums[nums[i]] === nums[i]){ return nums[i]; } [nums[nums[i]], nums[i]] =[nums[i], nums[nums[i]]]; } else { i++; } } };
04 二维数组中的查找
因为每行、每列都是递增顺序排列的,所以可以从右上角开始,如果当前数组中的数比target
大,那么往左移;小,那么往下移;终止循环的条件是索引越界
var findNumberIn2DArray = function (matrix, target ) { let i = 0 ; let j = matrix[0 ].length-1 ; while (true ) { if (i>matrix.length-1 || j<0 ) { return false ; } if (target === matrix[i][j]) { return true ; } if (matrix[i][j] > target) { j--; } else if (matrix[i][j] < target) { i++; } } };
05 替换空格
使用js的常用API就可以解决
var replaceSpace = function (s ) { return s.split(' ' ).join('%20' ); }; var replaceSpace = function (s ) { return s.replace(/\s/g , '%20' ); };
str.replace(/^\s+|\s+$/g, 'haloha')
\s
: 表示 space ,空格+
: 一个或多个^
: 开始,^\s
,以空格开始$
: 结束,\s$
,以空格结束|
: 或者/g
:global, 全局
06 从尾到头打印链表
先顺序遍历,把结果放到数组里,再reverse
一下;或者使用unshift()
var reversePrint = function (head ) { let result = []; while (head) { result.push(head.val); head = head.next; } result.reverse(); return result; }; var reversePrint = function (head ) { let result = []; while (head) { result.unshift(head.val); head = head.next; } return result; };
07 重建二叉树
核心是递归,根据preorder
里的第一个元素,可以把inorder
划分成左子树和右子树,再根据inoder
里子树的大小,将preorder
里划分成左子树和右子树 ,然后对应的进行递归操作即可
var buildTree = function (preorder, inorder ) { let result = new TreeNode(preorder[0 ]); let index = inorder.indexOf(preorder[0 ]); result.left = buildTree(preorder.slice(1 , 1 +index), inorder.slice(0 , index)); result.right = buildTree(preorder.slice(1 +index), inorder.slice(index+1 )); return result; };
10 斐波那契数列
解法1(动态规划) 使用最简单的递归方法,,果然超时了,函数的入栈出栈的时间成本还是太高了,所以用了最简单的迭代,不过空间复杂度是O(N),比较占用内存
var fib = function (n ) { if (n === 0 || n === 1 ) { return n; } let arr = [0 , 1 ]; for (let i=2 ; i<=n; i++) { arr[i] = (arr[i-1 ] + arr[i-2 ])%1000000007 ; } return arr[n]; };
解法2 使用递归的同时,再开辟一个新的数组用来存储已经算过的项,避免重复;例如算n-1和n-2这两项的时候都会用到第n-3项。所以在使用某一项的时候先看看这个数组里面有没有值,没有值再递归计算赋值
不过这样时间复杂度和空间复杂度都好高啊,还是使用动态规划最好
青蛙跳台阶也可以转换成类似的想法,关键是得到f(n)=f(n-1)+f(n-2)
这个不变式
11 旋转数组的最小数字
线性遍历数组,发现后一个比前一个小,那么后一个就是答案;如果遍历完都没找到就说明这个数组是升序排列,返回第一个就好;时间复杂度O(N)
var minArray = function (numbers ) { for (let i=0 ;i<numbers.length-1 ;i++){ if (numbers[i+1 ] < numbers[i]){ return numbers[i+1 ]; } } return numbers[0 ] };
12 矩阵中的路径🌟
回溯
本题的思路核心是回溯,每次判断完一条路径是否符合条件后,要及时把状态恢复,以便其他条件递归
var exist = function (board, word ) { for (let i=0 ;i<board.length;i++){ for (let j=0 ;j<board[0 ].length;j++){ if (word.length===1 && board[i][j]===word.charAt(0 )){ return true ; } else if (board[i][j]===word.charAt(0 )){ if (help(board,word.substring(1 ),i,j)){ return true ; } } } } return false ; }; var help = (board, word, i, j ) => { let temp=board[i][j]; board[i][j]='0' ; let target = word.charAt(0 ); let signal1=false ; let signal2=false ; let signal3=false ; let signal4=false ; let res1=false ; let res2=false ; let res3=false ; let res4=false ; if (j+1 <board[0 ].length && board[i][j+1 ]===target){ signal1=true ; res1=help(board, word.substring(1 ),i,j+1 ); } if (i+1 <board.length && board[i+1 ][j]===target){ signal2=true ; res2=help(board, word.substring(1 ),i+1 ,j); } if (j-1 >=0 && board[i][j-1 ]===target){ signal3=true ; res3=help(board, word.substring(1 ),i,j-1 ); } if (i-1 >=0 && board[i-1 ][j]===target){ signal4=true ; res4=help(board, word.substring(1 ),i-1 ,j); } board[i][j]=temp; if (word.length===1 && (signal1||signal2||signal3||signal4)){ return true ; } else if (signal1||signal2||signal3||signal4){ return res1||res2||res3||res4; } else { return false ; } }
18 有效的回文
解法一 给定的字符串s中会有一些非字母和数字的字符,所以要先做一步过滤
var isPalindrome = function (s ) { if (!s){ return true ; } let arr=[]; let pattern=/[a-z]|[A-Z]|[0-9]/ ; for (let i=0 ;i<s.length;i++){ if (pattern.test(s[i])){ arr.push(s[i]); } } console .log(arr.join('' )); return arr.join('' ).toLowerCase()===arr.reverse().join('' ).toLowerCase(); };
解法二 原地判断,这样空间复杂度就变成了O(1),时间复杂度还是一遍遍历O(N)
var isPalindrome = function (s ) { if (!s){ return true ; } let i=0 ; let j=s.length-1 ; let p=/[a-z]|[A-Z]|[0-9]/ ; while (i<j){ while (!p.test(s[i])){ i++; } while (!p.test(s[j])){ j--; } if (!s[i] || !s[j]){ break ; } if (s[i].toLowerCase()!==s[j].toLowerCase()){ return false ; } i++; j--; } return true ; };
19 删除链表的倒数第 N 个结点
var removeNthFromEnd = function (head, n ) { let p=head; let target=head; for (let i=0 ;i<n-1 ;i++){ p=p.next; } let pre=head; while (p.next){ pre=target; p=p.next; target=target.next; } if (head===target){ return target.next; } pre.next=target.next; return head; };
29 顺时针打印矩阵
var spiralOrder = function (matrix ) { let result=[]; if (matrix.length===0 ){ return result; } help(matrix,result,0 ,0 ,matrix[0 ].length-1 ,matrix.length-1 ); return result; }; let help=(matrix,result,l,t,r,b )=> { if (l===r && t===b){ result.push(matrix[t][l]); return ; } if (l>r || t>b){ return ; } if (l<r && t==b){ for (let i=l;i<=r;i++){ result.push(matrix[t][i]); } } else if (t<b && l==r){ for (let i=t;i<=b;i++){ result.push(matrix[i][r]); } } else { for (let i=l;i<=r;i++){ result.push(matrix[t][i]); } for (let i=t+1 ;i<=b;i++){ result.push(matrix[i][r]); } for (let i=r-1 ;i>=l;i--){ result.push(matrix[b][i]); } for (let i=b-1 ;i>t;i--){ result.push(matrix[i][l]); } } l++; t++; r--; b--; help(matrix,result,l,t,r,b); }
38 字符串的排列🌟
回溯
考察的是DFS+回溯,回溯体现在数组和字符串上,需要注意的是当字符串只有一个字符时的特殊情况
var permutation = function (s ) { if (!s){ return []; } let strArr=s.split('' ); let result=new Set (); let temp='' ; for (let i=0 ;i<strArr.length;i++){ temp+=strArr[i]; let char=strArr[i]; let charIndex=strArr.indexOf(char); strArr.splice(charIndex,1 ); help(strArr,result,temp); temp=temp.substring(0 ,temp.length-1 ); strArr.splice(charIndex,0 ,char); } return Array .from(result); }; let help=(strArr,result,temp )=> { if (strArr.length===0 ){ result.add(temp); return ; } if (strArr.length===1 ){ temp+=strArr[0 ]; result.add(temp); return ; } else { for (let i=0 ;i<strArr.length;i++){ temp+=strArr[i]; let char=strArr[i]; let charIndex=strArr.indexOf(char); strArr.splice(charIndex,1 ); help(strArr,result,temp); temp=temp.substring(0 ,temp.length-1 ); strArr.splice(charIndex,0 ,char); } } }
44 数字序列中某一位的数字
找规律,计算不同位数占用索引的多少,倒推给定的n属于哪一个数,然后找到对应的某一位即可
var findNthDigit = function (n ) { if (n<10 ){ return n; } let i=1 ; let cp=n; while (cp-(Math .pow(10 ,i)-Math .pow(10 ,i-1 ))*i>0 ){ cp=cp-(Math .pow(10 ,i)-Math .pow(10 ,i-1 ))*i; i++; } cp=cp-1 ; let t=Math .floor(cp/i); let m=cp%i; let tempResult=Math .pow(10 ,i-1 )+t; return (tempResult+'' ).charAt(m); };
50 第一个只出现一次的字符
解法1 用两个数组来解决这个问题,一个数组用来存储遍历过的字符,一个数组用来存储已经重复过的的字符;遍历时遇到的字符在这两个数组内都没有出现时,进入第一个数组;如果在第一个数组中出现了,那么从第一个数组中剔除,并加入第二个数组;其余情况都不用考虑
不过好像其实也没有降低时间复杂度,因为使用到了indexOf
来查找重复字符的索引,如果让自己来实现的话,又是O(N)的复杂度,综合下来还是O(N2)时间复杂度
var firstUniqChar = function (s ) { if (!s){ return ' ' ; } let temp = []; let dupTemp = []; for (let i=0 ;i<s.length;i++){ if (dupTemp.indexOf(s[i]) === -1 && temp.indexOf(s[i]) === -1 ){ temp.push(s[i]); } else if (temp.indexOf(s[i]) !== -1 ){ temp.splice(temp.indexOf(s[i]), 1 ); dupTemp.push(s[i]); } } return temp[0 ]?temp[0 ]:' ' ; };
解法2 使用map来解决;需要注意到的是,map对象是乱序的;但是按照key
,value
的形式来遍历时,是按照插入的顺序进行的
var firstUniqChar = function (s ) { var map = new Map (); for (let i=0 ;i<s.length;i++){ var cur = s.charAt(i); if (map.has(cur)){ map.set(cur,false ); } else { map.set(cur,true ); } } for ([key,value] of map){ if (value){ return key; } } return ' ' };
55 平衡二叉树🌟
采用后序遍历的方式来计算每个节点的高度,这样最后遍历的head的高度就很容易计算了
var isBalanced = function (root ) { return help(root) === -1 ? false : true ; }; let help = function (node ) { if (!node) { return 0 ; } let left = help(node.left); let right = help(node.right); if (Math .abs(left-right) > 1 || left === -1 || right === -1 ) { return -1 ; } return Math .max(left, right) + 1 ; }