C++基础-介绍·数据结构·排序·算法
- 特点
- 使用方向
- RPC
- 数据结构
- 栈 Stack
- 堆 Heap
- 队列 List
- 数组 Array
- 链表 LinkTable
- 树 Tree
- (普通)二叉树
- 二叉查找树 Tree Search
- 平衡二叉树
- 红黑树
- 冒泡排序
- 选择排序
- Insertsort 插入排序
- Quicksort 快速排序
- Binarysearch 二分查找/折半查找
- Blocksearch 分块查找
- Treesearch 树查找
- 哈希算法
- 龟兔赛跑算法
- 递归算法
特点
C++是一门风格严谨又不失自由的开发语言,提供了完整的内存管理、支持函数式编程和面向对象编程,支持模板、多继承、多实现、重载、重写等多态特性。
优势在于目前90%的操作系统、数据库、应用基础架构、硬件嵌入式等都是使用C/C++制作的,而C++是对C的标准扩展,掌握C++的过程中也能比较好地掌握C本身。
缺点也比较鲜明,由于原始指针需要操作内存,并且有各种引用、双重引用、移动语义非常容易让新手望而生畏,即使后来有智能指针后也由于带有额外的资源消耗而被诟病。
最大的缺点在于对中国国内流行的互联网环境支持性不好,在大量Java、Go、Python流行作为服务后端开发语言时,C++却在这方面并没有易于使用的框架或者基础辅助快速构建后端服务。
使用方向
- 底层、基础架构、数据库开发,如RPC协议、Redis、Nginx、RocketMQ中间件
- 音视频
- 游戏引擎
- 网络安全、sdn
- 结合mfc/qt做软件
- linux嵌入式、芯片
- 虚拟化、操作系统
RPC
RPC即远程调用(Remote-Procedure-Call),指的是在分布式应用程序中,由于不同的服务程序处于不同的进程甚至不同服务器中,需要通过各种协议进行调用,例如网站之间可以通过HTTP协议调用彼此的接口,或者通过TCP/UDP协议完成即时传输,而使用RPC是程序之间高效传输的最佳选择。
其中有谷歌通用框架gRPC SDK,包含了在不同开发语言中的RPC实现,还有protobuf(Google Protocol Buffer)实现了一种自定义数据格式用于消息传输,相比xml、json体积更小,解析速度更快。
数据结构
栈 Stack
连续存储空间,出入口为同一个,只能先进后出,后进先出。
栈的分配与删除处理都很快,相当于移动指针,几乎只是一条CPU指令就能完成。
栈会所在作用域结束时会自动删除来释放内存,例如在方法里创建栈变量会在方法结束后删除,甚至可以在方法内部添加花括号来加速栈变量删除。
堆 Heap
栈与堆都是存放在内存里,只是在不同地方,也造成了分配方式不同。
程序专门开辟使用的内存存储空间,采用类似树结构储存,如果开发语言没有设定垃圾收集机制的话,需要程序员手动操作释放或者等程序结束才会回收。
堆的分配和删除很慢,程序需要维护、查找内存空闲列表也有消耗,一般通过new和delete关键字使用,两者都需要好几条指令完成,因此也造成大量资源消耗。
程序需要维护、查找内存空闲列表也有消耗。
new相当于自动调用malloc(memory allocate)分配堆内存。
队列 List
出口和入口各独立一个,先进先出,后进后出。
数组 Array
连续存储空间,有序,带有索引,可重复。可以高效率根据索引查找,但增删时涉及移动其他项导致效率慢。
链表 LinkTable
节点存储有数据和指向下一个节点的地址,多个节点串联起来,因为这种特性让增删节点比数组高效,但查询需要从头到尾遍历导致效率比数组低。
双向链表:让表的链接从头到尾,从尾到头两个方向都有地址指向,让头尾都可进行增删改,因此对头尾元素的处理更方便。
大部分Collection和List都是用数组混合链表,可以比较好地确保存入顺序和高效的增删改查。
树 Tree
从一个根节点出发,每个节点带有若干个子节点,并且这些子节点同样可以带有自己若干个孙子节点,这样就叫树结构。其中主要是二叉树。
大部分Map,Set底层就是链表混合树结构,具体看是否需求有序和排序等。
(普通)二叉树
每个节点上所包含的子节点不超过2个即称为二叉树,没有按照一定规律存储数据的二叉树没有任何搜索查找优化,不值得使用。
二叉查找树 Tree Search
将普通二叉树的数据按照一定规律存储即称为二叉查找树,一般规律是所有父节点都比左子节点大而比右节点小。
这样搜索时,可以通过从父节点开始比对,从而快速决定在左子树还是右子树继续查找,类似二分查找。
平衡二叉树
所有父节点的左右子节点高度差不超过1的二叉查找树就称为平衡二叉树。
在增删数据时,为了保持平衡,二叉树需要进行旋转操作,常见有:
- 左左,在根节点的左子节点中的左子节点添加数据时,需要进行一次右旋达成平衡。
- 左右,在根节点的左子节点中的右子节点添加数据时,可能需要进行一次局部左旋后再进行整体右旋。
- 右右,在根节点的右子节点中的右子节点添加数据时,需要进行一次左旋达成平衡。
- 右左,在根节点的右子节点中的左子节点添加数据时,可能需要进行一次局部右旋后再进行整体左旋。
红黑树
含有颜色标记的非平衡二叉树,规则:
- 节点都为红色或者黑色
- 根节点和Nil叶节点必须为黑色
- 若是没有子节点或者没有父节点,则将该链接点标记为Nil叶节点
- 红色节点之间不能互相连接
- 从同个节点到后代所有Nil叶节点的路径上,每条路径所包含的黑色节点数量都是相同的(通过变红处理)
插入数据时一般默认为红色,根据情况才需要处理为黑色。
冒泡排序
通过不断对比临近索引的值,经过多次循环完成排序。
int arr[7] = {5,1,9,11,3,2,7};
for (int i = 0; i < 7; i++) {}
选择排序
通过先将指定索引的值与所有其他数进行对比交换,之后经过多次循环完成所有指定索引的值的对比。
Insertsort 插入排序
先找到前面有序的部分,和后面无序的分开,之后通过类似冒泡排序的方式完成将后面无序的部分插入到前面有序的部分。
Quicksort 快速排序
先找到中间值,通过顺序和逆序遍历将值根据中间值交换位置。
Binarysearch 二分查找/折半查找
(前提:数据必须是有序的)先拿到中间索引的值进行对比,得到想要的值在左半边还是右半边,之后对所在的半边再次执行拿到中间索引的值进行对比后分半的操作,重复循环直到找到目标。
此方法适合数据量巨大但已经排序好,且分布比较规律的。
如果只是分布不规律可以采用黄金分割或者根据情况判断采用三分之二而非二分之一。
int arr[15] = {1,2,3,6,8,11,19,22,29,48,51,54,62,88,110};
Blocksearch 分块查找
(前提:先完成数据分块,块内可以无序,但块之间必须有序。)根据需要在对应的数据块再进行查找,减少循环所需次数。
Treesearch 树查找
树一般是二叉树或红黑树等已经有序的树结构,可以根据树特性借助递归轻松完成查找。
哈希算法
通过使用哈希表记录的方式,尽可能减少循环次数。
龟兔赛跑算法
通过使用索引先求链表的方式,尽可能减少带有规律的数组的时间复杂度。
/*** 判圈算法找到数组里的重复项(判圈算法又称龟兔赛跑算法)* @param {Array<number>} nums 需要找出重复项的数组*/
function findDuplicate(nums) {let tortoise = 0, hare = 0;while (tortoise !== hare && hare !== undefined) {tortoise = nums[tortoise];hare = nums[hare];if (hare !== undefined) {hare = nums[hare];}}if (hare === undefined) return hare;tortoise = 0;while (tortoise !== hare) {tortoise = nums[tortoise];hare = nums[hare];}return hare;
}
递归算法
通过拆分出重复的规律作为函数并自己调用自己大幅减少内存占用。