
C++ 数据结构与算法
文章平均质量分 63
该专栏主要用于考研数据结构复习,出于易学性的考虑,并没有选择严格意义上的C++数据结构实现方法,在一些地方仍然使用C语言的思想,比如使用 struct 而不是 class 等。
Dragon水魅
苦逼程序猿攻城狮。
展开
-
C++ 折半插入排序
折半插入排序是直接插入排序的优化,查找待插入元素的位置时使用折半查找。折半插入排序仅减少了比较次数,并未改变移动次数,故时间复杂度仍为On2O(n^2)On2。算法原理如下:1.设置第一个元素为有序区域,有序区域之后的第一个元素设为“标兵”。2.使用折半查找遍历有序区域,找到对应位置后右移后面的元素进行插入。3.当“标兵”大于某一元素时,将“标兵”插入该位置(因为是有序区域,“标兵”前面的数据一定是有序排列的)。4.更新有序区域和“标兵”, 持续遍历。原创 2023-01-29 16:56:25 · 1628 阅读 · 1 评论 -
C++ 折半查找(二分查找)
总结归纳二分查找的时间复杂度:O(logn) 。二分查找需要线性表有序,对于静态线性表,可以先使用 C++ 的标准库函数 sort() 或 C 语言的标准库函数 qsort() 进行排序,再进行查找。在高校的机试考试中,不必重写一个排序算法,直接调接口即可。二分查找的过程: low 指针指向最左边,high 指针指向最右边,每次判断中间数据 (low + high) / 2 是否等于要查找的值,若中间值小于要查找的值,意味着查找值位于中间点右侧,令 low = mid + 1,缩小一半的查找空间;若原创 2022-02-24 20:44:20 · 2211 阅读 · 0 评论 -
C++ 插入排序
基本原理设置第一个元素为有序区域,有序区域之后的第一个元素设为“标兵”。反向遍历有序区域,如果“标兵”小于其中的某一元素,则使该元素右移。当“标兵”大于某一元素时,将“标兵”插入该位置(因为是有序区域,“标兵”前面的数据一定是有序排列的)。更新有序区域和“标兵”, 持续遍历。以下 gif 参考https://blog.youkuaiyun.com/L_PPP/article/details/108498581:第一次遍历效果:第二次遍历效果:代码实现#include <iostream&原创 2020-10-19 14:35:49 · 278 阅读 · 0 评论 -
C++ 选择排序
基本原理选择排序的原理其实和冒泡排序很像,都是两层循环嵌套:设第一个元素为“标兵”,“标兵”的值为最小值,index 为最小值的 index 。遍历第一个元素之后的所有元素,若找到更小的值,则更新最小值和 index 。交换“标兵”和最小值,实现将最小值放在第一位,“标兵”后移,更新“标兵”。重复以上循环,直至遍历完毕。以下 gif 参考https://blog.youkuaiyun.com/L_PPP/article/details/108498581:代码实现#include <iost原创 2020-10-11 11:02:21 · 303 阅读 · 0 评论 -
C++ 冒泡排序
基本原理比较相邻的元素,如果第一个比第二个大,就交换他们两个。对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。重复以上的步骤,每次比较次数 - 1,直到不需要比较。注意边界的判断,可以代入几个简单的数进行验证。优化方法:可以设置一个哨兵并置为 0 ;每一趟排序后若有变化,将哨兵置为 1 ,继续下一趟。当某一趟排序后哨兵仍为 0 ,意味着这一趟没有数据交换,即剩下的元素已经有序,可以跳出循环停止,防止继续的无效匹配。通过这种方法,我们可以得到以下排序过程:经过分析,我们可以得到原创 2020-09-15 23:06:27 · 334 阅读 · 0 评论 -
C++ 二叉排序树
总结归纳二叉排序树的要求:树根的左子树,均要小于树根;树根的右子树,均要大于树根。对于任意一个结点及其子树,均要满足二叉排序树的要求。基于二叉排序树的基本特性,可知:左子树的最大数据一定位于左子树的最右边;右子树的最大数据一定位于右子树的最左边。对于二叉排序树的构建,依次与树 / 子树的根结点进行对比,小于根结点,放在左边;大于根结点,放在右边,直到插入位置左右子树为空时停止,在该位置插入。二叉排序树的最大查找次数,就是树的深度,类似于折半查找,每查一次排除一半的树。删除二叉排序树的过程: 基于原创 2022-02-23 19:59:24 · 2730 阅读 · 0 评论 -
C++ 二叉树的层次建树及其遍历
总结归纳先序遍历:根左右;中序遍历:左根右;后序遍历:左右根。若选取层次建树,则需要一个链式队列辅助实现 (规定,不必问为什么) 。该辅助队列的具体操作流程::队列结点的数据域 p 存放的是树结点的地址 ( BiTNode *p 类型 ) ,头指针 phead 指向该队列头部,尾指针 ptail 指向该队列尾部,队列指针 pCur 始终指向当前操作位置,当前操作位置指的是:有新结点加入二叉树时,新结点作为队列指针 pCur 所指向的树结点的子孩子。此时,若 PCur 所指向树结点的左孩子为空,将新结原创 2022-02-06 11:20:51 · 2485 阅读 · 0 评论 -
C++ 栈的括号匹配
总结归纳算法思想: 设置一个空栈,顺序读取括号。读取左括号时,将括号入栈;读取右括号时,弹出栈顶元素,栈顶即是与之匹配的最近的左括号。匹配不成功的三种情况:(1)读取右括号时,此时栈空,没有与之匹配的左括号。(2)读取右括号时,弹出的栈顶左括号与之不匹配。(3)顺序读取括号后,栈不为空,即有括号未被匹配。代码实践/*栈的括号匹配*/#include <iostream>#define MaxSize 10 // 栈中元素的最大个数using namespace std原创 2022-02-17 11:22:07 · 4743 阅读 · 0 评论 -
C++ 链式队列(不带头结点)
文章目录总结归纳代码实现总结归纳对于不带头结点的队列 / 链表等数据结构,第一个插入的结点总要特殊处理,而带头结点的数据结构就没有这个问题。对于销毁队列 DestroyQueue(LinkQueue &Q, ElemType &x) ,只需要一个循环判断:若队列一直不为空,则一直出队。其他的数据结构也是类似,通过一个递归实现。PS:返回 bool 类型的好处在这里体现了,while 循环里可以直接通过 ! 判断。// 销毁队列bool DestroyQueue(LinkQue原创 2021-12-14 12:23:32 · 2288 阅读 · 0 评论 -
C++ 链式队列(带头结点)
文章目录总结归纳代码实现总结归纳带头结点的链式队列的初始化,Q.front 指向头结点,Q.front->next 为队头元素,Q.rear 指向队尾。对于链式队列,每一个结点都是一个结构体,这个结构体应该包含 data(数据域,存放数据)和 next(指针域,存放下一个结点的地址),但基于队列先进先出的特性,我们还需要一个队头指针和队尾指针,所以需要再定义一个只包含队头指针和队尾指针的链式队列结构体,这是和之前数据结构不同的地方。值得一说的是 GetHead(LinkQuene &原创 2021-12-13 20:16:36 · 2101 阅读 · 0 评论 -
C++ 循环队列
文章目录总结归纳代码实现总结归纳队列实际上就是现实生活中的排队,队头的人先走,新来的人排到队尾。先进先出,后进后出。队列的实现,需要一个队头指针,一个队尾指针,用于入队和出队。对于循环队列,初始化时需将 Q.front = Q.rear = 0,Q.front 指向队头,Q.rear 指向新元素入队的位置。循环队列,物理上仍然是申请一片连续的内存空间,但通过 (Q.rear + 1) % MaxSize == Q.front 的判断条件从逻辑上模拟环状空间,从而实现循环队列。代码中值得一提的是原创 2021-12-13 13:20:23 · 2198 阅读 · 0 评论 -
C++ 链栈
总结归纳我想了又想,觉得链栈的实现只能是头插法建立单链表,S 为头结点,S-next->data 是栈顶元素,S->next 是栈顶指针,每次弹出就是通过头结点找到栈顶元素,再将栈顶指针后移,其实就是单链表里实现的那一套。其他的形式我实在是写不出来,如果有可以在评论区告诉我,感激不尽。这里为链栈设置了头结点,代码写起来更方便。对于不设置头结点的链栈,我认为也能实现,毕竟都有不带头结点的单链表,这单链表和链栈简直是一家子。在写代码过程中,对 . 和 -> 的使用产生了疑问,经过查询,原创 2021-12-08 17:29:55 · 608 阅读 · 0 评论 -
C++ 共享栈
文章目录总结归纳代码实现总结归纳共享栈的实现,就是申请一块内存,两个栈共同使用同一块内存空间,代码实现/*共享栈*/#define MaxSize 10 // 栈中元素的最大个数#include <cstdlib>#include <iostream>#include <string>using namespace std;typedef int ElemType;struct ShStack { ElemType data原创 2021-12-06 19:48:50 · 925 阅读 · 0 评论 -
C++ 栈
文章目录总结归纳代码实现总结归纳栈的特点是先进后出(First In Last Out / FILO),可以理解为叠罗汉,先来的叠在最下面,要等上面的都抽走了它才能出来。栈的创建、入栈、出栈、查找,都是在O(1)的时间复杂度内完成。关于销毁栈,这里使用的是静态数组,所以只需将S.top重置为-1,即为清空栈(逻辑上),新元素的入栈直接覆盖即可。通过变量声明占用的内存,将在代码结束后由系统自动回收,只有通过 malloc / new 申请的内存空间,才需要手动 free / delete。代码原创 2021-12-06 17:11:00 · 1598 阅读 · 0 评论 -
C++ 静态链表
总结归纳顾名思义,静态链表的长度是不可变的。静态链表的定义,实际上是一个 MaxSize 长度的结构体数组,以此来模拟链表。静态链表的 data 存放数据, next 存放下一个结点的位置。当静态链表遍历时,通过 next 指向的位置来进行遍历,依次达到链表的目的。由第 3 条可知,静态链表在物理空间上是连续的,但在逻辑空间上可以不连续。默认用 next 指向 -1 来表示静态链表的最后一个结点;初始化静态链表时,要初始化 data 来清理脏数据,还要将 next 置为一个特殊值,不能与静态链表原创 2020-11-21 20:33:02 · 1309 阅读 · 0 评论 -
C++ 循环双链表(带头结点)
文章目录总结归纳代码实现总结归纳在 InsertPriorNode 函数(前插操作)中,如果想在表尾处插入结点,则无法进行,需要特殊处理,比较简单,这里没有写出;同时,也可以使用 InsertNextNode 函数(后插操作)来实现。循环双链表的实现与循环单链表大同小异,甚至还更为简洁。由于可以快速找到指定结点的前驱结点,所以很多对头结点、尾节点的插入和删除操作就不用特殊处理。当然,循环双链表要付出一定的内存代价。代码实现/*循环双链表(带头结点)*/#include <ios原创 2020-11-19 20:19:45 · 878 阅读 · 0 评论 -
C++ 循环单链表(带头结点)
文章目录总结归纳代码实现总结归纳在 InsertPriorNode 函数中(前插操作),存在一处 bug ,如果在头结点前进行插入会出现数据混乱的情况,因为头结点不存在数据。初步估计,可以通过遍历找到头结点的前一个结点(即最后一个结点),再进行插入操作。在 DeleteNode 函数中(删除指定结点),如果删除的是最后一个结点,则要特殊处理,需要通过遍历找到该结点的前驱结点,再进行删除操作。循环链表比起单链表,它的优势在于:知道一个结点,就可以知道该结点的前驱结点,以致于所有结点;而单链表必须通过原创 2020-11-18 19:02:02 · 1756 阅读 · 6 评论 -
C++ 单链表(无头结点)
文章目录总结归纳代码实现总结归纳头结点可以没有,头指针必须有。访问整个链表,是用过遍历头指针来进行的。这里没有特别的设置一个头指针,因为当指针指向整个链表 L 时,该指针的实现效果就是头指针。不带头结点的单链表,即单链表的第一个结点就存储数据,头指针也指向第一个结点;带头结点的单链表,第一个结点是头结点,不存储数据,从头结点的 next 开始存储,头指针可以从头结点的 next 开始遍历。对于结点的前插操作,找到对应位置的结点,设新结点为该节点的后继结点,将该结点的 data 后移至新结点的 d原创 2020-11-17 17:34:35 · 5536 阅读 · 3 评论 -
C++ 单链表(带头结点)
#include <iostream>#include <stdio.h>#include <string>using namespace std;// 单链表结点struct LNode { int data; // 数据域 LNode *next; // 指针域};// 单链表struct LinkList { LNode *head; // 头结点 int length; // 单链表长度};//原创 2020-11-17 16:51:07 · 5965 阅读 · 3 评论 -
C++ 顺序表(动态分配)
/*顺序表————动态分配*/#define InitSize 5 // 顺序表初始长度#include <iostream>#include <stdio.h>using namespace std;struct SqList { int *data; // 数组 int MaxSize; // 顺序表的最大长度 int length; // 顺序表的当前长度};// 初始化顺序表void InitList(SqList &L原创 2020-11-02 17:20:55 · 3358 阅读 · 0 评论 -
C++ 顺序表(静态分配)
/*顺序表————静态分配*/#define MaxSize 15 // 顺序表的最大长度#include #include <stdio.h>using namespace std;struct SqList {int data[MaxSize]; // 数组int length; // 顺序表的最大长度};// 初始化顺序表void InitList(SqList &L) {for (int i = 0; i < MaxSize - 5;原创 2020-11-02 16:28:35 · 644 阅读 · 0 评论 -
C++ 实现数组逆置
基本思想思想很简单,我们需要一个临时数组,存放首数据,将尾数据覆盖到首数据时,在将临时数组赋值给尾数据(临时数组就是首数据),再设置一个循环,达到逆置的效果。一个常用的循环结束条件时:当尾数据下标小于首数据下标时(start > end),证明整个数组已经交换过了,及结束循环。代码实现#include <iostream>using namespace std;int main(){ int arr[5] = {1, 2, 3, 4, 5}; int s原创 2020-09-15 23:00:56 · 1758 阅读 · 0 评论