数据结构
未读1. 双向循环链表的结构在上一个章节中为大家详细讲解了双向链表,按照单向链表的思路继续对它进行改进,双向链表的首尾就可以相接,这样就得到了一种新的链表 ——双向循环链表。
1.1 双向循环链表的节点双向循环链表是一种特殊的链表结构,链表中的节点不仅有前驱和后继指针,还需要让链表的最后一个节点的后继指针指向第一个节点,第一个节点的前驱指针指向最后一个节点。
假设双向循环链表的节点存储的是整形数据,那么该节点的定义如下:
1234567// 定义一个节点,此为 C++ 语法struct Node{ int data = 0; Node* next = nullptr; Node* prev = nullptr;};
双向循环链表的节点结构包含三个部分:
数据域(data):存储节点的值
前驱指针(prev):指向前一个节点
后继指针(next):指向后一个节点
在操作双向循环链表的时候通常会定义两个辅助指针:头节点指针和尾节点指针,头指针指向链表的第一个节点,尾指针指向链表的最后一个节点。
和前面讲过的链表一样,双向循环链表也可以分为带头 ...
1. 双向链表的结构对于单向链表和单向循环链表而言有一个共同的特点,就是链表的每个节点都只有一个指向后继节点的指针,通过这个指针我们就可以从前往后完成对链表的遍历。但是开弓没有回头箭,遍历到尾节点之后再想要回到头结点,是无能为力的。
1.1 双向链表的节点为了克服链表单向性这一缺点,聪明的程序猿们便进行了改进,设计出了双向链表。双向链表(Doubly Linked List)是一种常见的数据结构,与单向链表不同的是,双向链表中的每个节点包含两个指针,一个指向前驱节点,一个指向后继节点。双向链表的这种特性使得我们可以从任意节点高效地向前或向后遍历。
假设双向链表的节点存储的是整形数据,那么该节点的定义如下:
1234567// 定义一个节点,此为 C++ 语法struct Node{ int data = 0; Node* next = nullptr; Node* prev = nullptr;};
双向链表的节点结构包含三个部分:
数据域(data):存储节点的值
前驱指针(prev):指向前一个节点
后继指针(next):指向后一个节点 ...
1. 缘起约瑟夫问题又叫约瑟夫环或丢手绢问题。故事据说发生在古罗马时期,在罗马人占领乔塔帕特后,39个犹太人与约瑟夫及他的朋友躲到一个洞中,他们宁愿死也不要被敌人抓到,于是约定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下1个人重新报数,直到所有人都自杀身亡为止。但是约瑟夫和他的朋友并不想死,就开始计算要站在什么地方才能避免被处决。
很显然如果想要逃避死亡,约瑟夫和他的朋友的死亡顺序应该是倒数第一和倒数第二,那么如何才能快速计算出这两个位置呢?
通过上图的推演大家应该是受到了启发,这个问题的本质其实就是循环链表的问题,围成一个圈之后,一个一个顺序报数,这就是相当于是在遍历循环链表。数到对应数字的出列,这就相当于是搜索并删除了循环链表中的一个节点!
2. 解决问题既然处理约瑟夫问题需要用到循环链表,那么这个链表是选择带头结点的还是不带头结点的呢?
仔细分析之后相信大家选择的都是不带头结点的循环链表,因为现在的需求是通过最后一个数据节点直接找到链表中的第一个数据节点,如果是带头结点的循环链表,由于头结点不携带任何数据,在完成一轮遍历之 ...
数据结构
未读1. 单向循环链表的结构在上一个章节中为大家详细讲解了单向链表,由于它的每个节点只有一个指向后继节点的指针,通过这个指针我们只能从前往后对链表进行遍历。当遍历到链表尾部的时候再想回到从前是绝对不可能了。想要实现从链表尾部往前遍历有两种解决方案就是使用循环链表或者双向链表。
1.1 单向循环链表循环链表又分为单向循环链表和双向循环链表,我们先来研究单向循环链表。将单向链表的尾节点的后继指针由指向空指针改成指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表就称之为单向循环链表。
在单向循环链表中的每个节点的结构和单向链表是相同的,假设单向循环链表的节点存储的是整形数据,那么该节点的定义如下:
123456// 定义一个节点,此为 C++ 语法struct Node{ int data = 0; Node* next = nullptr;};
1.2 头结点如果对单向循环链表再次进行细分可以分为两类:带头结点的单向循环链表和不带头结点的单向循环链表。
下图是不带头结点的单向循环链表:
如果单向循环链表不带头结点,它的尾节点的后继指针指向的是链 ...
1. 单向链表的结构在上一个章节中为大家详细讲解了静态链表,它解决了插入和删除数据的时候大量移动元素的问题,但是解决不了合理分配和使用内存的问题。解决这个问题的最优方案就是使用动态链表,简称链表。
链表和数组都可以称之为线性表,数组是一种顺序存储结构的线性表,而链表是一种链式存储结构的线性表,链表中的节点和节点之间的内存是不连续的,它们之间的前后关系需要通过指针来维系。
关于链表有很多种,比如:单向链表、单向循环链表,双向链表、双向循环链表,并且这些链表可以带头结点也可以不带头结点。
1.1 单向链表节点我们先从单向链表说起,所谓的单向链表就是链表的节点中只有一个指针域,并且这个指针域指向当前节点的下一个节点(后继节点)的地址。
假设单向链表的节点存储的是整形数据,那么该节点的定义如下:
123456// 定义一个节点,此为 C++ 语法struct Node{ int data = 0; Node* next = nullptr;};
对于上图这个单向链表而言:
链表第1个节点中的Next指针保存了第2个节点中Data1的起始地址
链表第2个节 ...
1. 静态链表的设计1.1 定义静态链表链表是由多个相同类型的节点组成的线性表,它的每个节点都包含一个数据项和一个指向下一个节点的指针,链表中各个节点的地址是不连续的。
下面是一个用于存储整形数据的链表节点结构:
12345struct Node{ int data; struct Node* next;};
data:用于存储实际数据
next:用于连接当前节点的后继节点,存储的是下一个节点的地址(很多资料中将其称之为游标)
静态链表是一种使用数组实现的链表,通常用于环境中无法使用指针的情况。这种结构在内存中是连续存储的,但节点的位置可以变化,因此称为静态链表。
静态链表的节点结构和链表类似,但是不完全相同,当前节点连接后继节点使用的是索引而不是指针。
下面是一个用于存储整形数据的链表节点结构:
12345struct Node{ int data; int next; };
data:用于存储实际数据
next:使用索引的方式记录后继节点的位置
由于静态链表是一个数组,也就是说在存储数据之前元素对应的存储 ...
数组(Array)是 C/C++ 中最基础和重要的数据结构之一,它提供了一种有效存储和访问固定大小元素集合的方式。关于数组的定义和使用相信大家都已经熟练掌握,本文将着重为大家剖析数组的物理结构和逻辑结构。
1. 数组的物理结构数组的物理结构是指数组元素在内存中的实际存储方式。在内存中,数组元素是连续存储的,这意味着相邻元素的地址是连续的,且每个元素占用固定大小的内存空间。
例如,对于一个整型数组 int numbers[5],如果数组的起始地址为 0x1000,每个整型元素占据4个字节,那么数组中的元素在内存中的存储情况可能如下:
123450x1000: numbers[0]0x1004: numbers[1]0x1008: numbers[2]0x100C: numbers[3]0x1010: numbers[4]
这样的存储方式保证了对数组元素的快速访问和遍历,因为可以通过计算地址偏移来访问数组中的任意元素。
2. 数组的逻辑结构2.1 线性表数组是一种线性数据结构,它由相同类型的元素组成,并以连续的内存地址存储。所谓的线性表就是由一个或多个元素组成的有序序列。在 ...
我们都知道,数据结构和算法本身解决的是 “快” 和 “省” 的问题,即如何让代码运行得更快,如何让代码更省存储空间。所以,执行效率是算法一个非常重要的考量指标。那如何来衡量你编写的算法代码的执行效率呢?这里就要用到本章要讲的内容:时间、空间复杂度分析。
1. 大O表示法时间复杂度指的就是算法的执行效率,粗略地讲,就是算法代码执行的时间,用时越短效率越高。但是,如何在不运行代码的情况下,用“肉眼”得到一段代码的执行时间呢?
先看一段简单的代码:
123456789void func(int n) { int sum = 0; for (int i=1; i <= n; ++i) { sum = sum + i; } printf("sum = %d\n", sum); }
从 CPU 的角度来看,执行每行代码的操作都类似,虽然执行时间不尽相同,但是可以假设 CPU 执行每行代码的时间都是一个指令周期,这样就可以轻松算出上面代码的执行时间了:
第 3、8 行,分别需 ...
1. 概述1.1 概念数据结构和算法是计算机科学中的一组重要概念。先看一下官方对这两个概念的定义:
数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。
算法:是指解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个步骤。
为了方便理解,上述概念可以再说的直白一些:
数据结构就是指一组数据的存储方式
算法就是操作数据的一组方法
1.2 为什么要学习数据结构和算法为什么要学习数据结构和算法呢?
想要通关大厂面试,千万别让数据结构和算法拖了后腿
业务开发工程师,不能做一辈子 CRUD(增删改查)
基础架构研发工程师,写出达到开源水平的框架才是目标
对编程还有追求?不想被行业淘汰?那就不要只会写凑合能用的代码
学习数据结构具有多重好处:
提高问题解决能力: 数据结构是组织和存储数据的方式,学习数据结构可以帮助你更好地理解问题的本质,并能够有效地设计和实现解决方案。
提升编程能力: 数据结构是编程的基础,掌握了数据结构可以让你写出更高效、更健壮的代码,提升编程技能和代码质量。
优化算法效率: 数据结构和算法是紧密相关的,选择合适的数据 ...
1. Protobuf 概述protobuf也叫protocol buffer是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python 等,每一种实现都包含了相应语言的编译器以及库文件。
由于它是一种二进制的格式,比使用 xml 、json进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。
1.1 源码安装github源代码下载地址:
1https://github.com/protocolbuffers/protobuf/releases
本篇文章中只介绍如何基于Linux完成protbuf的源码安装,基于提供的官方地址大家可以把自己的心仪的源码包下载到本地。接下来介绍安装步骤:
温馨提示:以下按照步骤只适用于 3.21.12 及以下版本,高于该版本的安装方式就不一样了。
123456789# 以 protobuf 3.21.12 为例# 自行下载源码包, ...