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 春招闯关活动」, 点击查看 活动详情