链表
单链表
由数组创建链表结构
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
let l1Arr = [1,2,3,4] // 需要转的数组
let l1 = new ListNode(l1Arr[0]); // 转成的链表实例对象,初始化给一个索引0的val
let head = l1; // 头节点保存,用于移动遍历l1Arr的每一个数组, 中转来保证l1的结构
for(let i=1; i<l1Arr.length; i++){ //从1开始赋值
head.next = new ListNode(l1Arr[i]); // 实例对象赋值给next
head = head.next //指向下一个,继续赋值
}
console.log(l1)
head用于移动,l1用于保存,head移动到了最后
查找是否存在某个val
function search(target){
while(l1.val){ // 还有当前值,继续
if(l1.val===target)return true;
if(!l1.next) return false // 没有下一个,结束
l1 = l1.next;
}
return false
}
console.log(search(5)) // false
树
树的几个概念
- 拥有相同父节点的节点,互称为兄弟节点
- 节点的深度 :从根节点到该节点所经历的边的个数
- 节点的高度 :节点到叶节点的最长路径
- 树的高度:根节点的高度
B、C、D就互称为兄弟节点,其中,节点B的高度为2,节点B的深度为 1,树的高度为3
二叉树
最多仅有两个子节点的树(最多能分两个叉的树🤦♀️)
细分
-
平衡二叉树
二叉树中,每一个节点的左右子树的高度相差不能大于 1,称为平衡二叉树。
-
满二叉树
除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树 -
完全二叉树
深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h 层所有的结点都连续集中在最左边
表示二叉树
链式存储
// 定义Node节点类
function Node(data, left, right) {
this.data = data;
this.left = left;
this.right = right;
this.show = show;
}
function show() {
return this.data;
}
Node(2,1,3);
show(); //2
一棵二叉树可以由根节点通过左右指针连接起来形成一个树。
function BinaryTree() {
let Node = function (val) {
this.val = val
this.left = null
this.right = null
}
let root = null
}
力扣上定义二叉树节点
function TreeNode(val, left, right) {
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
//存储
const arr = [5,3,6,2,4,null,8,1,null,null,null,7,9];
const res = new TreeNode(arr[0])
let currentNode = res;
for(let i=0; i<arr.length; ){
currentNode.left = null
currentNode.right = new TreeNode(arr[i + 1])
currentNode = currentNode.right
}
数组存储(适用于完全二叉树)
如上图完全二叉树,遵循以下三个规律:
- 位置为 i 的节点,它的父节点位置为 i/2
- 它的左子节点 2i
- 它的右子节点 2i+1
优点:如果我们把完全二叉树存储在数组里(从下标为 1 开始存储),我们完全可以通过下标找到任意节点的父子节点。从而完整的构建出这个完全二叉树。这就是数组存储法。
数组存储法相对于链式存储法不需要为每个节点创建它的左右指针,更为节省内存。
二叉树的遍历
遍历分类:
- 前序遍历
- 中序遍历
- 后序遍历
所谓前、中、后,不过是根val 的顺序,即也可以称为先根遍历、中根遍历、后根遍历
- 前序遍历
任意一个节点,先打印该节点,然后是它的左子树,最后右子树
// 递归 前序遍历核心代码
// - 遍历二叉树的过程也就是一个递归的过程,很像dfs??
var preOrderTraverseNode = (node) => {
if(node) {
// 先根节点
result.push(node.val)
// 然后遍历左子树
preOrderTraverseNode(node.left)
// 再遍历右子树
preOrderTraverseNode(node.right)
}
}
// 迭代 前序遍历,比递归难
const preorderTraversal = (root) => {
const list = [];
const stack = [];
// 当根节点不为空的时候,将根节点入栈
if(root) stack.push(root) //首先根入栈
while(stack.length > 0) {
const curNode = stack.pop() //将根节点出栈,将根节点值放入结果数组中
// 第一步的时候,先访问的是根节点
list.push(curNode.val)
//然后遍历左子树、右子树,因为栈是先入后出,所以,我们先右子树入栈,然后左子树入栈
// 我们先打印左子树,然后右子树
// 所以先加入栈的是右子树,然后左子树
if(curNode.right !== null) {
stack.push(curNode.right)
}
if(curNode.left !== null) {
stack.push(curNode.left)
}
}
return list
}
空间复杂度:O(n),时间复杂度:O(n)
- 中序遍历
任意一个节点,先打印它的左子树,然后是该节点,最后右子树
- 后序遍历
对于二叉树中的任意一个节点,先打印它的左子树,然后是右子树,最后该节点
二叉搜索树(BST)
一种特殊的二叉树,相对较小的值保存在左节点中,较大的值保存在右节点中,二叉查找树一种有序树。
//查找最小值
function getMin () {
var current =nums.root;
while (current.left) {
current = current.left;
}
return current.data;
}
//查找最大值
function getMax () {
var current = nums.root;
while (current.right) { //不为null就继续
current = current.right;
}
return current.data;
}
//查找给特定的值
// 先要和当前节点比较,如果和当前节点的数据不相等,那么要判断接下来是要向左遍历还是向右遍历。
function find (data) {
var current = nums.root;
while (current) {
if (current.data === data) {
return current;
} else if (data < current.data) {
current = current.left;
} else {
current = current.right;
}
}
return null;
}
参考文章:
https://juejin.cn/post/6844904154066845703
https://blog.csdn.net/PingZhi_6766/article/details/78077124