0%

0146. LRU Cache

难度: Medium

刷题内容

原题链接

内容描述

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

The cache is initialized with a positive capacity.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

1
2
3
4
5
6
7
8
9
10
11
LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4

解题方案

思路1

**- 时间复杂度: O(1)**- 空间复杂度: O(N)**

这是一道数据结构的考题。

简单的获取和插入,顺序不做考虑,所以数据结构用Object即可。

但是重要的考点——LRU,也就是删除最近没有使用的数据。所以想到用队列存在key列表:

  • 未超过存储上限时,每次put一个新数据时,向队列末尾插入当前key
  • 每次get时,如果key存在,则将对应的key从的队列中,移到对末尾
  • 在超过存储上限时,如进行put操作,则将队列首位删除掉

执行用时 :492 ms, 在所有 JavaScript 提交中击败了35.29%的用户

内存消耗 :59.7 MB, 在所有 JavaScript 提交中击败了16.36%的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* @param {number} capacity
*/
var LRUCache = function(capacity) {
this.limit = capacity || 2
this.storage = {}
this.keyList = []
};

/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function(key) {
if (this.storage.hasOwnProperty(key)) {
let index = this.keyList.findIndex(k => k === key)
this.keyList.splice(index, 1)
this.keyList.push(key)
return this.storage[key]
} else {
return -1
}
};

/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function(key, value) {
// 判断容量
if (this.keyList.length >= this.limit && !this.storage.hasOwnProperty(key)) {
this.deleteLRU()
}

// 存储数据
this.updateKeyList(key)
this.storage[key] = value
};

LRUCache.prototype.deleteLRU = function () {
delete this.storage[this.keyList.shift()]
}

LRUCache.prototype.updateKeyList = function (key) {
if (this.storage.hasOwnProperty(key)) {
var index = this.keyList.findIndex(k => key === k)
this.keyList.splice(index, 1)
}
this.keyList.push(key)
}

/**
* Your LRUCache object will be instantiated and called as such:
* var obj = new LRUCache(capacity)
* var param_1 = obj.get(key)
* obj.put(key,value)
*/


167. Two Sum II - Input array is sorted

难度: Easy

刷题内容

原题连接

内容描述

1
2
3
4
5
6
7
8
9
10
11
12
13
Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2.

Note:

Your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution and you may not use the same element twice.
Example:

Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore index1 = 1, index2 = 2.

解题方案

思路 1
**- 时间复杂度: O(log2 N)**- 空间复杂度: O(n)**

  • 使用贪心算法,用左右的数字开始加,如果数字大于目标right–,反之left++

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @param {number[]} numbers
* @param {number} target
* @return {number[]}
*/
var twoSum = function(numbers, target) {
if(numbers.length===0)
return [];
var left = 0,right = numbers.length-1;
while(right>left){
if(numbers[right]+numbers[left]>target)
right--;
else if(numbers[right]+numbers[left]<target)
left++;
else return [left+1,right+1];
}
return [];
};

167. Two Sum II - Input array is sorted

难度: Easy

刷题内容

原题连接

内容描述

1
2
3
4
5
6
7
8
9
10
11
12
13
Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2.

Note:

Your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution and you may not use the same element twice.
Example:

Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore index1 = 1, index2 = 2.

解题方案

思路 1
**- 时间复杂度: O(N)**- 空间复杂度: O(N)**

算法来源于知乎文章——趣味算法思想

  • 由于数组是有序的,所以可以从两边向中间逐渐收敛地进行查找,好处在于避免了双重循环
  • 如果最两端的和小于目标数,则可以让左侧下标+1,然后重新进行运算比较
  • 如果最两端的和大于目标数,则可以让右侧下标-1,然后重新进行运算比较

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @param {number[]} numbers
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
var number=[];
var left = 0;
var right = nums.length - 1 ;
while(left < right ) {
if(nums[left] + nums[right] === target) {
return [left+1, right+1]
} else if(nums[left] + nums[right] > target ) {
right--;
} else {
left++;
}
}
};

0171. Excel Sheet Column Number

难度: Easy

刷题内容

原题连接

内容描述

给定一个Excel表格中的列名称,返回其相应的列序号。

例如,

 A -> 1
 B -> 2
 C -> 3
 ...
 Z -> 26
 AA -> 27
 AB -> 28 
 ...
 

示例 1:

 输入: "A"
 输出: 1

示例 2:

 输入: "AB"
 输出: 28

示例 3:

 输入: "ZY"
 输出: 701

解题方案

思路 1
**- 时间复杂度: O(N)**- 空间复杂度: O(1)**

利用parseInt可以按照进制来解析的特性,可以将英文字母轻松转换成数字

代码:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @param {string} s
* @return {number}
*/
var titleToNumber = function(s) {
if (!s) {
return 0
}
let list = s.split('')
list = list.map((alpha, index) => (parseInt(alpha, 36) - 9) * Math.pow(26, list.length - index - 1))
return list.reduce((acc, cur) => (acc + cur), 0)
}

0179. Largest Number

难度: Medium

刷题内容

原题连接

内容描述

给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。

示例 1:

 输入: [10,2]
 输出: 210

示例 2:

 输入: [3,30,34,5,9]
 输出: 9534330
 

说明: 输出结果可能非常大,所以你需要返回一个字符串而不是整数。

解题方案

思路 1
**- 时间复杂度: O(N)**- 空间复杂度: O(1)**

排序时,计算两个数字谁在前面组成的数字比较大即可

代码:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @param {number[]} nums
* @return {string}
*/
var largestNumber = function(nums) {
if (nums.every(n => !n)) {
return '0'
}
return nums.sort((a, b) => {
return Number('' + b + a) - Number('' + a + b)
}).join('')
};

0198. House Robber

难度: Easy

刷题内容

原题连接

内容描述

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例 1:

 输入: [1,2,3,1]
 输出: 4
 解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
      偷窃到的最高金额 = 1 + 3 = 4 。
 
 

示例 2:

 输入: [2,7,9,3,1]
 输出: 12
 解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
      偷窃到的最高金额 = 2 + 9 + 1 = 12 。
 

解题方案

思路
**- 时间复杂度: O(N)**- 空间复杂度: O(N)**

DP动态规划

一次循环,循环到每个房间时都做一个选择:偷?还是不偷?

  • 偷:那么当前最大的收益(DP[i]) 为 DP[i-2] + Vi,其中为Vi代表当前房间的价值

  • 不偷:那么当前最大的收益(DP[i])为DP[i-1]

    所以DP[i] = Max(DP[i-2] + Vi, DP[i-1])

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
let dp = []
if(!nums.length) {
return 0
}
nums.forEach((n, i) => {
dp[i] = Math.max((dp[i-2] || 0) + n, (dp[i-1] || 0))
})
return dp[nums.length - 1]
};

203. Remove Linked List Elements

难度: Easy

刷题内容

原题连接

内容描述

删除链表中等于给定值 val 的所有节点。

示例:

1
2
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

解题方案

思路 1
- 时间复杂度: O(3N)

- 空间复杂度: O(2N)

暴力解法

  1. 将链表转化为数组
  2. 对数组进行过滤
  3. 将过滤后的数组重新转化为数组

执行用时 :160 ms, 在所有 JavaScript 提交中击败了**10.71%**的用户

内存消耗 :38.4 MB, 在所有 JavaScript 提交中击败了**5.13%**的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @param {number} val
* @return {ListNode}
*/
var removeElements = function(head, val) {
if (!head) {
return head
}
let array = listNodeToArray(head)
array = array.filter(i => (i !== val))
return arrayToListNode(array)
};


function listNodeToArray (head) {
let array = []
while (head) {
array.push(head.val)
head = head.next
}
return array
}

function arrayToListNode(array) {
if(!array || !array.length) {
return null
}

let node
let head = new ListNode(array[0])
let pnode = head //pnode变量用来保存前一个节点

for(let i = 1; i < array.length; i++) {
node = new ListNode(array[i])
pnode.next = node //将前一个节点的next指向当前节点
pnode = node //将node赋值给pnode
}

return head
}

思路 2
- 时间复杂度: O(N)

- 空间复杂度: O(1)

快慢指针

  1. 快指针(head)每次循环都向前移动一个
  2. 慢指针(slow)只有在快指针当前节点的值不等于给定值时,才会向前移动,并在此之前将快指针当前节点指向慢指针的next

执行用时 :108 ms, 在所有 JavaScript 提交中击败了**77.37%**的用户

内存消耗 :37.9 MB, 在所有 JavaScript 提交中击败了**11.11%**的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @param {number} val
* @return {ListNode}
*/
var removeElements = function(head, val) {
if (!head) {
return head
}
let slow = new ListNode()
let result = slow
while (head) {
if (head.val !== val) {
slow.next = head
slow = slow.next
} else if (!head.next) {
slow.next = null
}
head = head.next
}
return result.next
};


206. Reverse Linked List(反转链表)

难度: Easy

刷题内容

原题连接

内容描述

反转一个单链表。

示例:

1
2
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

解题方案

  • 思路1 :暴力解法

将链表转化为数组,再利用数组重建数组。

思路
- 时间复杂度: O(N²)

- 空间复杂度: O(N)

执行用时 :96 ms, 在所有 JavaScript 提交中击败了**54.77%**的用户

内存消耗 :35.2 MB, 在所有 JavaScript 提交中击败了**30.19%**的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
var nodeList = []
if (!head) {
return head
}
while (head.next) {
nodeList.push(head)
head = head.next
}
nodeList.push(head)
nodeList.forEach((node, index) => {
if (nodeList[index - 1]) {
node.next = nodeList[index - 1]
} else {
node.next = null
}
})
return nodeList[nodeList.length - 1]
};

  • 思路2: 迭代法

使用parentNode缓存上次循环的结果,每次循环都生成两个新的ListNode用来翻转链表各个元素,一次迭代即可完成链表反转。

思路
- 时间复杂度: O(N)

- 空间复杂度: O(1)

执行用时 :116 ms, 在所有 JavaScript 提交中击败了**20.19%**的用户

内存消耗 :35.5 MB, 在所有 JavaScript 提交中击败了**14.78%**的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
if (!head) {
return head
}
let parentNode = null
while (head) {
let current = new ListNode(head.val)
current.next = parentNode
if (head.next) {
let next = new ListNode(head.next.val)
next.next = current
} else {
let next = null
return current
}
parentNode = current
head = head.next
}
return head
};

209. Minimum Size Subarray Sum

难度: Medium

刷题内容

原题连接

内容描述

1
2
3
4
5
6
7
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead.

Example:

Input: s = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: the subarray [4,3] has the minimal length under the problem constraint.

解题方案

思路 1
**- 时间复杂度: O(N)**- 空间复杂度: O(N)**

算法来源于知乎文章——趣味算法思想

  • 这里可以看到由于需要找连续的子数组,所以依旧可以设置两个指针,往同一方向移动。
  • 如果两个指针中间的值加起来>sum的时候,记录此时数组的长度,接着左指针移动,减小sum的值 ;
  • 如果< sum的话,右指针移动扩大范围。
  • 最后返回最短的长度值。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* @param {number} s
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function(s, nums) {
var left = 0;
var right = -1; // right 的起始位置很重要,这里选择-1 [left, right]这个区间刚开始是没有值的
var tmpSum = 0;
var minLength;

// 循环停止的条件是左指针小于长度
while (left < nums.length - 1) {
if(tmpSum < s) {
// 这里要注意边界的处理,当右指针移动到最后一个元素的时候结束
if(right >= nums.length -1) {
return minLength || 0;
}
right ++;
// 这里tmpSum的计算也很巧妙,直接用累加的方式,节省计算量
tmpSum = tmpSum + nums[right]
} else {
var tmp = right - left + 1;
if(minLength) {
if(tmp < minLength) {
minLength = tmp;
}
} else {
minLength = tmp;
}
// 左边指针移动减少sum的值
tmpSum = tmpSum - nums[left];
left ++;
}
}
if(!minLength) {
return 0;
}
return minLength;
};

258. Add Digits

难度: Easy

刷题内容

原题连接

内容描述

1
2
3
4
5
6
7
8
9
10
11
Given a non-negative integer num, repeatedly add all its digits until the result has only one digit.

Example:

Input: 38
Output: 2
Explanation: The process is like: 3 + 8 = 11, 1 + 1 = 2.
Since 2 has only one digit, return it.

Follow up:
Could you do it without any loop/recursion in O(1) runtime?

解题方案

思路 1
**- 时间复杂度: O(1)**- 空间复杂度: O(1)**

  • 原理:

  • 假设输入的数字是一个5位数字num,则num的各位分别为a、b、c、d、e

  • 有如下关系:num = a * 10000 + b * 1000 + c * 100 + d * 10 + e

  • 即:num = (a + b + c + d + e) + (a * 9999 + b * 999 + c * 99 + d * 9)

  • 因为 a * 9999 + b * 999 + c * 99 + d * 9 一定可以被9整除,因此num模除9的结果与 a + b + c + d + e 模除9的结果是一样的。

  • 对数字 a + b + c + d + e 反复执行同类操作,最后的结果就是一个 1-9 的数字加上一串数字,最左边的数字是 1-9 之间的,右侧的数字永远都是可以被9整除的。

  • 这道题最后的目标,就是不断将各位相加,相加到最后,当结果小于10时返回。因为最后结果在1-9之间,得到9之后将不会再对各位进行相加,因此不会出现结果为0的情况。

  • 因为 (x + y) % z = (x % z + y % z) % z,又因为 x % z % z = x % z,因此结果为 (num - 1) % 9 + 1,只模除9一次,并将模除后的结果加一返回。

    代码:

1
2
3
4
5
6
7
8
9
10
11
/**
* @param {number} num
* @return {number}
*/
var addDigits = function(num) {
if(num==0)
return num;
if(num%9==0)
return 9;
else return num%9;
};