最近少更了,入職了新公司,熟悉各類環境和業務需求,短暫時間內都沒有抽出時間去總結和分享了;今天給你們分享下,我是如何複習算法編程題
來進行手撕代碼
;java
談起算法,它是解決某個問題的計算方法、步驟。爲何許多公司都喜歡讓手撕代碼呢,更多考察的是寫代碼的能力和思惟吧;不過多贅述,開啓咱們的算法複習之途吧;node
也許對於一些人來講,是「學習」,由於本身在大學期間是有修過一些算法和數據結構的課程,切入點也是比較容易的;下面總結下如何切入到深刻;算法
其實我本身開始是在leetcode上進行大量的練習,典型的亂序練習,可是本身感受仍是記不住哈哈哈哈,老了,趁着元旦的三天假期,本身安排了複習計算,決定本身先本地總結一波,常見理論知識和常見題目,本地練習;編程
這裏就略過概念的介紹了直接進入咱們經常使用到的算法編程題markdown
主要是本身進行本地練習,因此定義了前置的一些條件數據結構
class NodeTree{
constructor(val){
this.left = null //左子樹
this.right = null //右子樹
this.value= val; //存儲的值
return this;
}
}
複製代碼
根據根節點建立 根據節點建立 粗略符合左右樹的差別oop
function createTree(){
this.root = null;
}
createTree.prototype.insertNode=function(node,newNode){
//判斷新節點和根節點的大小
if(node.value > newNode.value){
if(node.left ==null){
node.left = newNode
}else{
//遍歷左節點 進行查找
this.insertNode(node.left,newNode)
}
}else{
if(node.right == null){
node.right = newNode
}else{
this.insertNode(node.right,newNode)
}
}
}
createTree.prototype.insert=function(val){
let node = new NodeTree(val);
//若是沒有根節點 則直接插入到根節點中
if(!this.root){
this.root = node;
}else{
//不然插入到左右元素中
this.insertNode(this.root,node)
}
}
複製代碼
let array = [10,7,6,8,9,13,5,11,14]
let tree = new createTree();
array.forEach(item=>{
tree.insert(item)
})
複製代碼
DLR--前序遍歷(根在前,從左往右,一棵樹的根永遠在左子樹前面,左子樹又永遠在右子樹前面 )post
前序遍歷的標準是,先遍歷根節點,在遍歷左子樹,在遍歷右子樹,若是左子樹中節點存在左節點,則存儲當前左節點,繼續遍歷左節點的左子樹則繼續進行遍歷;學習
function preorderTraversal(root){
//先遍歷根節點
preOrderArray.push(root.value);
//遍歷左子樹
root.left && preorderTraversal(root.left)
//遍歷右子樹
root.right && preorderTraversal(root.right)
}
複製代碼
function preorderTraversalNot(root){
let preOrderArrDir=[],stack =[];
let curr = root;
//先遍歷根 左子樹 左子樹的子節點
while(curr!=null || stack.length>0){
//當前節點存在 則壓入棧中
while(curr!=null){
//壓入當前元素
stack.push(curr);
//存儲當前的元素
preOrderArrDir.push(curr.value)
//將curr指向當前的左節點
curr = curr.left;
}
//判斷是否爲空,若是不爲空 彈出已經遍歷的元素,進行遍歷右子樹
if(stack.length !=0){
curr = stack.pop();
curr = curr.right;
}
}
return preOrderArrDir
}
複製代碼
預期結果:優化
[5,6,7,8,9,10,11,13,14]
複製代碼
let inOrderArray = []
function inorderTraversal(nodeTree){
if(!nodeTree) return;
//先遍歷左節點
nodeTree.left && inorderTraversal(nodeTree.left)
//存儲當前根節點
inOrderArray.push(nodeTree.value)
//在遍歷右節點
nodeTree.right && inorderTraversal(nodeTree.right)
}
inorderTraversal(tree.root)
console.log(inOrderArray)
複製代碼
function inorderTraversalNot(root){
let inOrderQueue = [];//存儲遍歷結果
let curr = root,stack =[];
while(curr!=null || stack.length>0){
//先遍歷左子樹 到葉子節點
while(curr!=null){
//逐個壓入左節點
stack.push(curr);
curr = curr.left;
}
//進行出棧
if(stack.length>0){
//取出棧的最後一個元素 逐個是左子樹排列的元素
curr = stack.pop();
//壓入當前的元素,當前元素排列爲左子樹
inOrderQueue.push(curr.value)
//遍歷當前節點的右子樹
curr = curr.right;
}
}
return inOrderQueue
}
複製代碼
實際輸出
[ 5, 6, 7, 8, 9, 10, 11, 13, 14 ]
[ 5, 6, 7, 8, 9, 10, 11, 13, 14 ]
複製代碼
預期輸出
[5,6,9,8,7,11,14,13,10]
複製代碼
let postOrderTrversal=[]
function postorderTraversal(root){
if(!root) return
//先左
if( root.left )
root.left && postorderTraversal(root.left)
//後右
root.right && postorderTraversal(root.right)
//在根
postOrderTrversal.push(root.value)
}
複製代碼
定義兩個棧,壓入根元素,在壓入左子樹,在壓入右子樹,而後從後開始取出元素,後面的就是右子樹,左子樹和根元素
function postorderTraversalNot(root){
let postOrderArray =[];
let curr = root,stack1 = [],stack2=[];
if(curr){
stack1.push(curr)
}
// stack1 存儲當前子樹的目錄結構
while(stack1.length > 0){
let curr = stack1.pop();//彈出當前元素
//壓入stack2中 stack2每次都存入的是根節點 而後是左子樹的根節點 一直到葉子節點
stack2.push(curr)
//存儲當前的左子樹
if(curr.left){
stack1.push(curr.left)
}
//存儲當前的右子樹
if(curr.right){
stack1.push(curr.right)
}
}
console.log(stack2)
//遍歷stack2的變量 此時stack 存儲的數據結構正好是 節點的 根左右節點z
while(stack2.length>0){
let curr = stack2.pop();
postOrderArray.push(curr || curr.value)
}
return postOrderArray;
}
let result = postorderTraversalNot(tree.root);
console.log(result)
複製代碼
後續遍歷的這個過程不是很好理解,所以畫了一個圖
屬於廣度優先搜索BFS,一層一層遍歷,遍歷的順序以下圖 彈出一個節點,訪問,若左子節點或右子節點不爲空,將其壓入隊列。
// 非遞歸
function bfsTree(root){
let quque =[],bfsQueue=[]; //存儲遍歷的元素
let curr = null;
quque.push(root)
while(quque.length>0){
curr = quque.pop();//取出最後一個元素
bfsQueue.push(curr.value)
//查看當前左邊是否存在元素 存在則壓入棧前面
if(curr.left) {
quque.unshift(curr.left)
}
//查看當前元素右邊是否存在元素
if(curr.right){
quque.unshift(curr.right)
}
//queue存儲的元素都是當前層的左子樹的子樹和右子樹的子樹
}
return bfsQueue;
}
let result = bfsTree (tree.root);
複製代碼
給定一個二叉樹,求節點的個數
求二叉樹的個數,其實就是對二叉樹的節點進行遍歷的過程;
採用先序中序 後序的均可以
let node = 1;
function comNodeSum(root){
node ++ ;
root.left && comNodeSum(root.left)
root.right && comNodeSum(root.right)
}
複製代碼
層次遍歷進行計算
function codeNodeNotSum(root){
let node = 1;
let queue =[],curr = root;
queue.unshift(root) //存儲根節點
while(queue.length>0){
curr = queue.pop(); //取出隊列中的最後一個
node++ ;//計數當前的節點數量
if(curr.left){
queue.unshift(curr.left); //將左節點存入到前面
}
if(curr.right){
queue.unshift(curr.right); //右節點入隊
}
}
return node;
}
let result =codeNodeNotSum (tree.root);
console.log(result)
複製代碼
//遞歸計算樹的深度
function maxDepTree(root){
let num = 0;
if(!root) return 0;
//取 左子樹和右子樹的最大高度 而後加上當前節點的1
num = Math.max(maxDepTree(root.left),maxDepTree(root.right)) +1;
return num;
}
let result = maxDepTree(tree.root)
console.log(result)
複製代碼
//非遞歸 計算樹的深度 層序遍歷
function dpComTreeHeight(root){
if(!root) return 0
let currentNum = 1,//當前層的節點
nextNum = 0,//下一層的節點數目
depth = 0;//樹的深度
let queue = [],curr=null;
queue.push(root);
while(queue.length >0){
curr = queue.pop();
currentNum --;
//收集左節點
if(curr.left){
queue.unshift(curr.left)
nextNum++;
}
//收集右節點
if(curr.right){
queue.unshift(curr.right)
nextNum ++ ;
}
//若是是當前該層的最後一個節點
if(currentNum == 0){
depth++;
currentNum = nextNum;
nextNum = 0;
}
}
return depth;
}
let depth =dpComTreeHeight(tree.root);
console.log(depth)
複製代碼
思路
/** * 層序遍歷 * @param {*} root 當前的樹 * @param {*} key 當前底基層 */
function findKeyNumber(root,k){
if(!root || k==0) return 0
let currentNum = 1,
nextCurr =0,h=1;
let queue = [],
curr = null;
queue.push(root);
while(queue.length > 0){
if(k == h) return currentNum;
curr = queue.pop();
currentNum --
if(curr.left){
queue.unshift(curr.left)
nextCurr ++ ;
}
if(curr.right){
queue.unshift(curr.right)
nextCurr ++
}
//判斷是否遍歷完
if(currentNum == 0){
h++; //計數遍歷的第幾層
currentNum = nextCurr;
nextCurr = 0; //開始下一步步驟
}
}
return 0
}
let re = findKeyNumber(tree.root,4)
console.log(re)
複製代碼
思路:
//遞歸
function isSametree(tree1,tree2){
if(!tree1 && !tree2){
return true;
}
if(!tree1 || !tree2){
return false
}
//兩個值不相同
if(tree1.value != tree2.value){
return false;
}
//比較左子樹和右子樹
return isSametree(tree1.left,tree2.left) && isSametree(tree2.right,tree2.right)
}
複製代碼
function isSametreeNot(tree1,tree2){
if(!tree1 && !tree2){
return true;
}else if(!tree1 || !tree2){
return false
}
let stack1 = [],stack2 =[];
let curr1 = tree1,curr2= tree2;
stack2.push(tree2);
stack1.push(tree1)
while(stack1.length >0 && stack2.length >0 ){
curr1 = stack1.pop() //取出對應節點1
curr2 = stack2.pop()
//比較是否相同
if(curr1==null && curr2==null){
continue
}else if(curr2!=null && curr1!=null && curr1.value ==curr2.value){
//當前節點相同,比較下一個
stack2.push(curr2.left)
stack2.push(curr2.right)
stack1.push(curr1.left)
stack1.push(curr1.right)
}else{
return false;
}
}
return true;
}
let result3 = isSametreeNot(tree2,tree)
console.log(result3)
console.log(isSametreeNot(tree2,null))
複製代碼
它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹
思路
//遞歸
function isAsl (root){
if(!root) return true;
//計算左子樹和右子樹高度
let leftH = maxDepTree(root.left)
let rightH = maxDepTree(root.right)
//左右子樹的因子大於1即樹的高度太高
if(Math.abs(leftH-rightH)>1) return false;
//在進行比較左右子樹
return isAsl(root.left) && isAsl(root.right)
}
複製代碼
function createMirrorTree(root){
if(!root) return;
let newNode = new NodeTree(root.value);
//新節點左子樹等於右子樹
newNode.left = createMirrorTree(root.right)
//新節點右子樹等於左子樹
newNode.right = createMirrorTree(root.left)
return newNode
}
複製代碼
function isMirrorTree(root1,root2){
if(root1==null && root2==null ) return true;
if(!root2 || !root1 ) return false;
if(root1.value != root2.value) return false;
return isMirrorTree(root1.left,root2.right) && isMirrorTree(root1.right,root2.left)
}
複製代碼
function isMirrorTreeNot(root1,root2){
if(!root1 && !root2){
return true;
}else if(!root1 || !root2){
return false
}
let stack1=[],stack2 =[];
let curr1 = null,curr2= null;
stack1.push(root1);
stack2.push(root2)
while(stack2.length > 0 && stack1.length >0){
curr1 = stack1.pop();//取出當前節點的數據
curr2 = stack2.pop();
if(!curr1 && !curr2){
continue ;
}else if(curr2 && curr1 && curr1.value == curr2.value){
stack1.push(curr1.left) //現存root1左
stack1.push(curr1.right) //存root1 右
stack2.push(curr2.right) //先存root2 左
stack2.push(curr2.left) //存root2 左
}else{
return false;
}
//存棧的數據
}
return true;
}
複製代碼
二分查找樹特色 就是中序遍歷是一個有序的列表
function isValidBST(root,pre){
//進行中序遍歷
if(!root) return true;
let verLeft = isValidBST(root.left,pre)
if(!verLeft){
return false
}
//當前節點比前一個小,則不是遞增的,則錯誤
if(root.value <=pre){
return false;
}
pre = root.value;
let verRight = isValidBST(root.right,pre)
if(!verRight) return false;
return true;
}
複製代碼
給定兩個樹,判斷B是不是A的子樹
function hasSubTree(root1,root2){
if(!root1 || !root2) return false;
return isSubTree(root1,root2) || isSubTree(root1.left,root2) || isSubTree(root1.right,root2)
}
function isSubTree(root1,root2){
if(!root2) return true;
if(!root1) return false;
return root1.value !=root2.value ? false:
isSubTree(root1.left,root2.left) && isSubTree(root1.right,root2.right)
}
console.log(JSON.stringify(tree.root))
let result = hasSubTree(tree.root,null)
console.log(result)
複製代碼
/* * function TreeNode(x) { * this.val = x; * this.left = null; * this.right = null; * } */
/** * * @param root TreeNode類 * @param o1 int整型 * @param o2 int整型 * @return int整型 */
function lowestCommonAncestor( root , o1 , o2 ) {
// write code here
if(!root) return -1;
/** 關鍵仍是找到最近公共節點的特徵: 1. 若是該節點不是O1也不是O2,那麼O1與O2必然分別在該節點的左子樹和右子樹中 2. 若是該節點就是O1或者O2,那麼另外一個節點在它的左子樹或右子樹中 稍微能夠優化的一點就是,遇到O1或者O2節點就不往下遞歸了,把O1或者O2節點一層層往上傳。 */
if(o1==root.val || o2==root.val) return root.val;
let left = lowestCommonAncestor(root.left,o1,o2);
let right = lowestCommonAncestor(root.right,o1,o2);
if(left==-1) return right;
if(right==-1) return left;
return root.val;
}
module.exports = {
lowestCommonAncestor : lowestCommonAncestor
};
複製代碼
本篇文章主要講述了在算法複習過程當中的知識方法和二叉樹的常見算法,學習方式因人而異,可是算法學習是一個鍛鍊的結果,共勉~