- 博客(20)
- 收藏
- 关注
原创 C++笔记-list
因为在之前的string和vector中,它们两个底层都是数组,它们的原生指针就能完成解引用和++等操作,而链表我们知道解引用是访问一个结点,而一个结点不止有数据,还有prev和next,而迭代器我们要求解引用就是直接拿出其中的数据。当传进去的参数类型是自定义类型时,我们要访问其中的数据要用到->符号,但是vector是可以直接用的,因为它用的是原生指针,而list不能使用,因为是自定义类型迭代器,所以在这里我们要自己实现->符号。
2025-04-17 17:59:34
620
原创 C++笔记-vector
因为第二个例子在尾插前就已经扩容过了,此时空间没有满,用的还是原来的空间,所以不会出问题,所以为了解决第一个例子出现的迭代器失效的问题,我们在扩容前就记录下pos和_start之间的距离,扩容后更新pos的位置,这样就能解决问题。我们在这种使用时就会出问题,因为此时的pos是临时对象,而临时对象具有常性,也就是之前讲的权限扩大的问题,可能又会与人说加个const就行了,那又会出现新的问题,这里我就不过多赘述了,而底层用的也是传值调用,所以就按这个来。
2025-04-14 12:34:42
805
原创 C++笔记-string(下)
当时头插时,i只有等于-1时循环才会停止,但是当i等于-1时会出现类型提升的现象,类型提升就是范围小的向范围大的提升,有符号的向无符号的提升,此时i就会向无符号整型提升,-1转为无符号整型是整形最大值,所以这个程序会死循环导致崩溃。这是我们输出时写的形式,可以看出在符号左边是ostream流中定义的变量cout,而参数右边是我们要输出的字符串,所以在实现<<时参数是这两个的原因,并且第一个参数要用引用,道理是和传值调用是一样的,这里就不过多赘述了。此时就会出问题,比较就会停止,所以不能用。
2025-04-07 17:56:50
762
原创 C++笔记-string(中)
但是它的用途并没有size广泛,因为length能做到的size一样能做到,并且size能做到的length不一定能做到,比如:如果想知道到一个二叉树有多大,我们需要的肯定是二叉树结点的个数,而不是二叉树的长度,这点size能做到,但是length就做不到。其实不同的编译器下扩容的规则是不一样的,在vs编译器下除了第一次扩容外,其余每次扩容都是之前容量的1.5倍大小,而在linux编译器下,每次扩容都是之前容量的2倍,扩容规则是由编译器来决定的。此时我们resize的大小已经超出了初始容量。
2025-03-29 17:27:27
988
原创 C++笔记-模板初阶,string(上)
C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C++标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合0OP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。其中的iterator是string类中的一个类型,而begin获得的是字符串的第一个位置,end获得的是字符串最后一个位置的下一个位置,在这里it其实就相当于指针,通过解引用来获取每个字符。1.第三个参数不写的话是有默认值的,这个默认值如果去查的话显示是-1,但其实就是整数的最大值。
2025-03-26 16:31:39
739
原创 C++笔记-内存管理
new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
2025-03-20 17:33:06
667
原创 C++笔记-类和对象(下)
1.之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。3.内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。1.如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
2025-03-17 17:52:32
926
原创 C++笔记-类与对象(中)
第二个构造函数就是全缺省构造函数,而无参构造函数和全缺省构造函数在调用时会有歧义我们之前已经讲过,原因是一样的,他们都叫做默认构造函数,不过我们在实际应用中用全缺省构造函数比较舒服,因为我不想传参我就不传参,我如果想修改那个值,我就传参,还是比较方便的。但在程序结束时,我们知道会调用析构函数,而且是先析构st2,那么st2中_a指针所指向的空间就会被销毁,而轮到析构st1时,st1中的_a的地址也是指向刚才已经被销毁的空间,所以此时就会出现问题,一段空间被销毁两次,这显然是不对的。
2025-03-15 13:20:37
884
原创 C++笔记-类和对象(上)
我举个例子,我们每个人家里有时候会有不同的人去做饭,做的饭也不一样,但是做饭时用的锅是不是都是同一个锅,如果我们每个人都用不同的锅,是不是需要多买几个锅,是不是感觉很浪费。其实是一个道理,成员变量就相当于不同的人,而成员函数相当于锅,我们没必要把成员函数的大小也加进对象的大小中去,因为每个对象调用成员函数时地址是一样的,存储在对象中就浪费了。通过调用程序我们可以知道类的实际大小,我们再通过画图计算发现类的大小只是成员变量的大小,这里可以不用按照我所写的去计算,可以将成员变量的类型变为更小的,这样便于计算。
2025-03-08 18:06:21
731
原创 C++笔记-入门基础
刚开始的using namespace std此时就用第三条可以解释了,就是把展开std中的全部成员,将全部成员放在了全局域中,这样我们要使用std中的变量或者函数是就不需要指定命名空间,而cout和endl都是属于std中的,所以我们在使用它们时,就不需要指定std这个命名空间,这在我们平时学习写代码是比较方便的。namespace名为命名空间,在C/C++中,变量,函数和后面要学的类都是大量存在的,这些变量,函数和类的名称都存在于全局作用域中,可能会导致很多冲突。命名空间中可以定义变量/函数/类型等。
2025-03-05 17:02:01
1111
原创 数据结构-排序
我以这个数组为例,将基本思想实现起来我们可以定义begin和end两个变量,表示我们要在哪个区间里找最大值和最小值,然后定义maxi和mini表示最大值和最小值,起始位置在begin处,在相应区间找到最大值和最小值之后,让maxi位置的值与end位置的值进行交换,mini位置的值与begin位置的值进行交换 ,之后再将begin++,end--,改变区间,就这样一直循环,循环停止的条件就是begin=end或begin>end,此时区间内只有一个数据或没有数据,就可以实现数组的升序排序。
2025-03-01 14:56:48
815
原创 二叉树的链式结构
利用队列来实现,和层序遍历不同的是放入的左右孩子不需要判断是否为NULL,直接放入,当取出的数据为NULL时,循环停止,下面就要判断队列里面的内容,如果队列里没有非空结点,此二叉树就是完全二叉树,反之就不是完全二叉树。利用队列来实现,先把根结点放入队列中,然后把队头数据取出,打印出队头的数据,再把对头结点的左右孩子放入到队列中,放入左右孩子要先判断是否为NULL,不为NULL再放入队列中,重复上述循环,直到队列为空。里面包含了结点中存储的数据,左右子树的地址,这是结点的基本结构。
2025-02-05 17:19:40
708
原创 数据结构-堆及堆排序
因为是向下调整,所以我们要从根结点开始,首先让根结点和子结点进行比较,而在和子结点比较之前,要先比较两个子结点,也就是左子树和右子树,让较大的那个子结点和根结点进行比较,这么做是因为较大的子结点如果比根结点要大的话,那么交换两者的值之后,以根结点为父结点的子树就符合大堆的结构,所以没必要两个都比较。如果子结点比父结点要大,我们就要交换子结点和父结点的值,这样这颗子树才是大堆,之后让新的子结点,也就是之前那棵子树的父结点继续向上比较,直到子结点转移到根结点时结束,因为根结点无父结点。
2025-01-20 20:21:21
1056
原创 数据结构-二叉树
如图就为满二叉树,每一层的结点个数都是最大值,而满二叉树的结点个数我们通过观察可以看出它每一层的个数都是2^(k-1)个结点,我们把每一层的结点个数相加起来就会发现满二叉树的结点个数就是等比数列求和,公比为2,得出总的结点个数为2^k-1个。要注意的是满二叉树是一种特殊的完全二叉树。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。:一个结点含有的子树的根结点成为该结点的子结点;
2025-01-19 20:53:38
880
原创 数据结构-栈和队列
如果我们都取最优的时间复杂度,这时候发现数组和链表都可以,这时我们可以从内存的角度去考虑,数组的话一个空间只需要用4个字节就可以了,而链表则需要8个字节,这里是拿数据为int类型来举例。而如果栈顶和栈底的位置相反,入栈的时间复杂度为O(1),出栈的时间复杂度为O(n),相当于尾插和尾删。如果用链表来实现,入队的时间复杂度为O(n),相当于尾插,出队的时间复杂度为O(1),相当于头删。:我们在测试的时候,要创建的是存放队头和队尾的结构体变量,如果创建的是结点的结构体变量,还是会找不到队尾。
2025-01-17 21:13:43
1108
原创 数据结构-双向带头循环链表
而双向带头循环链表我们初始化的是哨兵位,本身phead指的就是哨兵位的地址,后续的功能并不需要让phead发生改变,就比如让phead移动到第一个节点的位置,并没有,只需要让phead的next和prev发生改变就行。哨兵位的next要指向新结点,当前第一结点的prev要指向新结点,新节点的prev要指向哨兵位,next要指向当前第一结点。哨兵位的prev指的是尾结点,尾结点的next指的是哨兵位,而新创建的节点的prev要指向原来的尾结点,next要指向哨兵位。2.尾删影响的是哨兵位和尾结点的前驱节点。
2025-01-15 20:43:48
1109
原创 数据结构-单链表
淡季的时候,车厢多了,就需要减少几节车厢,这就相当于链表的删除。4.故加一个判断条件判断位置,第一种情况就直接调用上面的头插函数,第二种情况依旧需要prev变量来记录pos的前驱节点,确定好prev和pos的位置后,让prev的next来记录新结点的地址,再让新结点的next记录pos的地址。3.此时不再需要变量来记录前驱节点,此时pos为前驱节点,让newnode的next记录当前pos的下一个结点的地址,再让pos的next记录newnode的地址(如果是最后一个结点,也就是尾插,也是不影响的)。
2024-12-21 16:51:35
966
原创 数据结构-顺序表
因为我要构建的是动态的顺序表,所以用的指针,而上面我对int进行了typedef,这是因为我们顺序表里存储的数据可以不局限于int类型,如果后面想要修改顺序表里的数据类型,就只需要把int改为其他类型即可。2.这里我们先建立一个新变量newcapacity来记录新的空间的容量,如果capacity为0,说明数组现在为空,我们先创建4个大小的空间,反之就把原来的空间扩展到原来的2倍,这里几倍都无所谓,看个人喜好。2.因为是头删,所以要将数组现有的数据向前移一位,将第一位的数据覆盖掉,就实现了头删的操作。
2024-12-14 20:19:52
775
原创 空间复杂度
简单来讲,函数传过去的形参在编译时空间就已经创建,所以形参的变量不算在空间复杂度中,而这个示例中,这是一个冒泡排序,我们可以观察到函数内部有两个新创建的变量end和exchange,这就是函数需要额外申请的空间,但是变量的个数是常数,根据大O渐进表示法,就可以得出空间复杂度了。,在这个例子中递归次数我们知道为N,而在这个函数中并没有创建新的变量,都是对形参N做出改变再传回去,故单次递归的空间复杂度就为1,根据公式就可以计算出空间复杂度。空间复杂度也是一个数学表达式,是对一个算法在运行过程中因为算法的需要。
2024-12-11 17:42:59
397
原创 时间复杂度
这个例子是冒泡排序,计算它的执行次数可以把每次循环的执行的次数写下来,写下来,比如第一次:n-1,第二次:n-2......到最后一次就是1,很明显,这就是一个等差数列,而总的执行次数就是等差数列求和,之后根据渐近法就能找出时间复杂度。就如这个例子而言,func1的基本执行次数:T(N)=N^2+2*N+M,在计算时间复杂度的时候,计算的并不是具体的执行次数,而是大概执行次数,其实就是找对结果影响最大的一项,而找这个就要用到大O的渐进表示法。时间复杂度:O(M+N)时间复杂度:O(N)时间复杂度:O(1)
2024-12-09 23:03:41
303
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人