【算法与数据结构】链表、哈希表、栈和队列、二叉树(笔记二)

文章目录

  • 四、链表理论
  • 五、哈希表理论
  • 五、栈和队列理论
    • 5.1 单调栈
  • 六、二叉树理论
    • 6.1 树的定义
    • 6.2 二叉树的存储方式
    • 6.3 二叉树的遍历方式
    • 6.4 高度和深度

  最近博主学习了算法与数据结构的一些视频,在这个文章做一些笔记和心得,本篇文章就写了一些基础算法和数据结构的知识点,具体题目解析会放在另外一篇文章。在学习时已经有C, C++的基础。文章附上了学习的代码,仅供大家参考。如果有问题,有错误欢迎大家留言。算法与数据结构一共有三篇文章,剩余文章可以在 【CSDN文章】晚安66博客文章索引找到。

四、链表理论

  单链表:由一个个节点组成,每个节点由一个数据域和一个指针域组成,数据域放数据,指针域指向下一个节点,链表入口节点叫做head,最后一个节点的next指针指向NULL(空指针)。
在这里插入图片描述
  双链表:在单链表的基础上增加了一个指针域,这个指针域指向前一个节点,head的prev指针指向NULL。双链表既可以向前查,也可以向后查。
在这里插入图片描述
  循环链表:链表的首尾相连。循环链表可以解决约瑟夫环问题。
在这里插入图片描述
  数组在内存中是连续分布的,但是链表不是连续分布的,它通过指针域的指针链接在内存中的各个节点。
链表定义方式:

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

初始化链表:

// 法一 
ListNode* head = new ListNode(5);
// 法二
ListNode* head = new ListNode();
head->val = 5;

删除和添加节点:
  假如想删除图中D节点,那么我们将C节点指针指向E节点,然后释放D的内存(C++需要手动释放,Java,Python有内存回收机制,不需要手动释放)。
在这里插入图片描述
  假如要添加F节点,将C节点的指针指向F,F指针指向D即可。
在这里插入图片描述
查询:
  链表查询是比较费劲的,例如想找第10个节点,那么得从第一个节点开始,按指针域一个一个找,找到第九个节点才能找到第十个节点。因此,链表查询时间复杂度为 O ( n ) O(n) O(n)

项目插入/删除查询使用场景
数组 O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)数据量固定,频繁查询,较少增删
链表 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n)数据量不固定,频繁增删, 较少查询

五、哈希表理论

  哈希表可以通过索引直接访问表中的元素哈希表一般用来快速判断一个元素是否出现在集合里,但哈希法是牺牲空间换取时间,因为要使用额外的数组set或map才能实现快速查找。举个例子,班级里是否有小明这个同学,如果要用枚举时间复杂度为 O ( n ) O(n) O(n),但如果哈希表只需要 O ( 1 ) O(1) O(1)就可以做到。

  在初始化时,只需要把全班的名字存在哈希表里,查询的时候通过姓名直接可以知道是否有这位同学。哈希表通过哈希函数(hash funciton)将学生姓名映射到哈希表上

在这里插入图片描述

工作原理:如上图所示,哈希函数将姓名转换成数值索引(一般通过特定编码方式),然后按索引在哈希表上得到目标数据。同时为了保证哈希函数计算的索引一定落在哈希表中,还做了取模操作。有时候,学生数量会大于哈希表长度,不同学生会得到同一个索引,也就是映射到哈希表上同一个位置,也就出现所谓的哈希碰撞问题。

哈希碰撞解决办法

  • 1、拉链法:在碰撞位置引入链表,链表指向依次指向不同的学生。拉链法要注意适当选择哈希表大小,充分利用哈希表内存,同时不要生成太长的链表
  • 2、线性探测法:当发生碰撞时,就找表的下一个空位方置。因此,一定要保证哈希表大小大于数据大小

常用的哈希表有

  • 数组
  • 集合(set)
  • 映射(map)

  在C++中,set和map提供了下面几种形式,使用集合来解决问题时,优先使用unordered_set,它底层用哈希表实现,查询效率和增删效率最高。只有处理有序数据时用set或者multiset(二者区别在于值能否重复)。

  虽然set、multiset、 map和multimap底层使用红黑树实现的,但是使用方式还是哈希表的key和value方式,同属于映射方法,同样可以归类到哈希法中。此外,红黑树是一种平衡二叉搜索树,key值是有序的,但key值不能修改,改动key值会导致整颗树错乱,所以智能删除和增加。map当中对key有限制,不可修改,value没有限制
在这里插入图片描述
在这里插入图片描述

五、栈和队列理论

  首先是关于栈和队列的元素进出关系:栈是先进后出,队列是先进先出栈提供push 和 pop 等等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。 不像是set 或者map 提供迭代器iterator来遍历所有元素。
在这里插入图片描述
  栈是以底层容器完成其所有的工作,对外提供统一的接口,我们可以控制使用哪种容器来实现栈的功能(栈是可插拔),例如vector,list,deque等等。所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。目前最常见的SGI STL(STL库的其中一个版本),如果没有指定底层实现,默认以deque(双向队列)缺省为底层容器,只要封住一端,开通另一端就可以实现栈的逻辑。
  也可以指定vector为底层实现:

std::stack<int, std::vector<int> > third;  // 使用vector为底层容器的栈

  队列的情况是一样的,队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。队列也不归为容器,也是容器适配器。

std::queue<int, std::list<int>> third; // 定义以list为底层容器的队列

5.1 单调栈

  单调栈问题长是针对一个一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。单调栈可以在 O ( n ) O(n) O(n)的时间复杂度内找到每一个元素的右边第一个比它大的元素位置。单调栈的本质是空间换时间,优点是整个数组只需遍历一次。我们使用一个栈来保存遍历过程中的元素。因为我们遍历数组的时候,我们不知道之前都遍历了哪些元素,以至于遍历一个元素找不到是不是之前遍历过一个更小的,所以我们需要用一个容器(这里用单调栈)来记录我们遍历过的元素。

  单调栈问题需要考虑以下几点:

    1. 单调栈里面存放的元素是什么?
    1. 单调栈是递增还是递减的?
        这里的递增或者递减的顺序指的是从栈底到栈顶(栈头)的顺序,C++中使用STL库可以用st.top()来访问栈顶。

六、二叉树理论

6.1 树的定义

  首先引入树的度的概念:结点拥有的子树个数称为结点的度,比如下图中结点3和结点4的度分别为3和2。对于树而言,树的度是结点最大的度,下面这棵树的度为4(结点1的度)。

在这里插入图片描述

  二叉树是指树的度最大为2的树。满二叉树:如果一棵树只有度为0和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树。如下图所示,这是一棵满二叉树。这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。

在这里插入图片描述

  完全二叉树:在完全二叉树中,除了最底层节点可能没有填满以外,其余每层节点数量都达到最大值,并且最下面一层节点都集中在该层的最左边若干位置。若底层为第k层,则该层包含 [ 1 , 2 k − 1 ] [1, 2^{k-1}] [1,2k1]个节点。
  在【算法和数据结构】347、LeetCode前 K 个高频元素中提到的优先级队列。实际上,优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。下图当中第三棵树就不是一棵完全二叉树。

在这里插入图片描述

  二叉搜索树:又叫二叉排序树。它具有下面三个特点:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

在这里插入图片描述

  平衡二叉排序树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。下图当中第三棵树就不是一棵平衡二叉树,左右两个子树的高度差绝对值超过了1。

在这里插入图片描述

  C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作的时间复杂度是 l o g ( n ) log(n) log(n)。unordered_map、unordered_set底层实现是哈希表, 增删操作的时间复杂度为 O ( 1 ) O(1) O(1)。详细内容可以看本文第五节。

6.2 二叉树的存储方式

  二叉树可以用链式存储,也可以顺序存储。那么链式存储方式就用指针, 顺序存储的方式就是用数组。链式存储如下图所示:

在这里插入图片描述

  顺序存储如下图所示,在遍历时,假设父节点为i那么它的左孩子就是 i ∗ 2 + 1 i*2+1 i2+1,右孩子就是 i ∗ 2 + 2 i*2+2 i2+2相较于链式存储,顺序存储方式比较不容易理解,也不直观,所以一般我们用链式存储二叉树
在这里插入图片描述

6.3 二叉树的遍历方式

二叉树主要有两种遍历方式,这两种也是图论当中最基本的两种遍历方式。

  • 深度优先遍历:先往深处走,遇到叶子节点再往回走。
  • 广度优先遍历:一层一层的去遍历。

在上面两种方式的基础之上进一步拓展,有如下的分类:

  • 深度优先遍历

    • 前序遍历(递归法、迭代法)
    • 中序遍历(递归法、迭代法)
    • 后序遍历(递归法、迭代法)
  • 广度优先遍历

    • 层次遍历(迭代法)

前中后是指中间节点的遍历顺序,是在前、中或者是后。例如,前序遍历:中左右;中序遍历:左中右;后序遍历:左右后。

在这里插入图片描述

  递归法和迭代法是这两种遍历的实现方法。深度优先遍历一般是用递归的方式实现,也就是说,用递归来实现前中后遍历比较方便。栈其实就是递归的一种实现结构,前中后遍历的逻辑也可以用栈使用非递归的方式来实现。广度优先遍历的实现一般使用队列来实现,队列是先进先出的结构,这样才能一层层的遍历二叉树。
  链式存储二叉树节点的定义方式如下,相较于链表节点,二叉树节点与其定义差不多,二叉树节点有两个指针分别指向了其左右孩子。
  树节点定义

// 树节点定义
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

  迭代法实现前中后遍历

class Solution {
public:
    // 前序遍历
    void traversal_preOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        vec.push_back(cur->val);                // 中
        traversal_preOrder(cur->left, vec);     // 左
        traversal_preOrder(cur->right, vec);    // 右
    }
    // 中序遍历
    void traversal_midOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;        
        traversal_midOrder(cur->left, vec);     // 左
        vec.push_back(cur->val);                // 中
        traversal_midOrder(cur->right, vec);    // 右
    }
    // 后序遍历
    void traversal_postOrder(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal_postOrder(cur->left, vec);     // 左
        traversal_postOrder(cur->right, vec);    // 右
        vec.push_back(cur->val);                // 中
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        traversal_preOrder(root, result);
        return result;
    }
};

6.4 高度和深度

  高度和深度是相反的表示,深度是从上到下数,而高度是从下往上数。深度指从根节点到该节点最长简单路径边数,而高度指从该节点到叶子节点的最长简单路径边数。叶子节点是指没有子节点的节点。假设根节点的深度和叶子节点的高度为1,那么树的深度和高度是相等的,而对其他节点来说高度和深度不一定相等。例如下图当中,8这个节点的深度为2,高度为4。
在这里插入图片描述
end

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

普中51单片机学习(红外通信)

红外通信 红外线系统的组成 外线遥控器已被广泛使用在各种类型的家电产品上&#xff0c;它的出现给使用电器提供了很多的便利。红外线系统一般由红外发射装置和红外接收设备两大部分组成。红外发射装置又可由键盘电路、红外编码芯片、电源和红外发射电路组成。红外接收设备可由…

C++的stack容器->基本概念、常见接口

#include<iostream> using namespace std; #include <stack> //栈stack容器常用接口 void test01() { //创建栈容器 栈容器必须符合先进后出 stack<int> s; //向栈中添加元素&#xff0c;叫做 压栈 入栈 s.push(10); s.push(20); s…

Windows+Yolo3-darknet训练自己数据集并测试

WindowsYolo3-darknet训练自己的数据集并测试 一、首要条件 Windows 7下配置好VS2015OPENCV3.4.2YOLO3CUDA10.0CUDNN7.5生成darknet.exe。具体配置可参考我的博客&#xff1a;https://blog.csdn.net/wszswllnzn_/article/details/100760477 二.制作数据集 1、方法1 使用软件la…

flutter插件开发基础教程

前言 虽然现在已经有很多插件了&#xff0c;但是有时候还是需要自己开发一个插件。因此打算学习一下如何开发一个插件。这里只考虑安卓&#xff0c;安卓使用kotlin&#xff0c;kotlin不会也没事&#xff0c;我也不会。 参考项目&#xff1a;https://github.com/TBoyLi/flutte…

nrm 镜像源管理工具

1、什么是nrm nrm(npm registry manager )是npm的镜像源管理工具。它可以快速在让你在本地源之间切换。 2、安装 npm install -g nrm 3、查看本地源&#xff08;nrm ls&#xff09; 4、切换 &#xff08;nrm use ***&#xff09; 5 、测试速度&#xff08;nrm test ***&…

spring注解驱动系列--Bean生命周期一

一、Bean生命周期 bean创建--- BeanPostProcessor.postProcessorsBeforeInitialization---初始化----BeanPostProcessor.postProcessAfterInitialization ----销毁的过程 二、管理Bean生命周期 在Bean的生命周期中&#xff0c;我们可以认为的进行干预Bean的创建到销毁的过程&…

石头剪刀布游戏(C语言)

题目描述 石头剪刀布游戏有 3 种出拳形状&#xff1a;石头、剪刀、布。分别用字母 A , B , C 表示。 游戏规则: 出拳形状之间的胜负规则如下&#xff1a; A > B&#xff1b;B > C&#xff1b;C > A&#xff1b;">"左边一个字母&#xff0c;表示相对优…

ElasticSearch之聚合aggs

写在前面 本文看下es的聚合相关内容。 1&#xff1a;什么是聚合 即&#xff0c;数据的统计分析。如sum&#xff0c;count&#xff0c;avg&#xff0c;min&#xff0c;max&#xff0c;分组等。 2&#xff1a;支持哪些聚合类型 2.1&#xff1a;bucket aggregation 对满足特…

抖音数据挖掘软件|视频内容提取

针对用户获取抖音视频的需求&#xff0c;我们开发了一款功能强大的工具&#xff0c;旨在解决用户在获取抖音视频时需要逐个复制链接、下载的繁琐问题。我们希望用户能够通过简单的关键词搜索&#xff0c;实现自动批量抓取视频&#xff0c;并根据需要进行选择性批量下载。因此&a…

Echarts与后台(mongoose)交互

Echarts引入地址可参考 echarts组件引入 <template><div><div id"main" style"width: 600px;height:400px;"></div></div> </template><script setup> import { onMounted, ref } from vue; import * as echa…

挑战杯 基于卷积神经网络的乳腺癌分类 深度学习 医学图像

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

数字滚动实现

介绍 vue-countup-v3 插件是一个基于 Vue3 的数字动画插件&#xff0c;用于在网站或应用程序中创建带有数字动画效果的计数器。通过该插件&#xff0c;我们可以轻松地实现数字的递增或递减动画&#xff0c;并自定义其样式和动画效果。该插件可以用于许多场景&#xff0c;例如展…

nginx 配置文件详细介绍

一&#xff0c; nginx 配置文件架构 上一篇 已对 main 全局配置做了详细介绍 本章对剩下的配置文件部分做介绍 二&#xff0c;event 设置 &#xff08;一&#xff09;event 相关的配置文件为 配置工作模式以及连接数 &#xff08;二&#xff09;具体表现 1&#xff…

抖音数据抓取工具|视频内容提取软件

引言部分&#xff1a; 介绍针对抖音视频下载需求开发的强大工具突出解决用户获取抖音视频繁琐问题的初衷 工具功能介绍&#xff1a; 详细描述工具功能&#xff0c;包括关键词搜索、自动批量抓取、选择性批量下载等提及基于C#开发的优势以及支持通过分享链接进行单个视频抓取…

k8s的svc流量通过iptables和ipvs转发到pod的流程解析

文章目录 1. k8s的svc流量转发1.1 service 说明1.2 endpoints说明1.3 pod 说明1.4 svc流量转发的主要工作 2. iptables规则解析2.1 svc涉及的iptables链流程说明2.2 svc涉及的iptables规则实例2.2.1 KUBE-SERVICES规则链2.2.2 KUBE-SVC-EFPSQH5654KMWHJ5规则链2.2.3 KUBE-SEP-L…

axios是如何实现的(源码解析)

1 axios的实例与请求流程 在阅读源码之前&#xff0c;先大概了解一下axios实例的属性和请求整体流程&#xff0c;带着这些概念&#xff0c;阅读源码可以轻松不少&#xff01; 下图是axios实例属性的简图。 可以看到axios的实例上&#xff0c;其实主要就这三个东西&#xff1a…

自定义悬浮气泡组件

一.常用悬浮气泡展示 在一个项目中&#xff0c;常常会使用点悬浮展示&#xff0c;而市面上悬浮tooltip的组件非常多 例如常用的antd提供的Tooltip 用法如下&#xff08;来自于官方文档示例&#xff09;&#xff1a; import React from react; import { Button, Tooltip, Con…

129 Linux 系统编程7 ,make 的编写和解析

前文中&#xff0c;我们有多少个.c文件&#xff0c;就需要build 出来多少个.o文件 假设我们的项目很大&#xff0c;怎么管理这些 .c文件呢&#xff1f; 这里就要学习一个make文件的编写了。 makefile 本质上是一个脚本语言 脚本语言实际上就是将一系列命令放在一起执行 mak…

服务器被黑该如何查找入侵痕迹以及如何防御攻击

当公司的网站服务器被黑&#xff0c;被入侵导致整个网站&#xff0c;以及业务系统瘫痪&#xff0c;给企业带来的损失无法估量&#xff0c;但是当发生服务器被攻击的情况&#xff0c;作为服务器的维护人员应当在第一时间做好安全响应&#xff0c;对服务器以及网站应以最快的时间…

程序环境和预处理(1)

文章目录 目录1. 程序的翻译环境和执行环境2. 详解编译链接2.1 翻译环境2.2 编译本身也分为几个阶段2.3 运行环境 3. 预处理详解3.1 预定义符号3.2 #define3.2.1 #define 定义标识符3.2.2 #define 定义宏3.2.3 #define 替换规则3.2.4 #和##3.2.5 带副作用的宏参数3.2.6 宏和函数…