数据结构与算法学习笔记
文章目录
C语言备考
参数传递时都得加*【为了确保能改动其本身,即使二级指针也是这样:指向指针的指针】,同时在用参数时注意要把地址转换回来
最好别用 typedef listNode* linkedlist
,没多大用,主要是简化了一点,设计时别用
第一章 绪论
-
数据结构三要素:逻辑结构、存储结构、数据的运算;
其中逻辑结构包括线性结构(线性表、栈、队列)和非线性结构(树、图、集合)
数据的存储结构主要有顺序存储、链式存储、索引存储和散列存储
链式存储设计时,各个不同结点的存储空间可以不连续,但结点内的存储单元地址必须连续
-
数据是信息的载体,是描述客观事物属性的数、字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。
-
数据元素是数据的基本单位,可由若干数据项组成,
数据项是构成数据元素的不可分割的最小单位,
-
数据对象是具有相同性质的数据元素的集合,是数据的一个子集
-
数据结构:带结构的相同性质数据元素集合。结构是一种或多种关系。
形式定义:Data_Structure = (D,S) D为数据元素的有限集,S为D上关系【数据关系】的有限集
-
数据类型是一个值的集合和定义在此集合上的一组操作的总称
-
数据类型包括:原子类型、结构类型、抽象数据类型
-
数据的逻辑结构和存储结构是密不可分的,算法的设计取决于所选定的逻辑结构,而算法的实现依赖于采用的存储结构
-
施加在数据上的运算包括运算的定义和实现。
运算的定义是针对逻辑结构的,指出运算的功能;运算的实现是针对存储结构的,指出运算的具体操作步骤
-
在存储数据时,通常不仅要存储各数据元素的值,而且要存储数据元素之间的关系
-
对于两种不同的数据结构,逻辑结构或物理结构一定不同吗?
数据运算也是数据结构的一个重要方面。对于两种不同的数据结构,他们的逻辑结构和物理结构完全有可能相同(比如二叉树和二叉排序树)
-
算法是对特定问题求解步骤的一种描述,它是指令的有限序列,其中每条指令包括一个或多个操作。
-
算法的五个特性:有穷性、确定性、可行性、输入、输出(字面意思,第一遍看的话建议看看书具体概念)
-
通常设计一个好的算法应考虑:正确性、可读性、健壮性、效率与低存储量需求
-
算法的时间复杂度不仅依赖于问题的规模n,也取决于待输入数据的性质
-
若输入数据所占空间只取决于问题本身而和算法无关,则只需分析除输入和程序之外的额外空间
-
算法原地工作是指算法所需辅助空间为常量,即O(1)
-
一个算法应该是问题求解步骤的描述
-
所谓时间复杂度,是指最欢情况下估算算法执行时间的一个上界
-
同一个算法,实现语言的级别越高,执行效率越低
通常要求同一逻辑结构中的所有数据元素具有相同的特性,这意味着(不仅数据元素所包含的数据项的个数要相同,而且对应数据项的类型要一致 )
有序表与存储结构无关系
有序表和顺序表不一样。 有序表中的“有序”是逻辑意义上的有序,指表中的元素按某种规则已经排好了位置。 顺序表中的“顺序”是物理意义上的,指线形表中的元素一个接一个的存储在一片相邻的存储区域中。
第二章 线性表
-
线性表是具有相同数据类型的n个数据元素的有限序列。【也可能为空】
-
线性表特点:表中元素个数有限;表中元素具有逻辑上的顺序性;表中元素都是数据元素;表中元素的数据类型都相同,即每个元素占有相同大小存储空间;表中元素具有抽象性,即仅讨论元素间的逻辑关系而不考虑元素究竟表示什么内容
-
线性表是一种逻辑结构,表示元素之间一对一的相邻关系。
其顺序存储:顺序表;其链式存储:单链表、双链表、循环链表、静态链表。
-
线性表的顺序存储又称顺序表,特点是表中元素的逻辑顺序与其物理顺序相同。
-
线性表的顺序存储结构是一种随机存取的存储结构,通常用高级设计语言中的数组来描述线性表的顺序存储结构(线性表中元素位序从1开始)
-
顺序表最主要特点是随机访问。
它的存储密度高,每个结点只存储数据元素,插入和删除操作需要移动大量元素
-
顺序表插入,删除和查找时间复杂度均为O(n)
-
头指针和头结点区分:不管带不带头结点,头指针都始终指向链表的第一个结点,而头结点是带头结点的链表中的第一个结点,结点内通常不存储信息
-
采用头插法建立单链表时,读入数据的顺序与生成的链表中的元素的顺序是相反的,每个结点插入时间为O(1)一共为O(n)
-
尾插法必须增加一个尾指针使其始终指向当前链表的尾结点
-
双链表中的按值查找和按位查找的操作与单链表相同,但双链表在插入和删除操作的实现上时间复杂度仅为O(1)
-
循环单链表中没有指针域为NULL的结点,故循环单链表的判空条件为它是否等于头指针
-
有时对单链表常做的操作是在表头和表尾进行的,此时对循环单链表不设头指针而仅设尾指针
-
在循环双链表L中,某结点*p为尾结点时,p->next==L;当循环双链表为空表时,其头结点的prior域和next域都等于L
-
静态链表借助数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,这里的指针是结点的相对地址(数组下标)
-
静态链表以next==-1作为其结束的标志
-
通常较稳定的线性表选择顺序存储,而频繁进行插入、删除操作的线性表宜选择链式存储
-
链式存储结构比顺序存储结构能更方便地表示各种逻辑结构
-
顺序存储方式不仅能用于存储线性结构,也可以用于非线性(树和图)
-
若用单链表来表示队列,这应该选用带尾指针的循环链表
-
给定n个元素的一维数组,建立一个有序单链表最低时间复杂度为O(nlogn)
-
单链表中,增加一个头结点的目的是方便运算的实现
-
与单链表相比,双链表的优点之一是访问前后相邻结点更灵活
-
某线性表用带头结点的循环单链表存储,头指针为head,当head->next->next=head时,线性表长度可能是0或1
存储密度是指一个结点数据本身所占的存储空间和整个结点所占的存储空间之比
单链表 < 1
三维数组 A(3,4,5)
bounds[] = (3,4,5)
映像函数常量 constants[] =( 20, 5, 1 ) = 4*5,5,1【数组A各个维度上的数字加一时, 元素在线性结构L上所移动的距离】
有一个 100*90 的稀疏矩阵,数据元素为整型,非 0 元素有 10 个,设每个整型数占 2 字节,则用三元组表示该矩阵时,所需的字节数是(66)
将非零元素所在行、列、非零元素的值构成一个三元组(i,j,v) ;
对于该题:
每个非零元素占3*
2=6个字节,共10个非零元素,需6*10 = 60 个字节;
此外,还一般要用三个整数来存储矩阵的行数、列数和总元素个数,又需要3*2 = 6个字节;
总共:60 + 6 = 66 个字节
头插法(t->next = L->next;L->next = t;)
链表队列默认尾插法(rear->next = t; rear = t;)
矩阵在通常(稀疏因子)<0.05 时称为稀疏矩阵。
设指针变量 p 指向单链表中结点 A ,若删除单链表中结点 A ,则需要修改指针的操作序列为(A )
A.q=p->next;p->data=q->data;p->next=q->next;free(q);
B.q=p->next;q->data=p->data;p->next=q->next;free(q);
C.q=p->next;p->next=q->next;free(q);
D.q=p->next;p->data=q->data;free(q);
注意此时p就是A【因此只能在选项中选择能删除的那一个,不一定要标准的删除形式】
第三章 栈和队列
栈是一种特殊的线性表,允许插入和删除运算的一端称为栈顶 。不允许插入 和删除运算的一端称为 栈底。
-
栈是只允许在一端进行插入和删除操作的线性表,操作特性可以明显的概括为后进先出
-
n个不同元素进栈,出栈元素不同排列的个数为C(n:2n)/n+1,即卡特兰数
-
栈是一种操作受限的线性表,类似于线性表,它也有对应的两种存储方式
-
采用顺序存储的栈称为顺序栈;栈空:S.top==-1;栈满:S.top==MaxSize-1;栈长:S.top+1
-
由于顺序栈的入栈操作受数组上界的约束,有可能发生栈上溢
-
利用栈底位置相对不变的特性,可让两个顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸
-
采用连式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况,通常采用单链表实现
-
栈和队列具有相同的逻辑结构,他们都属于线性表,但是运算不同
-
队列简称队,也是一种操作受限的线性表,队尾插入队头删除,操作特性为先进先出
-
队列的顺序存储,队头指针front指向队头元素,队尾指针rear指向队尾元素的下一个位置
-
循环队列队空:Q.front==Q.rear ; 队满(Q.rear+1)%MaxSize == Q.front;
-
队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针的单链表。当Q.front== NULL且Q.rear==NULL时,链表队列为空
-
通常将链式队列设计成一个带头结点的单链表
-
用链式存储方式的队列进行删除操作时头尾指针可能都要修改
-
压缩矩阵:指为多个值相同的元素只分配一个存储空间,对零元素不分配存储空间
-
特殊矩阵:指具有许多相同矩阵元素或零元素(对称矩阵,上下三角矩阵,对角矩阵)
-
对n阶对称矩阵压缩存储时,需要表长为n(n+1)/2的顺序表
-
二维数组计算地址(行优先)LOC(i,j)=LOC(0,0)+(i*m+j)*L
-
三元组表的结点存储了行、列、值三种信息,是主要用来存储稀疏矩阵的一种数据结构
-
十字链表将行单链表和列单链表结合起来存储稀疏矩阵
-
二叉链表又名左孩子右兄弟表示法,可用来表示树或森林
TODO
栈与卡特兰数,链栈,循环队列,链队,十字链表,二叉链表
第四章 串、数组和广义表
串是一种特殊的线性表,其特殊性体现在( 数据元素是一个字符 )。
空格常常是串的字符集合中的一个元素,有一个或多个空格组成的串成为空格串,零个字符的串成为空串
KMP的next数组求值:0~next[j]-1 与 x~j-1为最长相同前后缀,因而可用j = next[j]来替换
a babaaababaa
-100123112345
011234223456
任何一个非空广义表的表头元素可能是原子元素,也可能是表元素,但其表尾元素一定是广义表。
表头元素的定义为:广义表中的第一个元素。
表尾的定义为:除去第一个元素,其余元素组成的表。
head() 返回列表的第一个元素;
tail() 返回列表的删去第一个元素之后的剩余列表
广义表((a,b,c,d))的表头是( ),表尾是( )。
A.a B.( ) C.(a,b,c,d) D.(b,c,d)
答案:C、B
广义表的深度是指广义表中展开后所含括号的层数,广义表的长度是指广义表中所含元素的个数
设广义表L=((a,b,c)),则L的长度和深度分别为1,2
KMP
模式串前进原理:
前后缀相同,从而两者可以等效替换(直接移过去)
移动标准:前一位的next值【具体设计算法时是当位next值,移动了一位】
//最长相等前后缀不包括整个字符串它本身
//只有当i,j均处于相同前/后缀的后一位了,就可以进行替换了
//next[i] = j 之后紧接着便是 j = next[j]
abcabd
i j
void getNext(string pat,int[] next){
next[0] = -1;//设定底线
int i = 0;//推进用的指针
int j = -1;//前缀长度
while(i < strlen(pat)){
if(j == -1 || p[i] == p[j]){ //j == next[0]
i++;j++;
next[i] = j; //此时i为后缀的下一位,j为前缀的下一位
}else{
//0~next[j]-1 与 x~j-1为最长相同前后缀,因而可用j = next[j]来替换
j = next[j];
}
}
}
做书面题时,注意加一:
a babaaababaa
-100123112345
011234223456
拓展:nextval
有时会遇到:即使 j 跳转了一次,依然比对不上,而且和上一次比对的情况一样,还需要跳转
此时:若能一次跳转到不一样的位置,岂不美哉?
若想达到这种效果,那就是把一样的那种情况跳过就好
j==0 nextval[0] = -1
j>0 str[j] != str[next[j]] nextval[j] = next[j] {对不上,执行原来的next}
str[j] == str[next[j]] nextval[j] = nextval[next[j]] {对上了,执行新的nextval}
同样的,做书面题时,注意加一:
a babaaababaa
-100123112345 next
-10-10-1…
01010
第五章 树和二叉树
节点度数:该节点的孩子个数
结点「深度 Depth」 :根结点到该结点走过边的数量;
二叉树是有序树,二叉树可以为空
对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,
则N0=N2+1;
满二叉树(完美二叉树):二叉树中除了叶子结点,每个结点的度都为 2
完全二叉树:二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布(完全二叉树非常适合用数组来表示:如果按照层序遍历序列的顺序来存储,那么空结点 null
一定全部出现在序列的尾部,因此我们就可以不用存储这些 null 了)
「完满二叉树 Full Binary Tree」除了叶结点之外,其余所有结点都有两个子结点。
「平衡二叉树 Balanced Binary Tree」中任意结点的左子树和右子树的高度之差的绝对值 ≤1 。
建立平衡二叉树:一秒学会 平衡二叉树的调整,非标题党!不简单你打我! (考研数据结构)_哔哩哔哩_bilibili
【从底往上找,找到高度差>=2的根节点,然后从这个根节点到添加节点的路径上,选从根节点开始的三个点调整,得到后其他节点直接根据查找二叉树性质得到】
已知一颗完全二叉树的第5层有8个叶节点,则该完全二叉树节点最多有多少个?1+2+4+8+8+8+2*8 = 47
B树也称B-树,它是一颗多路平衡查找树。(一般用字母m表示阶数。当m取2时,就是我们常见的二叉搜索树。)
(80条消息) 图解B树的原理及操作_土豆西瓜大芝麻的博客-CSDN博客_b树原理
每个结点最多有m-1个关键字
当超过记录容量后,就需要按顺序分裂来解决(左小,根中,右大)
插入关键字根据顺序放入节点(父节点、子节点均可)
当子节点的容量超过后,会把中间值给根,另一半生成为兄弟节点
若子节点给的超过父节点容量,父节点会再分裂生成新的子节点,旧的子节点则会成为新子节点的子节点
检验方法:查找树性质(左边的所有节点均小于根节点)
B-树中的叶子节点为返回查找失败的节点,不保存关键字
高度为 4 的 3 阶 B-树中(第 4 层是叶子结点),最多有( 26 )个关键字 (1+3+9) * (3-1) = 26
B+树的不同在于将分裂点标记了但根没保存记录,就是5个节点会分为2个和3个,根则一个都没有
遍历主要在于找到根,一层中,根只有一个(以此来找)
由图快速得到遍历结果:7.2. 二叉树遍历 - Hello 算法 (hello-algo.com)
因为二叉树有左孩子、右孩子之分,故一棵树转换为二叉树后,这棵二叉树的形态是唯一的。
树转换为二叉树:1.连兄弟 2.保留一个孩子,抹除其他孩子
森林转换为二叉树:1.每棵树转换为二叉树 2.多个二叉树的根之间连线
二叉树的线索化是将二叉链表中的空指针改为指向前驱或后继的线索。而前驱或后继的信息只有在遍历时才能得到,因此线索化的实质是遍历一次二叉树
线索树是基于二叉链表构建的,所以能用来指向的是还处于空的指针
赫夫曼树(最优二叉树)构建:
1.生成n棵只有一个节点的森林F。2.将F中最小的两棵树作为左右节点,根节点为两个叶节点之和,和为一颗树(原来树弃之不用了)。3.重复步骤2直到F中只有一棵树
赫夫曼树的所有权重都位于叶节点
树带权路径长度:带权值的节点值 x 深度 【赫夫曼树就是 叶节点值 x 深度】
构建小顶堆:先按原先数列构建完全二叉树,然后从底到顶逐步调整为小顶堆
第六章 图
度(degree)是图论中的基本概念,指与图中指定节点相连的边的条数
不存在重复边且不存在顶点到自身的边则为简单图
在有向图中任意一对顶点都是强连通的(有路径),则为强连通图
最短路径查找:
Dij:【算法】最短路径查找—Dijkstra算法_哔哩哔哩_bilibili
每次从未标记的节点中,选择距离出发点最短的点,标记
得到刚标记的节点,与临近节点的距离,计算(其距离+该点到原点的距离),若小于原来的值就更改
floyd:Floyd-傻子也能看懂的弗洛伊德算法(转) - Yuliang.wang - 博客园 (cnblogs.com)
矩阵序列(A)即为以A为中转,重新划定B、C、D…之间的距离
连通图的生成树是包含图中全部顶点的一个极小连通子图。若图中顶点为n,则它的生成树含有n-1条边
最小生成树:
prim算法:首先选的还是最小的边,将边上两点中的一个作为顶点,选择较小边,然后根据较小边继续选择连接(稠密图)
(克鲁斯卡尔算法)Kruskal算法:在全图,选择最小的边(稀疏图)
这两种算法只要能将所有点连起来即可
图的BFS生成树的树高比DFS生成树的树高要(小或相等)、
在路径系列中,顶点不重复出现的路径称为简单路径
除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路
AOV网:无权值,有前驱,有后驱
拓扑排序:选择无前驱的节点,输出,然后删除该 节点和以它为起点的边,然后继续
AOE网:有权值,有前驱,有后驱,
AOV与AOE都是无环图
带权有向图中,顶点为事件,有向边为活动
关键路径:最大路径长度,这一路径上的活动一定是关键活动
最早发生时间,理解:只有全部准备工作完成后才可进行,因此要选最大
最迟发生时间,理解:从最早发生时间的结果往前推,减去最长的活动时间,得到的就是最晚可以发生的时间点【也就是我最迟在那一点开始干活,我也可以达成最好的效率】
关键路径,理解:这一路径上的活动不能托【最早=最晚】,事关整个工程时间,缩短工程时间也是缩短它
顶点(最早发生找多路中的最大值,最晚发生逆向减去最小值【一直最晚时间来减】)
边(最早即开始时刻,最晚为到达时刻减去权重)
关键路径(时间差额为0的路径)
第7章 查找
平均查找长度计算:最少也是查找1长度
设顺序线性表的长度为 30,分成 5 块,每块 6 个元素,如果采用分块查找,则其 平均查找长度为 (1+5)/2+(1+6)/2 = 6.5
分块查找中块索引一定有序,而一块之内不一定有序
二叉树的平均查找长度:关于ASL(平均查找长度)的简单总结 - 回忆酿的甜 - 博客园 (cnblogs.com)
【成功查找长度:从根节点到本节点的比较次数(本节点深度)*节点个数;失败查找长度:从根节点到叶节点的比较次数(叶节点深度)*
节点个数】
适合静态查找表的查找方法有顺序查找、折半查找、散列查找等;适合动态查找表的查找方法有二叉排序树查找、散列查找等
散列表中解决冲突的两种方法是(开放定址法,链地址法)
设哈希表长为 14,哈希函数是 H(key)=key%11,表中已有数据的关键字为 15,38, 61,84 共四个,现要将关键字为 49 的结点加到表中,用二次探测再散列法解决冲 突,则放入的位置是( 9 )。【属于开放地址法】
(49+1)% 11 = 6被占用;(49-1)% 11 = 4被占用;(49+4)% 11 = 9
为什么是4?2的平方
平衡因子:左子树高度- 右子树高度
LL调整:左子树的左孩子下插入(插入后平衡因子变为2,但根据平衡二叉树的特性便可调整回正常的情况,从而解决最开始平衡因子为1的情况)
B-树就是B树(多路平衡查找树)
阶数表示了一个结点最多有多少个孩子结点,阶数为2,就是常见的二叉搜索树
第八章 排序
时间复杂度不受数据初始状态影响而恒为 O(nlog2n)的是(堆排序)
若要求尽可能快地对序列进行稳定的排序,则应选( 归并排序 )
JAVA语言实现
5.栈与队列
栈用ArrayList即可,队用ArrayDeque,强制List的用LinkedList
链栈:头节点为栈顶,注意记录长度
class LinkedListStack{
private ListNode stackPeek;
private int stkSize = 0;
public LinkedListStack(){
stackPeek = null;
}
public int size(){
return stkSize;
}
public boolean isEmpty(){
return size() == 0;
}
public void push(int num){
ListNode node = new ListNode(num);
node.next = stackPeek;
stackPeek = node;
stkSize++;
}
public int pop(){
int num = peek();
stackPeek = stackPeek.next;
stkSize--;
return num;
}
public int peek(){
if(staSize == 0){
throw new EmptyStackException();
}
return stackPeek.val;
}
public int[] toArray(){
ListNode node = stackPeek;
int res = new int[size()];
for(int i = res.length - 1;i >= 0;i--){
res[i] = node.val;
node = node.next;
}
return res;
}
}
链队:头节点尾节点都要有,注意记录长度
class LinkedListQueue {
private ListNode front, rear;
private int queSize = 0;
public LinkedListQueue() {
front = null;
rear = null;
}
public int size() {
return queSize;
}
public boolean isEmpty() {
return size() == 0;
}
public void push(int num) {
ListNode node = new ListNode(num);
if (front == null) {
front = node;
rear = node;
} else {
rear.next = node;
rear = node;
}
queSize++;
}
public int poll() {
int num = peek();
front = front.next;
queSize--;
return num;
}
public int peek() {
if (size() == 0)
throw new EmptyStackException();
return front.val;
}
public int[] toArray() {
ListNode node = front;
int[] res = new int[size()];
for (int i = 0; i < res.length; i++) {
res[i] = node.val;
node = node.next;
}
return res;
}
}
环形数组:
class ArrayQueue {
private int[] nums;
private int front;
private int queSize;
public ArrayQueue(int capacity) {
nums = new int[capacity];
front = queSize = 0;
}
public int capacity() {
return nums.length;
}
public int size() {
return queSize;
}
public boolean isEmpty() {
return queSize == 0;
}
public void push(int num) {
if (queSize == capacity()) {
System.out.println("队列已满");
return;
}
int rear = (front + queSize) % capacity();
nums[rear] = num;
queSize++;
}
public int poll() {
int num = peek();
front = (front + 1) % capacity();
queSize--;
return num;
}
public int peek() {
if (isEmpty())
throw new EmptyStackException();
return nums[front];
}
public int[] toArray() {
int[] res = new int[queSize];
for (int i = 0, j = front; i < queSize; i++, j++) {
res[i] = nums[j % capacity()];
}
return res;
}
}
双向链表:
class ListNode{
int val;
ListNode next;
ListNode prev;
ListNode(int val){
this.val = val;
prev = next = null;
}
}
class LinkedListDeque{
private ListNode front,rear;
private int size = 0;
public LinkedListDeque(){
front = rear = null;
}
public int size(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
private void push(int num,boolean isFront){
ListNode node = new ListNode(num);
if(isEmpty()){
front = node;
rear = node;
}
if(isFront){
front.prev = node;
node.next = front;
front = node;
}else{
node.prev = rear;
rear.next = node;
rear = node;
}
size++;
}
public void pushFirst(int num){
push(num,true);
}
...
private int pop(boolean isFront){
if(isEmpty()){
throw new Exception();
}
if(size() == 1){
ListNode node = front;
front = rear = null;
return front.val;
}
int res;
ListNode node;
if(isFront){
res = front.val;
node = front.next;
front.next = null;
node.prev = null;
front = node;
}else{
res = rear.val;
node = rear.prev;
rear.prev = null;
node.next = null;
rear = node;
}
size--;
return res;
}
...
TODO
...
}
7.二叉树
遍历:
public List<Integer> levelOrder(TreeNode root){
ArrayDeque<TreeNode> queue = new ArrayDeque<>();
queue.addFirst(root);
List<TreeNode> ans = new ArrayList<>();
while(!queue.isEmpty()){
TreeNode node = queue.removeLast();
ans.add(node.val);
if(node.left != null){
queue.addFirst(node.left);
}
if(node.right != null){
queue.addFirst(node.right);
}
}
return ans;
}
public void preOrder(TreeNode root){
if(root == null){
return;
}
list.add(root.val);
preOrder(root.left);
preOrder(root.right);
}
public List<Integer> preOrder(TreeNode root){
List<TreeNode> stack = new ArrayList<>();
List<Integer> ans = new ArrayList<>();
stack.add(root);
while(!stack.isEmpty()){
while(root != null){
ans.add(root.val);
stack.add(root.left);
root = root.left;
}
if(!stack.isEmpty()){
root = stack.remove(stack.size() - 1);
root = root.right;
}
}
return ans;
}
二叉搜索树:
public TreeNode insert(int num){
if(root == null){
root = node;
return node;
}
TreeNode cur = root,pre = null;
while(cur != null){
if(num == cur.val){
return null;
}
pre = cur;
if(num < cur.val){
cur = cur.left;
}else{
cur = cur.right;
}
}
TreeNode node = new TreeNode(num);
if(pre.val < num){
pre.right = node;
}else{
pre.left = node;
}
return node;
}
TODO