- 博客(53)
- 收藏
- 关注
原创 C++ - 认识C++
随着AI的发展,未来是否会利用AI工具来重写C++代码?AI 工具(如 Copilot)可提升 C++ 开发效率,自动生成代码或优化算法,但难以完全替代人工。例如,复杂的内存管理和性能优化仍需开发者深度参与;C++ 系统的底层逻辑和硬件依赖使得 AI 重写风险极高,尤其在需要严格实时性的场景(如航空航天控制系统);重写,即使在有AI 工具的情况下,仍然是一个巨大的消耗,企业更倾向于维护现有 C++ 系统而非重写,尤其涉及关键基础设施时;若未来 AI 自举编程技术成熟,可能催生更高效的二进制级语言,
2025-03-20 15:08:08
811
原创 数据结构与算法 - 二叉树 #四种遍历方式 #求二叉树的结点个数 #叶节点个数 #树的高度 #第K层的结点个数 #查找值为x的结点 # 销毁 #判断二叉树是否为完全二叉树
1、前序遍历(前根遍历):若二叉树为空,则返回空操作; 否则访问根节点的操作发生在遍历其左右子树之前,即其的访问顺序为:根节点、左子树、右子树2、中序遍历(中根遍历):若二叉树为空,则返回空操作;否则访问根节点的操作发生在遍历左右子树之中,即访问的顺序为:左子树、根节点、右子树3、后序遍历(后根遍历):若二叉树为空,则返回空操作;否则访问根节点的操作发生在遍历左右子树之后,即访问的顺序为:左子树、右子树、根节点;即从左向右先叶子后节点的方式遍历访问左右子树,最后访问的是根节点;4、层序遍历:若二叉树
2024-12-25 08:35:37
1023
原创 数据结构与算法 - 归并排序 #递归版本 #非递归版本 #文件归并
1、归并排序(MERGE-SORT) 是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer) (分治可以理解为递归) 的一个非常典型的应用。将有序的子序列进行合并得到完全有序的序列;即先使每个子序列有序,再使得子序列段间有序。若将两个有序的子序列合并为一个有序的序列,称为二路归并;2、外排序(External sorting)是能够处理极大数据量的排序算法;通常来说,外排序的数据不能一次性全部装入内存之中,只能放在读写较慢的外存储器之中(eg.硬盘)。外排
2024-12-24 15:01:47
978
原创 数据结构与算法 - 快速排序 #hoare版本 #挖坑法 #lomuto前后指针 #三数取中 #三路划分 #introsort自省排序
快速排序是Hoare 于 1962年提出的一种二叉树结构的交换排序方法;其基本思想为:任取待排序元素序列中的某元素作为基准值,然后再根据所得到的基准值的下标将待排序序列分割成两子序列,其中左子序列中所有元素均小于等于基准值,右子序列中给的所有的元素均大于等于基准值,然后其左右子序列又重复该过程,直到所有元素均排到其应该到的相应的位置上为止;
2024-12-24 09:40:19
778
原创 数据结构与算法 - 排序 #直接插入排序 #希尔排序 #直接选择排序 #堆排序 #冒泡排序 #快速排序(hoare、挖坑、lomuto) #非递归版本快速排序 #归并排序 #非递归版本归并 #计数排序
1、直接插入排序:将待排序的数据往有序的序列中插入,将该数据与序列中的数据依次比较,在序列中找到合适的位置并挪动数据将这个位置空出来,然后再将该待排序数据放到该位置上;2、希尔排序:又称缩小增量法;先选定一个整数(通常是 gap=n/3+1), 将待排序序列分为多组,所有的距离相等的数据分在同一组中,并对每一组中内的数据进行排序;然后再gap=gap/3+1 将数组再分组(组会越来越少),并进行插入排序,当gap为1的时候,就相当于直接插入排序;3、直接选择排序:在待排序列中找最大值、最小值,找到之
2024-12-23 19:48:47
1203
原创 数据结构与算法 - 树-堆 相关算法的时间复杂度分析 #向上调整 #向下调整 #向上调整建堆 #向下调整建堆 #堆排序
向上调整建堆是将本节点与其祖先节点进行比较,而节点所在的层数越大,那一层的节点数越多,该层一个节点所要向上调整的次数也就越多,即节点数量多的层*调整次数多,节点数量少的层*调整次数少;而向下调整是将本节点与其子孙节点进行比较,而层数越大,该层的节点数量越多,调整的次数少;层数越小,该层的节点数量越少,调整的次数越多,即节点数量多的层*调整次数少,节点数量少的层*调整次数多;
2024-12-23 18:21:15
774
原创 leetcode解题 - #用栈实现队列 #用队列实现栈 #循环队列
对于此题,我们首先需要思考,该循环队列用什么实现合适;我们先来思考能否用链表实现;必然是会用到循环链表(仅使用单链表实现循环队列是非常麻烦的,因为单链表中的结点(假设该结点存在上一结点与下一结点)只能找到下一结点,找该结点的上一结点是非常麻烦的;当然,可以使用双链表来实现,但是此处还是先考虑"简单、节约空间"的结构是否能解决此问题),循环链表中的尾结点中的next 指向头结点;(假设用单向循环链表来实现会使用到两个变量phead 与 ptail 分别指向循环队列中的头和尾)假设题干要求循环队列有
2024-10-22 21:49:17
945
原创 数据结构与算法 - 树 #数的概念 #二叉树 #堆 - 堆的实现/堆排序/TOP-K问题
1、树是一种非线性的数据结构,它是由 n (n>=0) 个有限结点组成的具有层次关系的集合,将此结构叫做树的原因是因为此结构像一棵倒挂的树,即其根在上,而叶朝下;2、二叉树(Binary Tree) 是n (n>=0) 个结点的有限集合,该集合或者为空集(即空二叉树),或者由一个根节点和两棵互不相交的、分别称为根节点的左子树和右子树的二叉树构成,3、堆是一种数据结构,可以在其中插入、删除数据,让其结构仍为堆;利用堆来进行选数是非常方便的,不用我们自己去建堆、调整。(注:以后在c++中还有一种数据结构
2024-10-18 21:46:21
1039
原创 数据结构与算法 - 队列
队列:只允许在一端(队头)进行插入数据的操作,在另外一端(队尾)进行删除数据操作的特殊线性表,并且队列具有先进先出(FIFO)的特点;队列最好不要用数组来实现,因为尾插或者头删存在一个一个挪动数据的可能性而导致效率低下;可以使用链表来实现,介于当可以用单链表实现的时候就采用单链表,能节约一点空间是一点空间,当单链表不能完成当前问题的时候再考虑使用双链表。队列可以用单链表实现,故而本文中的队列使用单链表来实现的;
2024-10-14 15:28:26
902
原创 数据结构与算法 - 栈
栈是一种特殊的线性表,只允许在固定的一端(栈顶)进行插入和删除元素的操作。进行数据的插入与删除的一端称为栈顶,另外一端称为栈底;栈中的元素遵循先进先出(LIFO:Last In First Out)的原则;压栈:将数据放入栈中的操作称为压栈/入栈,在栈顶进行入栈的操作出栈:将栈中的数据删除也称为出栈,出数据在栈顶荐用数组,因为数组的CPU高速缓存命中率高;
2024-10-13 15:08:47
610
原创 数据结构与算法 - 顺序表与链表的区别
顺序表与链表各有优缺,很难说明二者谁更优;准确来说这两个结构之间是相辅相承的关系;1、顺序表(动态顺序表):逻辑结构上是线性的,物理结构上也上一定是线性结构支持随机访问(利用下标)但是在任意位置上插入或者删除数据需要一个一个地挪动数据以保证顺序表结构的连续性,时间复杂度为O(N),效率不高;并且插入数据,会面临扩容的问题,扩容本身就存在消耗,就地扩容消耗不大(开辟空间,返回该空间的地址),但倘若是异地扩容,便会找新空间,拷贝旧空间的数据到新空间中,释放旧空间中的数据,返回新空间的地址;并且扩容的
2024-10-13 15:08:18
923
原创 数据结构与算法 - 单链表 & 双链表 -- 概念+实现
链表有八种(下述三种特性任意组合):单向或者双向带头或者不带头循环或者非循环1、其中,在单结构中,链表分为单向链表、双向链表;单向链表:上一个结点存放了下一个结点的地址;双向链表:前一个结点存了后一个结点的地址,后一个结点也存放了前一个结点的地址;注:如若想在单链表中找倒数第k个结点,是非常麻烦的,因为单向链表只记录了数据与下一结点的地址,也就是说单链表中的结点不能找到自己的上一结点;但是对于双向链表来说,就完全不是事;并且在双向链表中,删除结点无需保存上一个结点的地址(相较于单链表);
2024-10-12 08:56:31
1078
原创 数据结构与算法 - 动态顺序表 --概念+实现
1、线性表:在逻辑结构上呈现线性,其物理结构不一定呈现线性;线性表包括:顺序表、链表、栈、队列、串等;2、顺序表就是数组,但是在数组的基础上,它还要求数据是从头开始连续存储的,并不能跳跃间隔;并且顺序表作为线性表的一种,其逻辑结构与物理结构均呈现线性;3、线性表的实现定义动态顺序表的类型初始化顺序表功能接口函数的实现(增删查改打印)一些细节的接口函数(销毁动态开辟的空间)小技巧,倘若想迅速搓出一个顺序表,可以先写指定pos位置的插入、指定pos位置的删除这两个函数,头插、尾插、头删、
2024-10-12 08:55:30
1056
原创 解题 -- 有关二进制的题型总结
我们可以将奇数位上全是1的数与目标数值进行按位与,得到目标数值中奇数位上数据,再利用右移操作符>> 将奇数位上的数据向右移动一位;同理,可以将偶数位上全是1的数与目标数值进行按位与,得到目标数值中偶数位上的数据,然后再利用左移操作符
2024-09-24 21:48:30
856
原创 数据结构与算法 #时间复杂度 #空间复杂度
算法的时间复杂度是一个函数,它定量地描述了该算法的运行时间(该算法执行所消耗的时间);理论来说,时间复杂度是不可能算出来的,只有当此程序在机器上跑起来的时候,才可以知道此算法运行的时间;注:此处所述的函数,并不是在C语言中的那个函数,而是在数学之中的函数表达式;时间复杂度为什么不是用来计算该代码运行了多少秒呢?因为并没有一个准确的方式去计算一个算法运行了多少秒;相同的算法在不同的机器上,是有差异的,即一个算法的执行速度受环境的影响;那么时间复杂度究竟计算的是什么呢?计算的是一个算法之中基本
2024-09-23 19:00:45
1165
原创 解题-offsetof 宏的模拟实现
看图容易理解,但是若想要通过计算得到偏移量该如何做呢?如果起始位置的地址为0的话,此时取出成员a 的地址便是其偏移量,同理取出成员i 、成员c 的地址对应的也是其偏移量;即当起始地址为0的时候,相应地取出其地址便是得到了其偏移量;那么如何让结构体变量的其实地址为0呢?0这个地址的空间是不可以进行访问的,但是并不代表不可以使用这个地址;只要将地址0进行强制类型转换成该结构体变量类型的地址;
2024-09-17 20:49:39
366
原创 函数栈帧的创建与销毁
在写代码的过程中,你是否会有疑问,当你越界打印数据的时候,屏幕上会出现"烫烫烫烫……" 或者 “屯屯屯屯……” ,这是为什么呢?这是由于在Debug 模式的第四步中,会将所有分配出来的栈空间的每一个字节均初始化为0xCC,而 0xCCCC所对应的汉字编码便是"烫" ; 在堆区中,编译器会将分配出来的堆空间中的每一个字节初始化为 0xCD ,而0xCDCD所对应的汉字编码便是“屯” ;局部变量是怎么创建的?为什么局部变量的值为随机值?函数名可以表示函数的地址,函数如何调用的?其传参的过程又是怎样
2024-09-17 16:11:48
792
原创 解题 - 模拟实现 atoi
atoi 函数的要求:1、在字符串中如果首先遇到的是空白字符串,便会跳过这些空白字符串(可以利用库函数 isspace 进行操作)2、可以识别 + 或者 - , 即atoi 返回的整型可以带上符号;3、遇到数字字符便会将数字字符转换成数字;如果遇到非数字字符便会直接返回;4、函数atoi 返回的值可能会存在超出int 所表示的数据的范围;
2024-09-13 11:13:37
1092
1
原创 通讯录 - 文件版本
想让数据做到持久化,又两种形式,一是文件,二是数据库;本篇博文便是以文件地形式让数据做到持久化;将数据写入文件的这步操作应该放在那个过程呢?显然,我们想要的效果是,当你退出该程序的时候,可以将数据写入文件中保存起来;当然,当我们保存文件之后,还希望当此通讯录程序再次运行起来的时候,程序可以从文件中读取先前保存的数据,我们仍然可以看到之前的信息;综上,我们需要增加两个功能;一是,当程序退出的时候,可以将该程序中已经存在的信息写入文件之中;二是,当程序再次运行起来的时候,可以加载文件中的信
2024-09-13 09:24:33
1070
原创 通讯录 - 动态内存开辟版本
在静态通讯录版本的基础上,动态版本的通讯录其内存根据数据的数量因而地制宜地开辟空间,为避免内存泄漏,不使用该程序时,自然要将此动态开辟地空间给释放掉;因地制宜地开辟空间便要采取三点:一是通讯录需要三个变量来维护,指向动态开辟空间的指针、记录通讯录中存了多少信息的变量以及当前通讯录的容量;二是,在每次存放数据的时候均要判断当前空间是否足够,不足便要进行扩容的操作;三是,当退出此程序的时候,要对动态开辟的空间进行释放;其他的步骤与静态版本的别无二致;
2024-09-11 18:40:58
947
原创 通讯录-静态版本
此通讯录要存放得下人得信息,而人的信息又是怎么样的呢?人的信息,必然是包含人的姓名、年龄、性别、电话、地址等信息,自然此处用结构体变量来存放人的信息;通过观察生活中手机上通讯录功能,我们可以大致列下一下功能(增删查改):存放100个人的信息(此处为静态通讯录)可以增加联系人可删除指定联系人查找联系人修改指定联系人的信息对联系人的信息按照一定的依据进行排序显示联系人
2024-09-11 10:25:24
1144
原创 C语言 预处理详解(二) #命令行定义 #条件编译 #文件包含 #其他预处理指令
1、命令行定义:许多C的编译器提供了一种能力,允许命令行中定义符号,用于启动编译的过程;2、条件编译:在某些条件下的编译,即条件满足便去编译,条件不满足便不编译;3、常见的条件编译指令:1、条件编译#if 常量表达式 //……#endif//常量表达式由预处理器求值2、多个分支的条件编译#if 常量表达式 //……#elif //……#else //……#endif
2024-09-10 10:51:20
966
原创 C语言 - 预处理详解(一)#预定义符号 ##define #undef
1、在C语言本身便预定义了一些符号,这些符号是可以直接使用的;__FILE__ //进行编译的源文件(文件名:路径+主干名+后缀) %s__LINE__ //文件当前的行号 %d__DATE__ //文件被编译的日期 %s__TIME__ //文件被编译的时间 %s__STDC__ //如果编译器遵循 ANSI C,其值为1,否则未定义 %d2、#define 定义的标识符语法: #define name stuff
2024-09-08 21:40:35
1246
原创 C语言-程序环境 #预处理 #编译 #汇编 #链接 #执行环境
预处理阶段主要处理的是那些源文件中# 开始的预编译指令;比如:#include #define ,处理#include 预编译指令,将包含的头文件的内容插入到该预编译指令的位置;这个过程是递归进行的,也就是说被包含的头文件也有可能会包含其他文件;将#define 定义的标识符常量给替换掉,并且删除所定义的符号;删除注释(PS: 注释是给程序员看的,这些注释对于程序本身来说没什么用,于是乎在预处理阶段便删除了)
2024-09-06 19:22:39
1172
原创 解题--有关动态内存开辟 几道经典的笔试题
当函数GetMemory 返回的时候,其函数栈帧便会销毁,紧接着便会返回调用GetMemory的位置,然后执行以下的代码:printf(str) ; printf 为库函数,在使用的时候也会创建属于库函数printf 自己的函数栈帧,即会对Test 函数栈顶上的空间进行重新使用,那么原来GetMemory函数中变量p 所占的空间里存放的数据便很有可能被覆盖;而一旦被覆盖,那么此空间中的值便被改变了;
2024-09-04 19:49:15
924
原创 C语言 文件 #程序文件 #环境文件 #文件的打开与关闭 #流 #文件缓冲区 #文件的顺序读写 #文件的随机读写
流,FILE* 类型的指针便称为"流";针对文件、键盘、显示器(屏幕)、网络、U盘、软盘、光盘、打印机等外部设备上的数据的读写都是通过 '流'来进行的;当数据需要从输入流中读取到内存中时,可以将数据转移到缓冲区中进行暂存。 当数据需要从外存写入到输出流中时,可以先将数据存放在缓冲区,当缓冲区满了之后,再一次性写入到输出流。 这样可以避免频繁地打断操作系统;
2024-09-04 12:40:38
848
原创 解题-写一个程序判断当前机器的大小端存储模式 #两种方法
核心:整型变量会向内存申请 4 byte 的空间来存放数据,此时给这个变量一个值 :1;然后取其第一个字节空间的数据看是0还是1;是0则为大端字节序,因为大端字节序存储模式会将低位字节序的数据放在高地址处;而若是1,则就为小端字节序,因为小端字节序的存储模式会将低位字节序的数据放在低地址处;
2024-08-30 20:37:27
974
原创 C语言 动态内存管理 #动态内存函数的介绍 #常见的动态内存错误 #C\C++ 程序的内存开辟 #柔性数组
柔性数组的特点结构体中的柔性数组成员前面必须至少有一个其他成员;sizeof 返回的这种结构的大小不包括柔性数组所占的内存;包含柔性数组成员的结构体需要用malloc 对其进行动态内存的分配,并且所分配的内存应该大于结构体的大小以适应柔性数组的预期大小;
2024-08-29 21:35:44
852
2
原创 c语言 自定义类型--枚举 、联合 #枚举类型的定义 #枚举的优点 #枚举的使用 #联合类型的定义 #联合的特点 #联合大小的计算
1、枚举的关键字 : enum ;在声明的时候,带上标签名、成员列表、变量列表(可省略),但是在成员列表之间与结构体不同的是,枚举的成员为带有值得字符无需带上类型,而结构体成员是需要带上变量类型的;且在枚举成员之间是用 , 隔开,最后一个成员不需要 , ,而结构体的每一个成员后面都需要带上 ;2、联合的关键字: union ;在声明的时候,需带上标签名、成员列表、变量列表(可省略),联合的成员之间和结构体成员之间一样用 ; 隔开;但是其成员在内存的分配上不同,联合的成员会共用内存空间,
2024-08-26 19:00:35
1263
原创 C语言 自定义类型-结构体 #结构体类型的声明 #结构体的自引用 #结构体内存对齐 #结构体位段的实现
1、第一个成员总是存放在结构体变量申请的空间偏移量为0的地址处;2、其他成员变量(除第一个成员变量以外的成员变量)要对齐到其对齐数的整数倍偏移量的地址处;对齐数的计算原则:对齐数 = 在该成员所占空间的大小 与 编译器默认对齐数中间取一个较小值;注:在VS编译器中,其默认对齐数为8(默认对齐数可进行修改,文章下面会讲述);可以说,只有在VS编译器上有默认对齐数的概念,而在其他编译器上,没有默认对齐数的概念,那么在其他编译器上,其对齐数就是该数据自身的大小;
2024-08-23 21:20:10
1222
原创 解题—求两数的最大公约数与最小公倍数 #辗转相除法
(a,b)-->求 a,b 的最大公约数(a,b)本质原理:a/b=q…r如果有两个整数a,b 便会存在唯一的一个整数q和r,满足式子 a/b=q...r经过上面图形的启发,我们可得知:(a,b)=(b,r)如何验证这个公式呢?由a/b=q…r,可得a=b*q+r;以及r=a-b*q;假设(a,b) = d1; 则可以得到 a= d1*m,b=d1*n,其中(n,m)=1,即m与n 互质;存在n与m均属于整数那么可知:r=d1*m -di*n*q= d1(m-ng);
2024-08-22 23:18:05
3207
原创 内存库函数的使用与其模拟实现 #memcpy #memmove #memcmp #menset
memcpy 内存拷贝--> C语言标准中明确说明memcpy 可以不考虑重叠拷贝memmove --> 可以实现重叠拷贝memcmp --> 将内存中的数据一个字节一个字节地比较memset --> 不用利用循环便可以批量改变内存中的数据
2024-08-13 16:15:30
1128
原创 字符串查找 - 模拟实现strstr 、BF算法 、 KMP算法
KMP算法是一种字符串匹配算法,由D.E.Knuth.J.H.Morris和V.R.Pratt提出,因此被称为克努特-莫里斯-普拉特操作(简称KMP算法)。该算法的核心是利用匹配失败后的信息,尽量减少使用子串与主串的匹配次数,即在匹配成功得串中找与字串其前段字符匹配的串,以达到快速匹配的目的;具体实现是通过一个数组:next 数组,每次匹配失败后j 回退的下标的信息在next 数组中(此next 数组中会包含字串的局部匹配信息);想要得到数组需要通过写函数计算得到;
2024-08-12 21:25:00
1144
原创 常见 字符串库函数 的使用与模拟实现 #strlen #strcpy #strcat #strcmp#strstr #strtok #strncpy #strncat #strncmp
strstr --> 查找一个子字符串的函数(在一个字符串中找另外一个字符串的函数)strstr 的工作原理: 在 str1 所指向的字符串中查找str2 所指向的字符串;如果找到了,然后就返回在str1 中找到 str2 的起始位置;如果没有找到就返回空指针 NULL;
2024-08-10 15:58:26
1432
原创 解题—杨氏矩阵
补充说明:时间复杂度小于O(N) 是什么意思?如果此数组有n 个元素,那么在找其中某一个元素时,在其最坏的情况下找了n 次才找到,此时的时间复杂度便是O(N);同样说明,此处不能采用利用循环来遍历查找;
2024-08-09 08:53:25
655
原创 解题 - 左旋字符串的三种解法(思路)
思路一:循环;创建临时变量来支持整个数组的前移;思路二:利用库函数strcpy 与 strncat +创建一个足够大的数组思路三:发现规律;翻转的巧妙
2024-08-04 21:03:26
605
原创 C语言 #函数 #库函数 #自定义函数 #形参和实参 #嵌套调用和链式访问 #函数的声明和定义 #函数指针 #函数指针数组#函数递归 #回调函数 #库函数qsort #利用qsort 思想实现冒泡排序
回调函数是什么?回调函数就是通过函数指针调用的函数;如果你将一个函数的地址作为一个参数传给另外一个函数,当此指针被用来调用所指向的函数的时候,被调用的函数就是回调函数;回调函数不是由该函数的实现方直接调用,而是在特定的事件或者条件发生时由另外一方函数调用的,用于对此事件进行响应;
2024-08-02 11:43:57
1283
原创 C语言 #具有展开功能的排雷游戏
游戏思路:当玩家输入自己的选择后,要么退出游戏,要么进入排雷游戏;当玩家进入排雷游戏时,就要先给玩家展示一下棋盘,让玩家输入想要排查的坐标,计算机再判断玩家输入的这个坐标是不是'雷',如果是雷,游戏结束;当然菜单会再出现一次,玩家可自行判断要不要再来一局游戏;如果不是雷就要向玩家展示此坐标周围雷的个数,并且玩家会一直输入坐标进行排雷直到玩家踩到雷或者玩家排雷成功;
2024-07-28 20:43:48
1349
原创 C语言 #指针数组 #数组指针 #数组参数、指针参数
1、指针数组是一个存放指针的数组。2、想要访问指针数组中存放的数组名中的元素便有两种方式,一是通过数组的下标进行访问;二是利用地址的方式进行访问;3、数组名通常表示的都是其首元素的地址;但是有两个例外:1、sizeof(数组名) ,这里的数组名表示整个数组,所以计算的是整个数组的大小2、&数组名,这里的数组名表示的依然是整个数组,所以 &数组名 取出的是整个数组的地址;4、数组指针的初始化:想要存放数组的地址,指针变量类型中的[ ]里必须要标明数组的元素个数5、[ ] 的优先级高于*
2024-07-25 11:00:09
2478
2
原创 C语言 #字符指针
以例子为导向来分析字符指针的中存的是什么,以及常量字符串在内存中是如何存储的。1、字符指针就是指针变量指向的对象是字符类型,即指针变量的类型为 char*;2、指针p中存放的是常量字符串在常量区存放的首字符 'a' 的地址3、常量字符串存放在内存中的常量区,常量区中的数据只能读取而不能被修改。
2024-07-20 11:46:31
2809
1
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人