《数据结构与算法之美》学习笔记二

news2024/11/16 5:49:11

前言:本篇文章介绍了一下二叉树中的基本知识点,包括二叉树的种类、二叉树的存储方式以及二叉树的深度和广度优先遍历;以及《数据结构与算法》中对于数组的讲解记录,只记录了本前端能看懂的🤓,还有很多知识点是我看不懂的,后端老师请自行探索吧。

一、二叉树

最近一直在刷《代码随想录》二叉树相关的题目,总结一下非常基本的一些知识点

(一)二叉树的种类

1、满二叉树

二叉树上只有度为0和度为2的节点,并且度为0的节点都在同一层,这样的二叉树叫做满二叉树。
就是所有的节点都满满当当的。假设二叉树的深度为k,那么满二叉树的节点数为 2^k -1
在这里插入图片描述

2、完全二叉树

完全二叉树除了最底层没有填满,其他层的节点都是满的。最后一层的节点集中在左侧。
在这里插入图片描述

3、二叉搜索树

二叉搜索树是有顺序的树。
对于二叉搜索树的所有节点,如果左子树不为空,那么左子树上所有节点值都小于节点值;如果右子树不为空,那额右子树上所有节点值都大于节点值。
在这里插入图片描述

4、平衡二叉树

它是一棵空树,或者左右子树的高度差不超过1
在这里插入图片描述

(二)二叉树的存储方式

1、使用指针的链式存储

链式存储就是用 TreeNode 这个数据类型存储,相信大家在刷力扣的时候见过很多次了。
在这里插入图片描述

3、使用数组的顺序存储

使用数组存储的顺序是按照层序遍历的顺序
在这里插入图片描述
假设节点的索引是 i,那么左节点的索引就是 i*2+1;右节点的索引就是 i*2+2

(三)二叉树的遍历

神一样的递归三部曲:
1、确定递归的参数和返回值
2、确定递归的终止条件
3、确定递归的单层逻辑

1、深度遍历

深度遍历的前中后序中的前中后,指的是中间节点出现的顺序

(1)前序

就是中左右的顺序
① 递归

const dfs = function (node) {
    if (!node) return;
    // 访问中间节点
    console.log(node);
    dfs(node.left);
    dfs(node.right);
}

② 迭代
前序遍历的访问顺序是 中左右,先访问中节点,使用栈暂存中节点的左右子节点,由于栈是先进后出的,所以应该先加入右节点,后加入左节点

var preorderTraversal = function (root) {
    const ans = [];
    if (!root) return ans;
    const stack = [root];
    while (stack.length) {
        const item = stack.pop();
        ans.push(item.val);
        item.right && stack.push(item.right);
        item.left && stack.push(item.left);
    }
    return ans;
};
(2)中序

顺序是 左中右
① 递归

const dfs = function (node) {
    if (!node) return;
    dfs(node.left);
    // 访问中间节点
    console.log(node);
    dfs(node.right);
}

② 迭代

// 中序遍历 左中右
// 其实递归就是一个模拟的过程
// 要先加左节点就要一直 .left 到达左叶子节点
// 所以要先将路过的节点存到stack里面
// 并且要用指针指向当前节点
var inorderTraversal = function (root) {
    const stack = [];
    const res = [];
    let cur = root;
    while (stack.length || cur) {
        if (cur) {
            stack.push(cur)
            // 一直找 .left
            cur = cur.left;
        } else {
            // 出栈
            const item = stack.pop();
            res.push(item.val);
            cur = item.right;
        }
    }
    return res;
};
(3)后序

顺序是 左右中
① 递归

const dfs = function (node) {
    if (!node) return;
    dfs(node.left);
    dfs(node.right);
    // 访问中间节点
    console.log(node);
}

② 迭代
上面的前序遍历的迭代实现的是 中左右,后序遍历顺序是 左右中,那么只需要先把前序变成 中右左,然后再翻转最后得到的数组就可以了

var postorderTraversal = function (root) {
    const ans = [];
    if (!root) return ans;
    const stack = [root];
    while(stack.length){
        const cur = stack.pop();
        ans.push(cur.val);
        cur.left && stack.push(cur.left);
        cur.right && stack.push(cur.right);
    }
    return ans.reverse();
};
2、广度优先遍历

就是层序遍历,使用队列或者栈暂存

 // 使用队列保存每一层的节点
var levelOrder = function(root) {
    const ans = [];
    if(!root) return ans;
    const queue = [root];
    while(queue.length){
        const len = queue.length;
        for(let i=0;i<len;i++){
            const item = queue.shift();
            ans.push(item.val);
            item.left && queue.push(item.left);
            item.right && queue.push(item.right);
        }
    }
    return ans;
};

二、数组

这一节是《数据结构与算法之美》基础课的第一讲
数组是数据结构中最基本的概念,是我们每天都在用的数据结构,对于这个我们自认为简单又熟悉的数据结构,却有一个深刻而陌生的问题:
数组为什么从0开始编号呢?
下面就通过这一章的学习来解答这个问题,让我们对数组更亲切熟悉吧。

(一)基础概念

虽然大家对数组很熟悉,但是还是有必要用学术性的语言定义一下数组
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。
其中涉及到的线性表概念解释一下

1、线性表

线性表就是在排列上排成一条线的结构,它只有前后两个方向,线性表的种类:数组、列表、队列、栈等等
在这里插入图片描述

2、非线性表

就是数据排列不是线性的数据结果,数据不是简单的前后关系,比如二叉树、图、堆等
在这里插入图片描述

(三)数组的特性

1、随机访问

由于数组是在连续空间上存储的相同数据类型的数据,这种特性使得数组具有 随机访问 的能力,也就是可以根据数组的下标随机访问数组元素。那么这个特异功能是怎么实现的呢?
我们以一个长度为10的数组举例,计算机会为这个数组开辟长度为10的空间,假设分配的内存空间是 1000 ~ 1039,其中内存块的首地址 base_address 是1000
在这里插入图片描述
计算机会给每个内存单元分配地址,计算机通过寻找内存地址来寻找对应的数据,当计算机需要随机访问数组中的某个元素的时候,就会通过下面的寻址公式查找对应位置的数据

a[i]_address = base_address + i * data_type_size

其中 data_type_size 表示内存空间大小,对于我们的例子 int 类型来说,就是4个字节。
由于这种根据寻址公式查找数据的机制,数组可以支持时间复杂度为 O(1) 的随机访问。

2、插入删除速度慢

由于连续存储数据的这种机制,数组的插入和删除操作的速度会比较慢。

  • 插入操作
    先分析一下插入操作,要插入一个元素,就必须要把这个元素后面的元素都往后搬运一位,然后把元素放在指定位置。
    这样的时间复杂是多少呢?
    时间复杂度取决于要进行多少次数据操作。最好情况下,我们在数组的最后插入元素,那么不需要搬运其他元素,时间复杂度为 O(1),所以最好时间复杂度就是 O(1);最坏情况下,我们在数组的开头插入元素,所有的元素都需要往后搬运一位,最坏情况时间复杂度就是 O(n)。又由于插入的位置从 1 ~ n 的概率都是 1/n,所以平均时间复杂度是 O(1+2+...+n)/n = O(n)
    如果我们只是为了在索引为 k 的地方插入元素 a,有一个更省时的处理方式,就是把索引为 k 的元素搬到数组的最后,把 a 插入到位置 k,这其实就是快排的思想。

  • 删除操作
    其实删除操作和插入操作的分析过程是一样的,时间复杂度为 O(n)。
    如果我们要执行一系列的删除操作,例如要依次删除下面的 a、b、c,那么删除 a 的时候,b和c的搬运是不是就是浪费的?所以在删除操作中,我们可以先记录下打算删除哪个元素,并不真正的删除,等待一段时间后,再检查一下要删除的元素都有谁,批量进行删除。这就是 JVM 的垃圾回收的思想。好吧,本前端不会(傲娇.jpg)。
    在这里插入图片描述
    回到前面的问题:为什么数组都是从 0 开始编号呢?
    回顾一下寻址公式:

a[i]_address = base_address + i * data_type_size

数组的索引记录的其实是相对于 base_address 的偏移量,第一个元素就是在 base_address 的位置,如果数组从 1 开始编号,那么根据寻址公式随机访问元素的时候,都要进行 i-1 的操作,浪费一次计算,所以从 0 开始可以有效的提高随机访问数组元素的效率。当然最开始的 c 语言可能是出于这个目的,后来的编程语言可能是为了和 c 语言保持统一,干脆都从 0 开始编号。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1812817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

codeanalysis服务端Windows环境运行

1、在每个微服务中创建log文件夹 2、

接口+接口自动化测试疑难解答,一篇带你策底打通...

前言 1、你们做接口测试的流程一般是怎么样的&#xff1f; 接口测试的流程其实和功能测试的流程类似&#xff0c;因为接口测试依赖的主要对象也是需求说明书&#xff0c;所以&#xff0c;最初的流程就是参与需求讨论&#xff0c;评审需求。 需求确定以后&#xff0c;开发会根…

蚁群算法的优化计算——旅行商问题(TSP)优化matlab

微♥关注“电击小子程高兴的MATLAB小屋”获得资料 一&#xff0c;理论基础 生物学家研究发现&#xff0c;蚂蚁在寻找食物时&#xff0c;会在其经过的路径上释放一种信息素&#xff0c;并能够感知其他蚂蚁释放的信息素。信息素浓度的大小表示路径的远近&#xff0c;浓度越高&a…

大众点评全国宠物店铺POI采集13万家-2024年5月底

大众点评全国宠物店铺POI采集13万家-2024年5月底 店铺POI点位示例&#xff1a; 店铺id iJLEF6dlJjeeUum4 店铺名称 联合动物医院(汉口总院) 十分制服务评分 9.3 十分制环境评分 9.2 十分制划算评分 9.3 人均价格 679 评价数量 4879 店铺地址 解放大道1725附2号&#x…

catia展开模型树

1 直接点号 2 选中零件&#xff0c;右击--命令--将图居中即可 一般都是上面这样有选择性的展开 如果要一次性都展开那

【Spring6】1-12章源码级深入详解 IoC

一、Spring启示录 阅读以下代码&#xff1a; package com.powernode.oa.controller;import com.powernode.oa.service.UserService; import com.powernode.oa.service.impl.UserServiceImpl;public class UserController {private UserService userService new UserServiceI…

Linux-笔记 全志平台镜像中添加git提交号

引言 通过在镜像中某个位置添加git提交号&#xff0c;可以方便排查与追溯是哪个提交编译出来的。 这里使用的全志T113平台&#xff0c;SDK为Tina5.0。 实现的办法有&#xff1a; 1、内核/proc/cpuinfo的打印信息及设备树中查看提交号 2、从设备树查看提交号 3、从uboot启动…

Springboot3+自动装配

导言&#xff1a;这里主要讲述springboot3以后spring.factories功能失效&#xff0c;带来的解决办法。 之前有一次希望用springboot模块拿到工具模块的配置configuration的时候&#xff0c;想通过之前的spring.factories来实现自动装配&#xff0c;但是发现一直拿不到配置&…

java:FeignClient通过RequestInterceptor自动添加header

示例代码 【pom.xml】 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.3.12.RELEASE</version> </dependency> <dependency><groupId>o…

222、求出完全二叉树的节点

给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的若干位置。若最…

Exposure X7官方版下载-Alien Skin Exposure X7软件最新版安装步骤

​Exposure是用于创意照片编辑的最佳图像编辑器&#xff0c;Exposure结合了专业级照片调整&#xff0c;庞大的华丽照片外观库以及高效的设计&#xff0c;使其使用起来很愉悦&#xff0c;新的自动调整功能可简化您的工作流程&#xff0c;并使您进入创意区。 ​安 装 包 获 取 地…

代码随想录-Day29

491. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特殊情…

Game-Fi 新贵 MetaArena 项目全解析:重塑区块链游戏生态

在区块链技术迅猛发展的浪潮中&#xff0c;全球各行业都在探索如何利用这一革命性技术来提升效率、降低成本&#xff0c;并创造新的商业模式。游戏行业作为数字娱乐的核心领域之一&#xff0c;也在经历前所未有的变革。尽管传统游戏巨头如Steam和任天堂已推出Web3元宇宙游戏产品…

Android 如何调试已经运行在手机/模拟器中的软件界面上的像素 layout margin 等布局参数 用 Layout Inspector 工具

文章目录 Intro场景Layout Inspector 窗口的打开方式Tools --> Layout InspectorView --> Tool Windows --> Layout Inspector通用的方法 Help --> 搜索Layout Inspector Intro 一句话&#xff1a; Android Studio 有一个叫 Layout Inspector/布局检查器的工具&am…

【three.js】光源对物体表面影响

目录 一、受光照影响材质 二、光源简介 2.1 点光源 光源位置 点光源辅助观察 完整代码,粘贴即用 2.2 环境光 2.3 平行光 平行光辅助观察 实际生活中物体表面的明暗效果是会受到光照的影响,比如晚上不开灯,你就看不到物体,灯光比较暗,物体也比较暗。在threejs中,…

多个线程多个锁:如何确保线程安全和避免竞争条件

目录 前言 一、确定需要多个锁的场景 1.独立资源保护 2.部分依赖资源 二、避免死锁 三、锁粒度与并发性能 1. 粗粒度锁定 2.细粒度锁定 四、设计策略&#xff1a;减少资源依赖 1.资源分离 2.无锁设计 3.锁合并 五、Demo讲解 总结&#xff1a; 前言 当多个线程需要…

机器人建模、运动学与动力学仿真分析(importrobot,loadrobot,smimport)

机器人建模、运动学与动力学仿真分析是机器人设计和开发过程中的关键步骤。 一、机器人建模 机器人建模是描述机器人物理结构和运动特性的过程。其中&#xff0c;URDF&#xff08;Unified Robot Description Format&#xff09;是一种常用的机器人模型描述方法。通过URDF&…

Autoformer

A u t o f o r m e r Autoformer Autoformer 摘要 ​ 我们设计了 A u t o f o r m e r Autoformer Autoformer作为一种新型分解架构&#xff0c;带有自相关机制。我们打破了序列分解的预处理惯例&#xff0c;并将其革新为深度模型的基本内部模块。这种设计使 A u t o f o r m…

嵌入式系统概述

嵌入式系统是为了特定应用而专门构建的计算机系统&#xff0c;其嵌入式软件的架构设计与嵌入式系统硬件组成紧密相关。 1.嵌入式系统发展历程 嵌入式系统的发展大致经历了五个阶段&#xff1a; 第一阶段&#xff1a;单片微型计算机&#xff08;SCM&#xff09;&#xff0c;及…

如何愉快地实施数仓模型,对比下厨做饭

一般我们建设数仓&#xff0c;有一个链路&#xff1a; 比如这样的 数据从原始层到DWD、DWS层、然后ADS层。 嘿&#xff0c;未来的大数据专家们&#xff01;当我们开始实施数据模型时&#xff0c;不妨参考《大数据之路》这本宝藏书。 让我们一起简化流程&#xff0c;注重细节…