位運算知識點java
12>>1 //6 a/2 等價爲 a>>1node
中間數 (L+R)/2 會出現溢出(溢出的意思就是超過了二進制)算法
L+(R-L)/2 最終改爲 l+((r-l)>>1)api
const smallSum = arr => { if (arr == null || arr.length < 2) { return 0; } return mergeSort(arr, 0, arr.length - 1) } const mergeSort = (arr, l, r) => { if (l == r) { return 0; } // let mid = Math.floor((l + r) / 2) let mid = l+((r-l)>>1) return mergeSort(arr, l, mid) + mergeSort(arr, mid + 1, r) + merge(arr, l, mid, r) } const merge = (arr, l, m, r) => { let help = []; let i = 0; let p1 = l; let p2 = m + 1; let res=0; while (p1 <= m && p2 <= r) { //若是左邊小於右邊,r開始到p2的個數*p1 //簡單理解成 p1<p2 重複的加在一塊兒 res+=arr[p1]<arr[p2]?(r-p2+1)*arr[p1]:0; help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++] } while (p1 <= m) { help[i+1]=arr[p1++] } while (p2 <= r) { help[i++]=arr[p2++] } for (let j = 0; j < help.length; j++) { arr[l + j] = help[j] } return res } console.log(smallSum([1, 2, 3]))
遞歸算法其實是一種分而治之的方法,它把複雜問題分解爲簡單問題來求解。對於某些複雜問題(例如hanio塔問題),遞歸算法是一種天然且合乎邏輯的解決問題的方式,可是遞歸算法的執行效率一般比較差。所以,在求解某些問題時,常採用遞歸算法來分析問題,用非遞歸算法來求解問題數組
,遞歸會出問題的話,循環也必定會出問題,只不過遞歸是出了問題才告訴你,而循環則在執行前就能夠知道有問題函數
循環和遞歸有種逆向思惟關係, 循環一般來自底向上, 遞歸自頂向下。ui
將數組轉化成二叉樹this
左節點
2*i+1
右節點2*i+2
父節點 (i-1)/2.net大根堆=>就是徹底二叉樹3d
// 堆 let len; //數組長度 //創建大堆頂 function builddMaxHeap(arr) { len = arr.length; for (let i = Math.floor(len / 2); i >= 0; i--) { heapify(arr, i) } } //堆調整 const heapify = (arr, i) => { let left = 2 * i + 1, right = 2 * i + 2, 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, i, j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } //排序 function heapSort(arr) { builddMaxHeap(arr) for (let i = arr.length-1; i >0 ; i--) { //0 i>0 最後一個和0交換 swap(arr, 0, i) len--; //0從新被排到最後 heapify(arr,0) } return arr; }
let arr=[ {name:'張三',age:122,height:423}, {name:'張三',age:14,height:223}, {name:'張三',age:16,height:123}, ] console.log(arr.sort((a, b) => a.age - b.age))
回型打印
let arr=[ [1,2,3,4], [1,2,3,4], [1,2,3,4], [1,2,3,4] ] const spiralOrder=(arr)=>{ let x1=0; let y1=0; let x2=arr.length-1; let y2=arr[0].length-1; //這個代碼是直接找外層循環後再找內層循環 while (x1 <= x2 && y1 <= y2) { printEdge(arr,x1++,y1++,x2--,y2--) } //下面這層代碼是直接找外層循環 //printEdge(arr,x1,y1,x2,y2) } const printEdge = (arr,x1, y1, x2, y2) => { // x軸相等 // 0 0 0 3 if(x1==x2){ for (let i = y1; i <=y2 ; i++) { // [0][0] [0][1] [0][2] [0][3] console.log(arr[x1][i]) } //y軸相等 //0 0 3 0 }else if (y1 == y2) { for (let i = x1; i <=x2 ; i++) { console.log(arr[i][y1]) } }else{ let cy1=y1; let cx1=x1; while (cy1 != y2) { //(0,0) (0,1) (0,2) console.log(arr[x1][cy1]) cy1++ } while (cx1 != x2) { //(0,3)(1,3)(2,3) console.log(arr[cx1][y2]) cx1++ } while (cy1 != y1) { //(3,3)(3,2)(3,1) console.log(arr[x2][cy1]) cy1-- } while (cx1 != x1) { //(3,0)(2,0)(1,0) console.log(arr[cx1][y1]) cx1-- } } } spiralOrder(arr)
宏觀基礎
一行是一橫行
一列是一縱向
//雖然我懂了,可是我被這個行呀,列呀搞糊塗了
/** * 將 AB連線上的元素打印出來 * @param {要打印的矩陣} m * @param {A的橫座標} x1 * @param {A的縱座標} y1 * @param {B的橫座標} x2 * @param {B的縱座標} y2 * @param {打印方向} f */ printMatrizIGZag=(arr) =>{ let x1 = 0; let y1 = 0; let x2 = 0; let y2 = 0; let enx2 = arr.length - 1, eny2 = arr[0].length - 1; let fromUp = false; // 判斷條件:AB走到最後即結束循環 while (x1 != enx2 + 1) { printLevel(arr, x1, y1, x2, y2, fromUp); x1 = y1 == eny2 ? x1 + 1 : x1; y1 = y1 == eny2 ? y1 : y1 + 1; y2 = x2 == enx2 ? y2 + 1 : y2; x2 = x2 == enx2 ? x2 : x2 + 1; fromUp = !fromUp; } } printLevel=(m, x1, y1, x2, y2, f)=> { if (f) { while (x1 != x2 + 1) { console.log(m[x1++][y1--]) } } else { while (x2 != x1 - 1) { console.log(m[x2--][y2++]) } } } let arr = [ [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4] ] printMatrizIGZag(arr)
鏈表是由一系列的節點組成的集合,每一個節點都使用一個對象的引用指向他的後繼,指向另外一個節點的引用叫鏈
有點麻煩,先放放
定義一個初始化的二叉樹
var nodes = { node: 6, left: { node: 5, left: { node: 4 }, right: { node: 3 } }, right: { node: 2, right: { node: 1 } } } /* * 6 * 5 2 * 4 3 1 * */
先序遍歷
遞歸版
- 若二叉樹爲空,則算法結束,不然:
- 訪問根節點
- 前序遍歷根節點的左子樹
- 前序遍歷根節點的右子樹
let result = []; const dfs = nodes => { if (nodes.node) { result.push(nodes.node) //先遞歸添加全部的左節點 nodes.left && dfs(nodes.left) //再遞歸添加全部的右節點 nodes.right && dfs(nodes.right) } } dfs(nodes) console.log(result) // [6, 5, 4, 3, 2, 1]非遞歸版
- 初始化一個棧,將根節點壓入棧中
- 先判斷右節點有沒有,有就入棧,再判斷左節點有沒有,有就入棧
- 而後再出棧(pop), 先出左節點,再出右節點
var dfs = function(nodes) { var result = [] var stack = [] stack.push(nodes) while (stack.length) { var item = stack.pop() result.push(item.node) item.right && stack.push(item.right) item.left && stack.push(item.left) } return result } console.log(dfs(nodes)) // [6, 5, 4, 3, 2, 1]
中序遍歷
左 中 右
遞歸版
- 先入棧6,5,4 出棧4,5,6再5節點的時候由於有右節點3,先入棧,添加到數組中,因此是4,5,3,6
- 再右節點入棧的時候,由於入棧一個就添加到數組中,因此是2,1
var result = [] var dfs = function(nodes) { if(nodes.node) { //也就是先入棧6,5,4,全部出棧是4,5,6 nodes.left && dfs(nodes.left) result.push(nodes.node)//(4,5) 3 6 nodes.right && dfs(nodes.right)//由於5有右節點(3) , // 而後就是右節點2入棧的時候就添加到數組中,右節點1入棧也被添加了 } } dfs(nodes) console.log(result) // [4, 5, 3, 6, 2, 1]非遞歸版
var dfs = function(nodes) { var result = [] var stack = [] var item = nodes stack.push(nodes) while (stack.length) { if(item.left && !item.touched) {//由於4的item.left沒有直接跳出 item.touched = true item = item.left stack.push(item) //(6,5,4) continue } item.touched && delete item.touched // 清理標記 item = stack.pop() result.push(item.node) //4,5, item.right && stack.push(item.right) //而後把3入棧,由於3沒有左節點直接出棧 } return result } console.log(dfs(nodes))
後序遍歷
左右中
遞歸版
不用解釋,打印下你就懂了 var result = [] var dfs = function(nodes) { if(nodes.node) { nodes.left && dfs(nodes.left) nodes.right && dfs(nodes.right) result.push(nodes.node) } } dfs(nodes) console.log(result)非遞歸版
function Stack() { var items = []; //用來保存棧裏的元素 this.push = function (element) { items.push(element); } this.pop = function () { return items.pop(); } this.peek = function () { return items[items.length - 1]; } this.isEmpty = function () { return items.length == 0; } this.size = function () { return items.length; } this.clear = function () { items = []; } this.print = function () { console.log(items.toString()); } } //也就是先序遍歷(中左右)換成中右左 const preOrder = (head) => { if (head != null) { const stack = new Stack() stack.push(head) while (!stack.isEmpty()) { head=stack.pop() console.log(head.node) if (head.right != null) { stack.push(head.right) } if (head.left != null) { stack.push(head.left) } } } } preOrder(nodes) 最簡潔的方法 const preOrder = (head) => { if (head != null) { const stack = new Stack() stack.push(head) let c=null; while (!stack.isEmpty()) { //查看棧頂(就是最後一個) c=stack.peek() if (c.left != null && head != c.left && head != c.right) { stack.push(c.left) }else if (c.right != null && head != c.right) { stack.push(c.right) }else{ console.log(stack.pop().node) head=c } } } } preOrder(nodes)
直接用java代碼吧比較直觀
public static class Node { public int value; public Node left; public Node right; public Node parent; public Node(int data) { this.value = data; } } public static Node getSuccessorNode(Node node) { if (node == null) { return node; } if (node.right != null) { //若是當前節點的右孩子節點不爲空,說明有右子樹, return getLeftMost(node.right); //則找到並返回右子樹上最左的節點 } else { //若是當前節點沒有右子樹 Node parent = node.parent; while (parent != null && parent.left != node) { node = parent; parent = node.parent; } return parent; } } public static Node getLeftMost(Node node) { //在這個函數裏面,node是某個節點的頭部 if (node == null) { return node; } while (node.left != null) { //左子樹不爲空的狀況下,一路向左 node = node.left; } return node; }
定義一個如圖的二叉樹
const symmetricalTree = { val: 1, left: { val: 2, left: { val: 4, left: null, right: null }, right: { val: 5, left: null, right: null } }, right: { val: 3, left: { val: 6, left: null, right: null }, right: { val: 7, left: null, right: null } } }先序序列化
//序列化 function Serialize(pRoot, arr = []) { if (!pRoot) { arr.push('#'); } else { arr.push(pRoot.val); Serialize(pRoot.left, arr); Serialize(pRoot.right, arr); } return arr.join(','); }反序列化
//反序列化 function Deserialize(str) { if (!str) { return null; } return deserialize(str.split(',')); } function deserialize (arr) { let node = null; const current = arr.shift(); if (current !== '#') { node = { val: current }; node.left = deserialize(arr); node.right = deserialize(arr); } return node; }能夠去查查先序,中序,後序,層序的實現,還有其中的遞歸版和非遞歸版
................................................................................................................................................................