注意:
根据下图了解对应数据表示信息:
题解思路:动态规划
用一个空字符串,动态保存无重复的字符,并动态保存最大的无重复字符
当遇到重复的字符后,
从第一个重复的字符的下标往后一位切割并拼接上第二个重复的字符,例如 ‘abca’ ==> ‘bca’
// 用字符串保存结果
/*
思路:
用一个空字符串,动态保存无重复的字符,并动态保存最大的无重复字符
当遇到重复的字符后,
从第一个重复的字符的下标往后一位切割并拼接上第二个重复的字符,例如 'abca' ==> 'bca'
*/
var lengthOfLongestSubstring = function(s) {
var str="" //存放无重复子串
var size=0 //当前最长无重复子串的长度
for(var i=0,len=s.length;i<len;i++){
var index=str.indexOf(s[i])
if(index==-1){
str+=s[i]
size=size<str.length?str.length:size
}else{
str=str.substr(index+1)+s[i]
}
}
return size
};
//上题的用数组保存做法
var lengthOfLongestSubstring = function(s) {
var str =''
var res = []
for (let i of s ){
var index = str.indexOf(i)
if(index < 0){
str +=i
}else{
// 将重复前的字符放入数组中
res.push(str)
// 字符串切换到第一个重复后一位,例如 'abca' ==> 'bca'
str = str.substr(index + 1) + i
}
}
// 将没重复的放入数组
res.push(str)
// 将数组的字符从大到小降序排列,第一个就是最大的长度
res.sort((a,b)=> b.length - a.length)
return res[0].length
};
题解思路:双指针
具题目了解两数组以递增排好序
应题目最终要求将结果保存在数组1中,所以将在数组1中进行后续操作
从数组1的最后开始循环判断将两数组中的最大的放入,并将对应的指针,及数组的长度推进
最后形成的数组1就是排好的两数组
/*
题解思路:双指针
具题目了解两数组以递增排好序
应题目最终要求将结果保存在数组1中,所以将在数组1中进行后续操作
从数组1的最后开始循环判断将两数组中的最大的放入,并将对应的指针,及数组的长度推进
最后形成的数组1就是排好的两数组
*/
var merge = function(nums1, m, nums2, n) {
let len = nums1.length -1 // 对应下标都为数组的长度-1
m--
n--
while(n>=0){
// 在第二个数组推入完之前,都进行循环,将两数组中最大的放入最后
if(nums1[m] > nums2[n]){
nums1[len--] = nums1[m--]
}else{
nums1[len--] = nums2[n--]
}
}
};
题解思路:双指针
创建两个指针i,j分别为两数组的最后下标
将两数组的最后一位分别转为数值,随后进行相加,进位树保存给下一次相加
将所有结果的个位数全部放在一个数组里,最后拼接为字符串返回
/*
题解思路:双指针
创建两个指针i,j分别为两数组的最后下标
将两数组的最后一位分别转为数值,随后进行相加,进位树保存给下一次相加
将所有结果的个位数全部放在一个数组里,最后拼接为字符串返回
*/
var addStrings = function(num1, num2) {
let i = num1.length - 1
let j = num2.length - 1
let carry = 0 // 进位数,即留到下一轮相加的数
let res = [] // 存放相加结果的个位数
while(i >= 0 || j >= 0 || carry !== 0) {
// 将v1 , v2 转为数值,其中一个数加完 <0 后赋值对应值为0,防止出错
const v1 = i >= 0 ? num1.charAt(i) - '0' : 0
const v2 = j >= 0 ? num2.charAt(j) - '0' : 0
let val = v1 + v2 + carry
res.unshift(val % 10) // 将相加后的个位数放进数组,用unshift从头往前放,因为是从后往前相加的
carry = Math.floor(val / 10) // 相加后的十位数,留在下一次作为进位数一起进行相加
// 两个指针继续往前走
i--
j--
}
return res.join('')
};
题解思路:spilt分割+数值转化
使用字符串的split分割’.’,随后得到分割后的数组
从两个数组中最大的长度进行循环,防止比较不完全
逐个将分割后的数组提出来,将其转为数值后会忽略前导0,然后再进行比较,若相等则进行下次比较
最后都相等的话,就返回0
/*
题解思路:使用字符串的split分割'.',随后得到分割后的数组
从两个数组中最大的长度进行循环,防止比较不完全
逐个将分割后的数组提出来,将其转为数值后会忽略前导0,然后再进行比较,若相等则进行下次比较
最后都相等的话,就返回0
*/
var compareVersion = function(version1, version2) {
let arr1 = version1.split('.'); // 1.01 == > [1,01]
let arr2 = version2.split('.');// 1.001 == > [1,001]
for(let i = 0; i < Math.max(arr1.length,arr2.length) ; i++){
let x1 = arr1[i] || 0
let x2 = arr2[i] || 0
if(+x1 == +x2){ // 比较时 01 => 1 , 001 => 1 转为数值自动将忽略前导0
continue
}else if(+x1 > +x2){
return 1
}else if(+x1 < +x2){
return -1
}
}
return 0
};
题解思路:使用map保存出现过的数
目标和 – 当前数 等于rest 若rest,在对象中保存过则说明两数相加等于目标和,最后返回两坐标即可
例如:[2,7,3] target = 9
第一次 rest = 9 - 2 = 7
并在map中保存出现的 2(nums[i])和对应的下标(i)
第二次中 rest = 9 - 7 = 2
在map中找到出现过的 2,说明两个之和等于target
最后用数组返回对应下标 [map.get(rest),i]
map.get(rest) 会返回2的下标,因为就是用它的下标值保存在map中
i 为当前数下标
/*
题解思路:使用map保存出现过的数
目标和 -- 当前数 等于rest 若rest,在对象中保存过则说明两数相加等于目标和,最后返回两坐标即可
例如:[2,7,3] target = 9
第一次 rest = 9 - 2 = 7
并在map中保存出现的 2(nums[i])和对应的下标(i)
第二次中 rest = 9 - 7 = 2
在map中找到出现过的 2,说明两个之和等于target
最后用数组返回对应下标 [map.get(rest),i]
map.get(rest) 会返回2的下标,因为就是用它的下标值保存在map中
i 为当前数下标
*/
ar twoSum = function(nums, target) {
let map = new Map()
let len = nums.length
for(let i =0 ;i<len ;i++){
let rest = target - nums[i]
if(map.has(rest)){
return [map.get(rest),i]
}
map.set(nums[i],i)
}
};
题解思路:利用栈结构先进后出,满足有效括号要求
创建一个对象,保存左括号对应的右括号值
遍历字符里出现的有效左括号,然会推入栈中
在遍历右括号时,从栈中从栈尾依次取出,若取出的不等于当前的右括号则不是有效括号
例如:当输入 “([)]”,
栈中保存 [ )] ]
当遍历到 ‘)’ 时,从栈中取出的为 ‘]’ ,不相等,则不是有效括号注意:
最后返回判断的是栈内是否为空数组,
因为可能有输入 ‘(’ 然后栈内还有 ‘)’
/*
题解思路:利用栈结构先进后出,满足有效括号要求
创建一个对象,保存左括号对应的右括号值
遍历字符里出现的有效左括号,然会推入栈中
在遍历右括号时,从栈中从栈尾依次取出,若取出的不等于当前的右括号则不是有效括号
例如:当输入 "([)]",
栈中保存 [ )] ]
当遍历到 ')' 时,从栈中取出的为 ']' ,不相等,则不是有效括号
注意:
最后返回判断的是栈内是否为空数组,
因为可能有输入 '(' 然后栈内还有 ')'
*/
var isValid = function(s) {
if(!s) return true
let len = s.length
let str = {
'{':'}',
'(':')',
'[':']'
}
let stack = []
for(let i =0;i<len;i++){
const cur = s[i]
if(cur == '(' || cur == '[' || cur == '{') stack.push(str[cur])
else{
if(!stack.length || stack.pop() !== cur) return false // 若一开始就右括号,则直接为false
}
}
return !stack.length
};
题解思路:将递归转为迭代,以优化递归超时
任何递归都可以转为迭代以优化算法
递归解法(会超时) var climbStairs = function(n) { if(n<=2) return n return climbStairs(n-1) + climbStairs(n-2) }
/*
题解思路:将递归转为迭代,以优化递归超时
任何递归都可以转为迭代以优化算法
*/
/*
递归解法(会超时)
var climbStairs = function(n) {
if(n<=2) return n
return climbStairs(n-1) + climbStairs(n-2)
}
*/
// 第一种迭代中用变量保存前数值和
var climbStairs = function(n) {
let a=0,b=0,c=1
for(let i =0;i<n;i++){
a=b
b=c
c=a+b
}
return c
};
// 第二种将前数和保存在数组中
var climbStairs = function(n) {
if(n<=3) return n
const arr = new Array(n)
arr[1] = 1
arr[2] = 2
arr[3] = 3
for(let i = 4;i<=n;i++){
arr[i] = arr[i-1] + arr[i-2]
}
return arr[n]
};
题解思路:DFS 深度优先遍历字符,并动态保存途径的路径
/*
题解思路:DFS 深度优先遍历字符,并动态保存途径的路径
*/
var permute = function(nums) {
let path = [] // 保存途径的路径
let res = []
const dfs = (nums)=>{
if(path.length == nums.length){ // 当路径与数组长度相同时添加路径到结果中
res.push([...path])
return
}
for(let i =0;i<nums.length;i++){
if(path.includes(nums[i])) continue // 如果经过的路径就直接跳过
path.push(nums[i])
dfs(nums) // 递归找下一条路
path.pop() // 每次回溯时将经过的路出栈
}
}
dfs(nums)
return res
};
题解思路:动态规划
在原数组中维护i中最大值,为 i - 1 中与0相比最大的值
并维护最大的值max 与 i下标之间最大的值
/*
题解思路:动态规划
在原数组中维护i中最大值,为 i - 1 中与0相比最大的值
并维护最大的值max 与 i下标之间最大的值
*/
var maxSubArray = function(nums) {
let max = nums[0]
let len = nums.length
for(i=1;i<len;i++){
nums[i] += Math.max(nums[i-1],0)
max = Math.max(nums[i],max)
}
return max
};
题解思路:链表递归做法
// 递归到最后第2个节点,将它的节点指向null,它的前一个指向自己
var reverseList = function(head) {
if (head == null || head.next == null) {
return head;
}
const newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
};
// 维护前指针跟当前指针,并将当前指针指向前指针,最后头指针变为pre
var reverseList = function(head) {
if(!head) return head
let node = head
let pre = null,temp
while(node){
temp = node.next
node.next = pre
pre = node
node = temp
}
// 最后的头节点变为pre
return pre
};
题解思路:常规树解法
递归或者迭代
/*
题解思路:迭代法
*/
var hasPathSum = function (root, targetSum) {
if (!root) return false
let stack = [[root,root.val]]
while(stack.length){
let [node, sum] = stack.pop()
if(!node.left && !node.right && sum == targetSum) return true // 当前为叶子节点,并且结果 == targetSum
if(node.right) stack.push([node.right,node.right.val + sum])
if(node.left) stack.push([node.left,node.left.val + sum])
}
return false
};
/*
题解思路:递归法
*/
var hasPathSum = function (root, targetSum) {
if (!root) return false
let final = false
const dfs = (node, res) => {
if (!node.left && !node.right && res == targetSum) final = true // 当前为叶子节点,并且结果 == targetSum
if(node.left) dfs(node.left, res + node.left.val)
if(node.right) dfs(node.right, res + node.right.val)
}
dfs(root, root.val)
return final
};
题解思路:BFS广度优先搜索
/*
题解思路:BFS广度优先搜索
*/
var levelOrder = function(root) {
if(!root) return []
let count = 0 // 记录当前层级
let node = [root]
let res = []
while(node.length){
res[count] = [] // 初始化每层
let countNum = node.length // 每层的节点数
while(countNum--){
let temp = node.shift() // 推入每层的节点值
res[count].push(temp.val)
if(temp.left) node.push(temp.left)
if(temp.right) node.push(temp.right)
}
count++ // 每层遍历完,推进到下一层
}
return res
};
题解思路:快速排序取对应值
/*
题解思路:堆排序操作
*/
var findKthLargest = function(nums, k) {
var len
// 建立最大堆
function buildMaxHeap(arr){
len = arr.length
// 从非叶子节点开始调整
for(let i = Math.floor(len/2) ; i>=0;i--){
heapify(arr,i)
}
}
// 堆调整
function heapify(arr,i){
var left = 2*i+1
var right = 2*i+2
var largest = i
if(left < len && arr[left]>arr[largest]){
largest = left
}
if(right < len && arr[right]>arr[largest]){
largest = right
}
if(largest != i){
swap(arr,i,largest)
heapify(arr,largest)
}
}
function swap(arr,m,n){
var temp = arr[m]
arr[m] = arr[n]
arr[n] = temp
}
function heapSort(arr){
// 建立最大堆
buildMaxHeap(arr)
// 将堆顶元素放到末尾
for(let i = arr.length-1;i>0;i--){
swap(arr,0,i)
len--
// 每次交换完后调整堆
heapify(arr,0)
}
return arr
}
return heapSort(nums)[nums.length-k]
};
题解思路:排序+双指针
/*
题解思路:排序+双指针
*/
var threeSum = function(nums) {
let len = nums.length
if(len < 3 ) return []
let res = []
let right
let left
nums.sort((a,b)=>a-b) // 先将数组按升序排好
for(let i =0;i<len ;i++){
if(nums[i] > 0) break // 如果超过 0 的后面就都不可能满足三位和为0 则直接结束循环
if(i>0 && nums[i-1] === nums[i]) continue // 如果是重复的则跳过
left = i+1 // 当前指针的后一位
right = len - 1 // 数组的最后一位
while(left < right){ // 开始遍历
let sum = nums[i] + nums[left] + nums[right]
if(sum == 0){
res.push([nums[i],nums[left],nums[right]])
left++ // 将结果放进取后左指针推进一位,并判断是否有重复,重复就在推进一位
while(nums[left-1] == nums[left]) left++
}else if(sum > 0) right--
else left++
}
}
return res
};
题解思路:标记遍历过的链表
//解法一,标记遍历过的链表
var hasCycle = function(head) {
while(head){
if(head.flag) return true
head.flag = true
head = head.next
}
return false
};
//解法二,利用JSON不能转换嵌套循环对象
var hasCycle = function(head) {
try{
JSON.stringify(head)
return false
}catch{
return true
}
};
/*
手撕快排算法
*/
var sortArray = function(nums) {
if(nums.length <=1) return nums // 排序到1的时候返回
let index = Math.floor(nums.length/2) // 找中值
let pivot = nums.splice(index,1)[0] // 将数组的中值取出
let left = []
let right = []
for(let i =0;i<nums.length;i++){
if(nums[i] < pivot){
left.push(nums[i])
}else{
right.push(nums[i])
}
}
return [...sortArray(left),pivot,...sortArray(right)]
};
题解思路:dfs深度遍历,并携带路径相加
var sumNumbers = function(root) {
const dfs = (root,path = 0)=>{
if(!root) return 0
path = path * 10 + root.val // 路径值相加
if(!root.left && !root.right) return path // 到叶子节点将值返回
return dfs(root.left,path) + dfs(root.right,path) // 将所有叶子节点的值想加
}
return dfs(root) // 返回结果
};
var longestPalindrome = function(s) {
const ds = (m, n) => {
for( ; ; ){
if(m >= 0 && n < s.length & s[m] == s[n]){
m--;
n++;
}else{
break;
}
}
res = n - m - 1 > res.length ? s.slice(m + 1, n) : res;
}
if(s.length <= 1){
return s;
}
let res = "";
for(let i = 0; i < s.length; i++){
ds(i, i);
ds(i, i + 1);
}
return res;
};
var maxProfit = function(prices) {
// 创建保存数组中最小值
let min = prices[0]
// 记录最大差值
let max = 0
let len = prices.length
for(let i =1;i<len;i++){
// 在每次循环中记录最小的值
let cur = prices[i]
min = Math.min(cur,min)
// 如果当前值比最小值大则更新最大的差值
if(cur > min ) max = Math.max(cur - min,max)
}
return max
};
// 二分查找思路 每次都取一半开始找,并拿中值与目标值标记,若大了就左移,小了就右移
var search = function(nums, target) {
if(nums.indexOf(target)==-1) return -1
var low = 0
var high = nums.length-1
while(low<=high){
var mid = Math.floor((low+high)/2)
if(nums[mid] == target) {return mid}
else if(nums[mid] < target){
low = mid + 1
}else if(nums[mid] > target){
high = mid - 1
}
}
};
题解思路:双指针
先创建一个head新链表结构作为结果放回,
随后遍历list1,list2
将小的值放进新链表里,然后推进新链表与小的值
最后再将链表中剩下的值加进新链表里
/*
题解思路:双指针
先创建一个head新链表结构作为结果放回,
随后遍历list1,list2
将小的值放进新链表里,然后推进新链表与小的值
最后再将链表中剩下的值加进新链表里
*/
var mergeTwoLists = function(list1, list2) {
let head = new ListNode(-1) // 返回的结果链表
let preHead = head
while(list1 && list2){
if(list1.val < list2.val)
{
preHead.next = list1
list1 = list1.next
}
else{
preHead.next = list2
list2 = list2.next
}
preHead = preHead.next // 推进链表
}
preHead.next = list1 === null?list2:list1 // 将链表剩下的返回
return head.next
};
题解思路:循环一次缩减
将四个方向的数一次循环一遍,
每次循环往内缩
var spiralOrder = function (matrix) {
if (!matrix.length || !matrix[0].length) return []
let res = []
let row = matrix.length - 1, column = matrix[0].length -1
let left = 0, right = column, top = 0, bottom = row // 四个边界
while (left <= right && top <= bottom) { // 每一个数都遍历
for (let i = left; i <= right; i++) { // 从左到右边界的循环
res.push(matrix[top][i])
}
for (let i = top + 1; i <= bottom; i++) { // 从有边界到底的循环
res.push(matrix[i][right])
}
if (top < bottom && left < right) { // 满足两个条件才说明还有元素可遍历
for (let i = right - 1; i > left; i--) { // 从右到左不包括左边界
res.push(matrix[bottom][i])
}
for (let i = bottom; i > top; i--) { // 从底往上遍历不包括上边界
res.push(matrix[i][left])
}
}
[left, right, top, bottom] = [left + 1, right - 1, top + 1, bottom - 1] // 边界缩小,往内遍历
}
return res
};
题解思路:动态规划+两for循环
思路二:排序加二分查找
// 动态规划
var lengthOfLIS = function(nums) {
if(nums.length <= 1) return nums.length;
let dp = new Array(nums.length).fill(1); // 维持一个数组保存每个下标最大的数
let res = 0;
for(let i = 1;i<nums.length;i++){
for(let j = 0;j<i;j++){
if(nums[i]>nums[j]){
dp[i] = Math.max(dp[i],dp[j]+1) // 保持当前下标最大
}
}
if(dp[i]>res) res = dp[i]; // 保存最大结果
}
return res;
};
// 排序+二分
var lengthOfLIS = function(nums) {
const n = nums.length;
let len = 1;
if( n == 0) return 0;
const dp = new Array(n +1);
dp[len] = nums[0];
for(let i = 1;i < n;i++){
if(nums[i] > dp[len]) dp[++len] = nums[i];
else {
// 二分查找 pos 是比 num[i] 最小的数
let l = 1, r = len, pos = 0;
while(l <= r){
let mid = Math.floor((r - l) / 2) + l;
if(dp[mid] < nums[i]){
pos = mid;
l = mid + 1;
}else {r = mid -1 };
}
dp[pos + 1] = nums[i]
}
}
return len
};
// 迭代法
var inorderTraversal = function(root) {
if(!root) return []
let res = []
let stack = []
while(stack.length || root){
// 先遍历到所有左节点
while(root){
stack.push(root)
root = root.left
}
let temp = stack.pop()
res.push(temp.val)
// 再开始遍历右节点
root = temp.right
}
return res
};
// 递归法
var inorderTraversal = function(root,res = []) {
if(!root) return []
if(root.left) inorderTraversal(root.left,res)
res.push(root.val)
if(root.right) inorderTraversal(root.right,res)
return res
};
//递归加记录最大值
var maxDepth = function (root) {
let max = 0
const dfs = (root,res = 1) => {
if (!root) return
if(!root.left && !root.right) max = Math.max(res, max)
if (root.left) dfs(root.left, res+1)
if (root.right) dfs(root.right, res+1)
}
dfs(root)
return max
};
题解思路:遍历到1的,将它的上下左右的1,全变为0
var numIslands = function(grid) {
let row = grid.length
let column = grid[0].length
let count = 0
for(let i =0;i<row;i++){
for(let j=0;j<column;j++){
if(grid[i][j] == '1'){
count++
f(i,j)
}
}
}
function f(row,column){
// 遍历下
if(grid[row+1] && grid[row+1][column] == '1'){
//将遍历到的 1 填为0 防止重复
grid[row+1][column] = '0'
f(row+1,column)
}
// 遍历上
if(grid[row-1] && grid[row-1][column] == '1'){
grid[row-1][column] = '0'
f(row-1,column)
}
// 遍历左
if(grid[row][column-1] && grid[row][column-1] == '1'){
grid[row][column-1] = '0'
f(row,column-1)
}
// 遍历右
if(grid[row][column+1] && grid[row][column+1] == '1'){
grid[row][column+1] = '0'
f(row,column+1)
}
}
return count
};
题解思路:动态规划
var coinChange = function(coins, amount) {
if(amount == 0) return 0
let dp = new Array(amount+1).fill(Infinity)
dp[0] = 0
let dpLen = dp.length
let coLen = coins.length
// 动态规划每个数字对应的最优解
for(let i=0;i<dpLen;i++){
for(let j=0;j<coLen;j++){
if( i - coins[j] < 0 ) continue
dp[i] = Math.min(dp[i],dp[ i - coins[j] ]+1 )
}
}
return dp[amount] == Infinity?-1:dp[amount]
};
var getKthFromEnd = function(head, k) {
var node = head
var index = 0
while(node){
node = node.next
index++
}
for(let i =0;i<index-k;i++){
head = head.next
}
return head
};
/*
dfs深度遍历,结果满足长度等于n*2即添加
*/
var generateParenthesis = function (n) {
let res = [];
// cur :当前字符 left:当前字符左括号 right:当前字符右括号
const help = (cur, left, right) => {
if (cur.length === 2 * n) {
res.push(cur);
return;
}
if (left < n) {
help(cur + "(", left + 1, right)
}
if (right < left) {
help(cur + ")", left, right + 1);
}
};
help("", 0, 0);
return res;
};
var findLength = function (A, B) {
let maxLength = 0;
for (let i = 0; i < A.length; i++) {
if (maxLength >= Math.min(A.length - i, B.length))
break;
let len = 0;
for (let j = 0; j < Math.min(A.length - i, B.length); j++) {
if (A[i + j] != B[j])
continue;
let index = 1;
while (i + j + index < A.length &&
j + index < B.length &&
A[i + j + index] == B[j + index]) {
index++;
}
len = Math.max(len, index);
j = j + index;
}
maxLength = Math.max(maxLength, len);
}
for (let i = 1; i < B.length; i++) {
if (maxLength >= Math.min(B.length - i, A.length))
break;
let len = 0;
for (let j = 0; j < Math.min(B.length - i, A.length); j++) {
if (B[i + j] != A[j])
continue;
let index = 1;
while (i + j + index < B.length &&
j + index < A.length &&
B[i + j + index] == A[j + index]) {
index++;
}
len = Math.max(len, index);
j = j + index;
}
maxLength = Math.max(maxLength, len);
}
return maxLength;
};
// 递归法
var preorderTraversal = function(root,res=[]) {
if(!root) return []
res.push(root.val)
preorderTraversal(root.left,res)
preorderTraversal(root.right,res)
return res
};
// 迭代法
var preorderTraversal = function(root) {
if(!root) return []
let res = []
let queue = [root]
while(queue.length){
let temp = queue.pop()
res.push(temp.val)
if(temp.right) queue.push(temp.right)
if(temp.left) queue.push(temp.left)
}
return res
};
// 题解思路,先排序再进行比较
var merge = function(intervals) {
intervals.sort((a,b)=>a[0] - b[0]) // 将数组区间,按首数字进行排序
let res = [intervals[0]] // 初始化比较数组
let len = intervals.length
for(let i =1;i<len;i++){
if(res[res.length - 1][1] >= intervals[i][0] ){ // 当结果数组数组区间的最后一位数,在比较数组的区间内
res[res.length - 1][1] = Math.max(res[res.length - 1][1],intervals[i][1]) // 将结果数组数组区间的最后一位数换成最大的那个
}else{
res.push(intervals[i]) // 不然就添加到结果中
}
}
return res
};