楔子 -- 青蛙跳臺階
一只青蛙一次可以跳上一級臺階,也可以跳上二級臺階,求該青蛙跳上一個n級的臺階總共需要多少種跳法。
分析: 當n=1的時候,①只需要跳一次即可;只有一種跳法,即f(1)=1;
當n=2的時候,①可以先跳一級再跳一級,②也可以直接跳倆級;共有倆種跳法,即f(2)=2;
當n=3的時候,①一階一階跳即可;
②他可以從一級臺階上跳倆階上來
③也可從二級臺階上跳一階上來;即共有f(3)=f(2)+f(1);
所以當有n階臺階時,且當n>2的時候,根據上面式子可推導出→ f(n)=f(n-1)+f(n-2)
圖片
所以很直白的看出就是個 斐波那契數列 ,有一點不同的是,斐波那契數列從1,1,2,3,5,8,13…開始的,而我們這是以1,2,3,5,8,13…開始的,少了前面的1
什么是算法
解決一系列問題的具體步驟
算法是一組用于解決特定問題或執行特定任務的有限步驟序列。這些步驟按照確定的順序執行,以達到所需的結果。在計算機科學中,算法通常用于描述數據處理、自動化處理和數學運算的過程。算法可以用來解決各種問題,包括排序、搜索、路徑規劃等。
算法實例 :實現一個LRU緩存
LRU是Least Recently Used(最近最少使用)的縮寫。
在計算機科學中,LRU是一種頁面置換算法,通常用于操作系統的虛擬內存管理中。
該算法根據頁面(或者其他資源)的最近使用情況來進行置換,當需要置換時,選擇最近最少被使用的頁面進行替換。
這樣可以保證最常用的頁面保留在內存中,從而提高性能。
實例:vue keep-alive 緩存
LRU -- 最近使用
實現 LRUCache
緩存,有一個大小 const lru = new LRUCache(capacity)
提示
const lru = new LRUCache(2)
lru.put(1,1)
lru.put(2,2) // {1:1,2:2}
lru.get(1)
lru.put(3,3) // {1:1,3:3}
lru.get(2) // -1 (找不到)
lru.put(4,4) // {4:4,3:3}
實現代碼
// 函數的 get 和 put 必須以 O(1)的時間復雜度運行
// get,得是個hash(Map,Set),而不能是數組
// ES6 迭代器
const LRUCache = function(capacity){
this.cacheQueue = new Map()
this.capacity = capacity
}
LRUCache.prototype.get = function(key){
if(this.cacheQueue.has(key)){ // 如果找到了,這個key對應的value要提升新鮮度
const result = this.cacheQueue.get(key)
// 刪掉,然后再放進去,這樣,這個就能在cacheQueue的隊尾(隊尾為最新鮮地方)
this.cacheQueue.delete(key)
this.cacheQueue.set(key,result)
return result
}
return -1 // 如果沒有找到key,則直接返回 -1
}
LRUCache.prototype.put = function(key,value){
if(this.cacheQueue.has(key)){
// 如果找到了, 刪掉
this.cacheQueue.delete(key)
}
if(this.cacheQueue.size >= this.capacity){
// 刪除map的第一個元素,即最長時間未使用的
this.cacheQueue.set(key,value)
this.cacheQueue.delete(this.cacheQueue.keys().next().value)
} else {
this.cacheQueue.set(key,value)
}
}
擴展:ES6 Map
ES6的Map是一種內置的數據結構,它存儲鍵值對并允許你通過鍵快速查找值。
Map對象類似于對象,但不同之處在于Map的鍵可以是任意數據類型,而對象的鍵只能是字符串或Symbol。
Map還保留了插入順序,這意味著當你遍歷一個Map對象時,它會按照插入順序返回鍵值對。
Map的創建和初始化:
要創建一個新的Map,你需要使用new Map()語法。
例如:
const myMap = new Map();
添加鍵值對:
你可以使用Map對象的set()方法添加鍵值對。
例如:
myMap.set('key1', 'value1');
myMap.set('key2', 'value2');
獲取鍵值對:
你可以使用Map對象的get()方法獲取鍵對應的值。
例如:
const value1 = myMap.get('key1'); // 返回 'value1'
const value2 = myMap.get('key2'); // 返回 'value2'
檢查Map中是否存在某個鍵:
你可以使用Map對象的has()方法檢查Map中是否存在某個鍵。
例如:
const hasKey1 = myMap.has('key1'); // 返回 true
const hasKey3 = myMap.has('key3'); // 返回 false
刪除鍵值對:
你可以使用Map對象的delete()方法刪除鍵值對。
例如:
myMap.delete('key1');
遍歷Map:
你可以使用Map對象的keys()、values()或entries()方法遍歷Map中的鍵或值。
例如:
for (const key of myMap.keys()) {
console.log(key); // 輸出鍵名
}
for (const value of myMap.values()) {
console.log(value); // 輸出值
}
for (const [key, value] of myMap.entries()) {
console.log(key, value); // 輸出鍵名和值
}
myMap.forEach((value, key) => {
console.log(key + ' = ' + value);
});
獲取Map的大小:
myMap.size; // 返回Map的大小
更多詳細內容,請微信搜索“前端愛好者“, 戳我 查看 。
算法實例 :求環狀鏈表
leecode的地址:https://leetcode.cn/problems/linked-list-cycle/
鏈表:常見 -- react源碼
圖片
思路:快慢指針,兩個(起點相同位置)指針,n步驟以后指針相遇
圖片
實現代碼
head.next 指向下一個值
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
let fast = slow = head
while(fast && fast.next){
fast = fast.next.next
slow = slow.next
if(fast === slow){
return true
}
}
return false
};
擴展:鏈表
鏈表是一種數據結構,其中的元素以節點的形式按順序排列。
每個節點都包含數據和指向下一個節點的引用。
相比數組,鏈表在插入和刪除元素時更靈活,因為它們不需要連續的存儲空間。
舉例來說,一個簡單的鏈表可以被定義如下:
Node 1: 23 -> Node 2
Node 2: 47 -> Node 3
Node 3: 95 -> null
在這個例子中,鏈表中的每個節點包含一個值和指向下一個節點的引用。
第一個節點包含值23,同時指向第二個節點,依此類推。最后一個節點指向null,表示鏈表的結束。
通過使用鏈表,我們可以輕松地插入或刪除節點,而無需移動其他節點。
這使得鏈表在某些場景下比數組更為適用。
鏈表是一種物理存儲結構上 非連續、非順序 的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。
鏈表由一系列結點(鏈表中每一個元素稱為結點)組成,結點可以在運行時動態生成。
每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。
鏈表有很多種不同的類型,包括單向鏈表、雙向鏈表以及循環鏈表。
鏈表可以在多種編程語言中實現,例如C、C++和JAVA等。
算法實例 :二又樹的前序、中序、后序遍歷
圖片
前序遍歷 - 根左右
先走根節點,然后左,然后右
圖片
中序遍歷 - 左根右
先走左節點,然后根節點,然后右節點
圖片
后序遍歷 - 左右根
先走左節點,然后右節點,然后再根節點
圖片
舉例
圖片
前/先序遍歷: GDAFEMHZ 中序遍歷: ADEFGHMZ 后序遍歷: AEFDHZMG
圖片
前序遍歷: A-B-D-F-G-H-I-E-C 中序遍歷: F-D-H-G-I-B-E-A-C 后序遍歷: F-H-I-G-D-E-B-C-A
實現代碼
const treeRoot = {
val: 1,
left: {
val: 2,
left: {
val: 4,
},
right: {
val: 5,
}
},
right: {
val: 3,
left: {
val: 6,
},
right: {
val: 7,
}
}
}
// 前序
const preOrder = function(node){
if(node){
// 如果有根節點
console.log(node.val)
preOrder(node.left)
preOrder(node.right)
}
}
preOrder(treeRoot)
// 中序
const inOrder = function(node){
if(node){
// 如果有根節點
inOrder(node.left)
console.log(node.val)
inOrder(node.right)
}
}
inOrder(treeRoot)
// 后序
const nextOrder = function(node){
if(node){
// 如果有根節點
nextOrder(node.left)
nextOrder(node.right)
console.log(node.val)
}
}
nextOrder(treeRoot)
(二叉樹)層序遍歷
leetcode地址:https://leetcode.cn/problems/binary-tree-level-order-traversal/
圖片
輸入:root = [3,9,20,null,null,15,7] 輸出:[[3],[9,20],[15,7]]
/**
* @param {TreeNode} root
* @return {number[][]}
*/
var levelOrder = function(root) {
if(!root) return []
let queue = [root]
let result = []
// 開始遍歷
while(queue.length){
let temQueue = []
let temResult = []
let len = queue.length
for(let i = 0; i < len; i++){
let node = queue.shift()
temResult.push(node.val)
node.left && temQueue.push(node.left)
node.right && temQueue.push(node.right)
}
// 循環完畢
result.push(temResult)
temResult = []
queue = temQueue
}
return result
};
(二叉樹)的層級
leetcode地址:https://leetcode.cn/problems/maximum-depth-of-binary-tree/
直接return 上面層序的lengh
/**
* @param {TreeNode} root
* @return {number[][]}
*/
var maxDepth = function(root) {
if(!root) return []
let queue = [root]
let result = []
// 開始遍歷
while(queue.length){
let temQueue = []
let temResult = []
let len = queue.length
for(let i = 0; i < len; i++){
let node = queue.shift()
temResult.push(node.val)
node.left && temQueue.push(node.left)
node.right && temQueue.push(node.right)
}
// 循環完畢
result.push(temResult)
temResult = []
queue = temQueue
}
return result.length
};
使用DP方法
let maxDepth = function(root){
if(!root) return 0 // 如果沒有根節點,則直接返回0
// 找左右叉的最大值
return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1
}
類數組轉數組有多少種方法
const arrayLike = document.querySelectorAll('div')
圖片
擴展運算符
[...arrayLike]
prototype
Array.prototype.slice.call(arrayLike)
Array.prototype.concat.Apply([],arrayLike)
Array.from
Array.from(arrayLike)
Array.apply
Array.apply(null,arrayLike)
擴展:數組常用哪些方法?
數組常用方法很多,主要包括以下幾個:
- 1. find():返回數組中符合測試函數條件的第一個元素值。
const numbers = [10, 20, 30, 40];
const result = numbers.find(num => num > 25);
// 結果為30
- 2. findIndex():返回數組中符合測試函數條件的第一個元素索引。
const numbers = [10, 20, 30, 40];
const index = numbers.findIndex(num => num > 25);
// 結果為2(即元素30對應的索引)
- 3. forEach():對數組的每個元素執行提供的函數。
const numbers = [1, 2, 3];
numbers.forEach(num => console.log(num * 2));
// 輸出2, 4, 6
- 4. map():對數組的每個元素執行提供的函數,并返回結果組成的新數組。
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);
// 結果為[2, 4, 6]
- 5. filter():使用給定函數測試所有元素,并返回由所有通過測試的元素組成的新數組。
const numbers = [10, 15, 20, 25, 30];
const greaterThan20 = numbers.filter(num => num > 20);
// 結果為[25, 30]
這些方法在處理數組時非常有用,并且能夠簡化一些常見的操作。