Github來源:力扣 (LeetCode)|刷題打卡 | 求星星 ✨ | 給個❤️關注,❤️點贊,❤️鼓勵一下做者前端
[已開啓]任務一:刷題打卡 * 10 篇node
哪吒人生信條:若是你所學的東西 處於喜歡 纔會有強大的動力支撐。git
天天學習編程,讓你離夢想更新一步,感謝不負每一份熱愛編程的程序員,不論知識點多麼奇葩,和我一塊兒,讓那一顆四處流蕩的心定下來,一直走下去,加油,2021
加油!歡迎關注加我vx:xiaoda0423
,歡迎點贊、收藏和評論程序員
時間: 3 月 1 日 ~ 3 月 13 日github
若是這篇文章有幫助到你,給個❤️關注,❤️點贊,❤️鼓勵一下做者,接收好挑戰了嗎?文章公衆號首發,關注 程序員哆啦A夢 第一時間獲取最新的文章web
❤️筆芯❤️~面試
棧,隊列,鏈表,集合,字典和散列表算法
樹是一種分層數據的抽象模型,最多見的樹的例子是家譜或是公司的組織架構圖。編程
二叉樹和二叉搜索樹數組
二叉搜索樹數據結構
建立
BinarySearchTree
類
function BinarySearchTree() {
var Node = function(key){
// 聲明一個Node類來表示樹中的每一個節點
this.key = key;
this.left = null;
this.right = null;
};
var root = null;
}
複製代碼
insert(key)
,向樹中插入一個新的鍵search(key)
,在樹中查找一個鍵,若是節點存在,則返回true;若是不存在,則返回falseinOrderTraverse
,經過中序遍歷方式遍歷全部節點preOrderTraverse
,經過先序遍歷方式遍歷全部節點postOrderTraverse
,經過後序遍歷方式遍歷全部節點min
,返回樹中最小的值/鍵max
,返回樹中最大的值/鍵remove(key)
,從樹中移除某個鍵向樹中插入一個鍵
示例:
// 向樹插入一個新鍵的算法
// 要向樹中插入一個新的節點
this.insert = function(key){
var newNode = new Node(key); //建立用來表示新節點的Node類實例
// 只須要向構造函數傳遞咱們想用來插入樹的節點值,它的左指針和右指針的值會由構造函數自動設置爲null
if (root === null){ //第二步要驗證這個插入操做是否爲一種特殊狀況。
//這個特殊狀況就是咱們要插入的節點是樹的第一個節點
root = newNode;
} else {
insertNode(root,newNode); //將節點加在非根節點的其餘位置
}
};
複製代碼
// 將節點加在非根節點的其餘位置
var insertNode = function(node, newNode){
// 傳入樹的根節點和要插入的節點
if (newNode.key < node.key){ //若是新節點的鍵小於當前節點的鍵
if (node.left === null){ //須要檢查當前節點的左側子節點
// 若是它沒有左側子節點
node.left = newNode; //就在那裏插入新的節點
} else {
// 若是有左側子節點,須要經過遞歸調用insertNode方法
insertNode(node.left, newNode); //繼續找到樹的下一層
// 下次將要比較的節點將會是當前節點的左側子節點
}
} else {
if (node.right === null){
//若是節點的鍵比當前節點的鍵大,同時當前節點沒有右側子節點
node.right = newNode; //就在那裏插入新的節點
} else {
insertNode(node.right, newNode);
//若是有右側子節點,一樣須要遞歸調用insertNode方法,可是要用來和新節點比較的節點將會是右側子節點
}
}
};
複製代碼
示例:
var tree = new BinarySearchTree();
tree.insert(11);
複製代碼
樹的遍歷,遍歷一棵樹是指訪問樹的每一個節點並對它們進行某種操做的過程(中序遍歷的一種應用就是對樹進行排序操做)
訪問樹的全部節點有三種方式:中序、先序和後序。
BST
全部節點的遍歷方式(就是以從最小到最大的順序訪問全部節點)
// 中序遍歷的一種應用就是對樹進行排序操做
this.inOrderTraverse = function(callback){
// 接收一個回調函數做爲參數
// 回調函數用來定義咱們對遍歷到的每一個節點進行的操做
inOrderTraverseNode(root, callback); //接收一個節點和對應的回調函數做爲參數
};
複製代碼
var inOrderTraverseNode = function (node, callback) {
if (node !== null) {
//要經過中序遍歷的方法遍歷一棵樹,首先要檢查以參數形式傳入的節點是否爲null
inOrderTraverseNode(node.left, callback); //遞歸調用相同的函數來訪問左側子節點
callback(node.key); //接着對這個節點進行一些操做
inOrderTraverseNode(node.right, callback); //而後再訪問右側子節點
}
};
複製代碼
function printNode(value){ //須要建立一個回調函數
console.log(value);
}
tree.inOrderTraverse(printNode);
//調用inOrderTraverse方法並將回調函數做爲參數傳入
複製代碼
示例:
this.preOrderTraverse = function(callback){
preOrderTraverseNode(root, callback);
};
preOrderTraverseNode方法的實現以下:
var preOrderTraverseNode = function (node, callback) {
if (node !== null) {
callback(node.key); //先序遍歷和中序遍歷的不一樣點是,先序遍歷會先訪問節點自己
preOrderTraverseNode(node.left, callback);
//而後再訪問它的左側子節點
preOrderTraverseNode(node.right, callback); //最後是右側子節點
}
};
複製代碼
示例:
this.postOrderTraverse = function(callback){
postOrderTraverseNode(root, callback);
};
postOrderTraverseNode方法的實現以下:
var postOrderTraverseNode = function (node, callback) {
if (node !== null) {
postOrderTraverseNode(node.left, callback); //後序遍歷會先訪問左側子節點
postOrderTraverseNode(node.right, callback); //而後是右側子節點
callback(node.key); //最後是父節點自己
}
};
複製代碼
搜索樹中的值
有三種執行的搜索類型:搜索最小值,搜索最大值,搜索特定的值
示例:
// 尋找樹的最小鍵的方法
this.min = function() {
return minNode(root); //調用了minNode方法
// 傳入樹的根節點
};
複製代碼
var minNode = function (node) {
// 容許咱們從樹中任意一個節點開始尋找最小的鍵
if (node){
while (node && node.left !== null) { //遍歷樹的左邊
node = node.left; //遍歷樹的左邊
}
return node.key;
}
return null; //直到找到樹的最下層
};
複製代碼
// 實現max方法
this.max = function() {
return maxNode(root);
};
var maxNode = function (node) {
if (node){
while (node && node.right !== null) { //沿着樹的右邊進行遍歷
node = node.right; // 直到找到最右端的節點
}
return node.key;
}
return null;
};
複製代碼
搜索一個特定的值
// find、search或get方法來查找數據結構中的一個特定的值
this.search = function(key){
return searchNode(root, key);
//searchNode方法能夠用來尋找一棵樹或它的任意子樹中的一個特定的值
};
var searchNode = function(node, key){
if (node === null){
//先要驗證做爲參數傳入的node是否合法(不是null)。
//若是是null的話,說明要找的鍵沒有找到,返回false。
return false;
}
if (key < node.key){ //若是要找的鍵比當前的節點小
return searchNode(node.left, key);
//那麼繼續在左側的子樹上搜索
} else if (key > node.key){
//若是要找的鍵比當前的節點大,那麼就從右側子節點開始繼續搜索
return searchNode(node.right, key);
} else {
return true; //不然就說明要找的鍵和當前節點的鍵相等
// 就返回true來表示找到了這個鍵
}
};
複製代碼
移除一個節點
示例:
this.remove = function(key){
root = removeNode(root, key); //傳入root和要移除的鍵做爲參數
// root被賦值爲removeNode方法的返回值
};
複製代碼
// removeNode方法的實現
var removeNode = function(node, key){
if (node === null){
//若是正在檢測的節點是null,那麼說明鍵不存在於樹中,因此返回null
return null;
}
if (key < node.key) {
//若是要找的鍵比當前節點的值小
node.left = removeNode(node.left, key);
//就沿着樹的左邊找到下一個節點
return node; //
} else if (key > node.key){
//若是要找的鍵比當前節點的值大
node.right = removeNode(node.right, key);
//那麼就沿着樹的右邊找到下一個節點
return node; //
} else { //鍵等於node.key
//第一種狀況——一個葉節點 移除一個葉節點
if (node.left === null && node.right === null){
//第一種狀況是該節點是一個沒有左側或右側子節點的葉節點
// 給這個節點賦予null值來移除它
// 還須要處理指針
node = null; // 須要經過返回null來將對應的父節點指針賦予null值
return node; // 值已是null了,父節點指向它的指針也會接收到這個值
// 要在函數中返回節點的值的緣由
}
//第二種狀況——一個只有一個子節點的節點
// 移除有一個左側或右側子節點的節點
if (node.left === null){ //若是這個節點沒有左側子節點
node = node.right; //也就是說它有一個右側子節點
// 把對它的引用改成對它右側子節點的引用
return node; //並返回更新後的節點
} else if (node.right === null){
//若是這個節點沒有右側子節點
node = node.left; //把對它的引用改成對它左側子節點的引用
return node; //並返回更新後的值
}
//第三種狀況——一個有兩個子節點的節點
// 移除有兩個子節點的節點
var aux = findMinNode(node.right); //
node.key = aux.key; //
node.right = removeNode(node.right, aux.key); //
return node; //
}
};
複製代碼
要移除有兩個子節點的節點,須要執行四個步驟
// 找到了要找的鍵(鍵和node.key相等)
var findMinNode = function(node){
while (node && node.left !== null) {
node = node.left;
}
return node; // 在findMinNode中返回了節點
};
複製代碼
自平衡樹
BST
存在一個問題:樹的一條分支會有不少層,而其餘的分支卻只有幾層的狀況。
Adelson-Velskii-Landi 樹(AVL 樹)
AVL
樹是一種自平衡二叉搜索樹(任何一個節點左右兩側子樹的高度之差最多爲1)AVL
樹的不一樣之處在於咱們須要檢驗它的平衡因子,若是有須要,則將其邏輯應用於樹的自平衡(hr)
和左子樹高度(hl)
的差值,該值(hr-hl)
應爲0、1或1
AVL
樹-這就是平衡因子的概念。// 計算節點高度
var heightNode = function(node) {
if (node === null) {
return -1;
} else {
return Math.max(heightNode(node.left),
heightNode(node.right)) + 1;
}
};
// 替換insertNode方法的行
if ((heightNode(node.left) - heightNode(node.right)) > 1) {
// 旋轉
}
向右子樹插入新節點時,應用一樣的邏輯
// 替換insertNode方法的行
if ((heightNode(node.right) - heightNode(node.left)) > 1) {
// 旋轉
}
複製代碼
AVL
旋轉:能夠執行單旋轉或雙旋轉兩種平衡操做示例:
var insertNode = function(node, element) {
if (node === null) {
node = new Node(element);
} else if (element < node.key) {
node.left = insertNode(node.left, element);
if (node.left !== null) {
// 確認是否須要平衡
}
} else if (element > node.key) {
node.right = insertNode(node.right, element);
if (node.right !== null) {
// 確認是否須要平衡
}
}
return node;
};
複製代碼
1、題目描述
給定一個二叉樹,檢查它是不是鏡像對稱的。
2、思路分析
用遞歸:一棵樹是對稱的 等價於 它的左子樹和右子樹兩棵樹是對稱的,問題就轉變爲判斷兩棵樹是否對稱。
遍歷每個節點的時候,若是我均可以經過某種方法知道它對應的對稱節點是誰,這樣的話我直接比較二者是否一致就好了。
第一次遍歷的同時將遍歷結果存儲到哈希表中,而後第二次遍歷去哈希表取。
若是同時知足下面的條件,兩個樹互爲鏡像:
咱們將根節點的左子樹記作 left
,右子樹記作 right
。比較 left
是否等於 right
,不等的話直接返回就能夠了。
若是至關,比較 left
的左節點和 right
的右節點,再比較 left
的右節點和 right
的左節點。
終止條件:
left 和 right
不等,或者 left 和 right
都爲空left,left
和 right.right
,遞歸比較 left,right 和 right.left
3、答案代碼
var isSymmetric = function(root) {
if(!root) return true;
var stack = [root.left,root.right];
while(stack.length){
var p = stack.pop();
var q = stack.pop();
if (p === q) continue;
if (p && q && p.val === q.val) {
stack.push(p.left, q.right, p.right, q.left);
} else {
return false;
}
}
return true;
};
複製代碼
4、總結
對稱二叉樹,樹
❤️關注+點贊+收藏+評論+轉發❤️,原創不易,鼓勵筆者創做更好的文章
我是Jeskson
(達達前端),感謝各位人才的:點贊、收藏和評論,咱們下期見!(如本文內容有地方講解有誤,歡迎指出☞謝謝,一塊兒學習了)
文章持續更新,能夠微信搜一搜「 程序員哆啦A夢 」第一時間閱讀,回覆【資料】有我準備的一線大廠資料,本文 www.dadaqianduan.cn/#/ 已經收錄
github
收錄,歡迎Star
:github.com/webVueBlog/…
本文正在參與「掘金 2021 春招闖關活動」, 點擊查看 活動詳情