本篇为笔者学习数据结构时,在牛客网站的刷题笔记。
数据结构分为:
面向问题的
】
面向计算机的
】
链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。
链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。
循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数。
长度固定
对象
,不属于原生类
,数组的大小确定之后不可改变。【原生类指未被实例化的类,数组一般指实例化,被分配空间的类】查找与修改
不适合插入、删除
不是
线性结构。【线性结构(串、链式存储栈、顺序存储栈)指逻辑上是线性的,而不是存储上的线性。线性是线性,顺序是顺序,线性是逻辑结构,顺序是存储结构,两者不是一个概念,线性是指一个元素后继只有唯一的一个元素或节点,非线性是一个元素后面可以有多个后继或前继节点,顺序是指存储结构连续,例如数组是顺序的,链表不是顺序的,但他们都是线性的。当然顺序也可以是非线性的,例如顺序结构存储非线性结构的二叉树!!!】一个有限序列,可以为空
。数组越界不能插入
】JAVA数组必须指定行
;C数组必须指定列
,如a[][5]
。
在顺序表中,只要知道基地址和结点大小
,就可在相同时间内求出任一结点的存储地址。
对于不同的特殊矩阵应该采用不同的存储方式。
只有一个根结点的数据结构不一定是线性结构
。线性结构应满足:有且只有一个根结点与每个结点最多有一个前件,也最多有一个后件。所以有一个以上根结点的数据结构一定是非线性结构
。
在程序设计中,要对两个16K×16K的多精度浮点数二维数组进行矩阵求和时,行优先读取和列优先读取的区别是(行优先快
)【行优先免去计算数据存放地址,内存中直接偏移获取下一个数据】
在定义int a[3][4][2];
后,第 20 个元素是(a[2][1][1]
)
三维数组可以看成是一本书!
int a[3][4][2];
就是有3页每页4行2列
8, 8, 4,所以是第三页第四个元素,即第三页第2行第2列。
O(1)
)。【数组具有随机存取的特性,按下标访问元素,所以时间复杂度为O(1)
】【线性表的顺序存储结构
是一种随机存取
的存储结构。】O(n)
设有数组A[i,j],数组的每个元素长度为3字节,i的值为1到8,j的值为1到10,数组从内存首地址BA开始顺序存放,当用以行为内存放时,元素A[5,8]的存储首地址为(BA+141
)。
5,8就证明前面有4*10+7个元素,是141
在java中,要表示10个学生的成绩,下列声明并初始化数组正确的是int score[]=new int[10]
或int[] score = new int[10]
。
哪个Java语句声明了一个适合于创建50个字符串对象数组的变量?:
String a[];
、String[] a;
、Object a[];
【String是Object的子类】
C++写法是int score[10]
。
如果矩阵中大多数元素均为零元素,则矩阵为稀疏矩阵。稀疏矩阵有两种压缩存储方式,一个是三元组
(行, 列, 非零元素值),一个是十字链表
,两者各有适用环境。
采用压缩存储之后,下三角矩阵的存储空间可以节约一半。❌【假设是2*2的矩阵,下三角矩阵有3个元素,只是少了一个元素,没有达到减少一半的空间。】
若稀疏矩阵采用三元组
表形式进行压缩存储,若要完成对三元组表进行转置,只要将行和列对换❌。【①三元组转置(行列互换),②然后再按行排序。】
设有一个 10 阶的对称矩阵 A ,采用压缩存储方式,以行序为主存储, a[1][1]
为第一个元素,其存储地址为 1 ,每个元素占 1 个地址空间,则 a[8][5]
的地址为33
。【对称矩阵只需要存一半!!
】
压缩情况下,稀疏矩阵占用内存最少
将10阶对称矩阵压缩存储到一维数组A中,则数组A的长度最少为(55
)【主对角线都存:10个,剩下的90个只存一半45个,共55个】
设有一个10阶对称矩阵A[10][10],采用压缩存储方式按行将矩阵中的下三角部分的元素存入一维数组B[ ]中,A[0][0]存入B[0]中,则A[8][6]在B[ ]的(42
)位置。
将一个n×n的对称矩阵A的下三角部分按行存放在一个一维数组B中,A[0][0]
存放在B[0]中,那么第i行的对角元素A[i][i]
在B中的存放位置是((i+3)×i/2
)
对一维整型数组a的正确说明是 #define SIZE 10
(换行) int a[SIZE];
若定义char a[10] = “good”,则数组a在内存中所占的字节数(char为1个字节)为10
。
设某数据结构的二元组形式表示为 A=(D , R) , D={01 , 02 , 03 , 04 , 05 , 06 , 07 , 08 , 09} , R={r} , r={<01 , 02> , <01 , 03> , <01 , 04> , <02 , 05> , <02 , 06> , <03 , 07> , <03 , 08> , <03 , 09>} ,则数据结构A是树型结构
。
设以下c语句int array[2][3]={1,2,3,4,5};
中,对数组array定义后,数组元素array[1][2]
的值为:0
。
对于长度为n的线性表,建立其对应的单链表的时间复杂度为O(n)
。
若声明一个浮点数数组如下: float average[]=new float[30];
假设该数组的内存起始位置为200
, average[15]的内存地址是:260
。
float一般为4个字节,以0做下标,计算第15个则不包括第15个,所以只有15个
15*4+200=260
。
new出来的数组也在堆中。
数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n)。【堆内存是用来存放由new创建的对象和数组,即动态申请的内存都存放在堆内存。】
串是字符的有限序列
向后移动
(n-i+1
) 个元素。【前面i-1个元素不动,那么就要移动n-(i-1)个元素了】向前移动
(n-i
) 个元素。若定义:char s[20]=“programming”,*ps=s ;
则不能表示字符‘o’的是(ps+2
) 。【ps为指针,ps+2为一个地址,因此不能表示字符‘o’。】
可以表示字符
o
的有:ps[2]、s[2]、*(ps+2)
当ps为指针时,ps[2]等价于s[2],表示的都是数组中的第3个元素。前者表示指针名称
加下标引用元素,而后者表示数组名称
加下标来引用元素。
*(ps+2),取到了ps[2]的值,因此为‘o’。
*p = a;
,*p
与*a
等同。a[p-a]=0
渐进时间复杂度是指n趋于无穷时的复杂度。向有序表中任意一个位置插入元素,插入位置之后的元素依次挪动一个位置,假设元素插入的位置坐标为k,则时间复杂度为O(k),渐进时间复杂度为O(n)
已知int a[3][4];
则下列能表示a[1][2]
元素值的是*(*(a+1)+2)
。
在数组中,数组名是第一个数组的地址。
注意这里a不是第一个元素的地址,而是第一维
数组的地址,a[0][0]
才是表示的一维数组第一个元素的地址.
地址 + 1表示向下移一层
。
int main(){
int a[N][N]={{0,0},{0,0}};
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
//访问二维数组a的值
//选项代码
}
}
}
*(*(a+i)+j)=1
*(a[i]+j)=1
i控制行,j遍历列
A[1][1]
的存储地址为 420 ,则 A[5][5]
的存储地址为 (472
)。A=array[100][100]
,设每个数据元素占2个存储单元,基地址A[0][0]
为10,则A[5][5]
的地址为(1,020
)。【5*100*2+5*2+10=1020
】19
)。A[5][6][7]
,其中元素A[0][0][0]
的地址为1100,且每个元素占2个存储单元,则A[4][3][2]
的地址是1482
BA+180
)。【(7*8+5-1)*3 +BA
】位置
公式为( i*m+j+1
)。(假设 a[0][0]
位于数组的第一个位置上)【首先a有m列,a[i][j]
表示该元素在i行j列,i*m表示0至i-1行共有几个元素,再加上第i行的j个元素,由于是从a[0][0]
算起,再加上1就是a[i][j]所在位置】【位置是从1开始,下标是从0开始
】16902
108
)。【起始是100之后还有四个】SA+296
)【题目要求8行5列地址,其实求的就是A[7][4]地址,故位置为(710 + 4) 4 = 296】A[m][n]
,每个数组元素占用k个字节,第一个数组元素的存储地址是Loc(a[0][0])
,求按行优先顺序存放的数组元素a[i][j](0 ≤i≤m-1,0≤j≤n-1 )
的存储地址为Loc(a[0][0])+(i*n+j)*k
A[8][5]
的起始地址为(SA+222
)。【陷阱:起始地址为上一地址的结束。小心点】【首先,本题的数组是从A[1][1]开始的,也就是说和所谓的数组从零开始没什么关系。A[1][2]为SA+3.....A[2][1]
为SA+3*10.....A[i][j]
为SA+((i-1)*10+(j-1))*3
。A[8][5]
的起始地址就是SA+ (7*10+4)*3=SA+222.】A[0…m-1][0…n-1]
按行优先顺序存储在内存中,第一个元素的地址为p,每个元素占k个字节,则元素a[i][j]
的地址为(p+[(i*n+j)]*k
)A[8][10]
中(下标均从0开始), 每个元素的长度为3个字节,按列存储时,元素A[4][7]
的起始地址为(SA为数组存储时的首地址)(SA+180
)50
)个字节。对于一维整形数组 a ,以下描述正确的是:
#define SIZE 10
int a[SIZE]
define是设置常量,之后不能更改。
【宏定义后面不需要
分号】
错误的是:int n=10,a[n]
【注意不管啥语言,数组定义中等号左边括号内如果有则必须为常数(你想想要是这句代码后面n变了咋办,数组长度不能改) 】
元素类型 数组名[E];
,E 为(整型常量表达式
)。【常量表达式❌,小数也可以是常量】定义语句"double * array [8]"的含义正确的是array是一个数组,数组的每一个元素是指向双精度实型数据的指针
。
声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*,正确的是(int (*(*p)[10])(int *)
)
先看未定义标识符p,p的左边是
*
,*p
表示一个指针,跳出括号,由于[]的结合性大于*
,所以*p
指向一个大小为10的数组,即(*p)[10]
。左边又有一个*
号,修释数组的元素,*(*p)[10]
表示*p
指向一个大小为10的数组,且每个数组的元素为一个指针。跳出括号,根据右边(int *)
可以判断(*(*p)[10])
是一个函数指针,该函数的参数是int*
,返回值是int
。所以选C。
* (*pc+2)
,先取地址再取值,完成了 元素 引用。【* (pc+3)
、* (pc+1) +3
、pc+1
都是取地址】a数组第1行第2列元素的地址
)(从第0行开始)gets(字符数组名或指针)
gets能够接受空格、制表符Tab和回车等。
gets和sacnf函数,在字符串接受结束后自动加’\0’
数组指针只是一个指针变量,它占有内存中一个指针的存储空间
指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间
unshift()
向数组的开头添加一个或多个元素,并返回新的长度
strcpy(s1, s2);
,strcpy函数执行copy作用,将s2
指针所指数组负值给s1
push()向数组的末尾添加一个或更多元素,并返回新的长度
join()把数组的所有元素放入一个字符串
已知定义数组char a[6],若要用来接收输入5个字符:gets(&a[0]);
、gets(a);
、scanf("%s", a + 1);
scanf("%s", a[0]);
【a[0]不是地址,而是数组元素】在表长为n的顺序表上做插入运算,平均要移动的结点数为n/2
当申明类型为char而传入一个数值时,会默认那个数值为某char类型的ASCII编码。例如char a=48;打印出来a=‘0’.
设二维数组A[0..m-1][0..n-1]
按行优先顺序存储,则元素A[i][j]
的地址为(LOC(A[0][0])+(i*n+j)
)
有些题目是从1开始的,有些题目是从0开始的,但是不管从1还是从0开始,都有差不多的套路。首先给出的是[0][0]的地址,要表示出[i][j]的地址,从第0行到第i行共有i+1行,而在这i+1行当中最后一行只取到第j个,也就是说前面i行中每行是完全满的,所以就是每行乘列数n即in,对于最后剩下的j,因为下标是从0开始,所以它前面有j个元素,所以最后结果LOC(A[0][0])+in+j,同理,对于从1开始的也是一样。还有一个要注意的是,这个题目应该是默认每个元素只占1个单位。
a[0]代表了以a[0][0]为首的一维数组
,也就是将这个char a[2][3]摊成一维数组了,而printf(“%s”,a[0])就是打印这个字符串数组,打印遇’\0’结束。
请问下面哪种方式可以在不改变原来数组的情况下,拷贝出数组 b ,且满足 b!=a 。例如数组 a 为 [1,2,3] 。let b=a.slice();
、let b=a.concat();
。
int (*p)[3]
中p的含义是一个指向元素个数为3的int数组的指针
一个有序数列,序列中的每一个值都能够被2或者3或者5所整除,这个序列的初始值从1开始,但是1并不在这个数列中。求第1500个值是多少?2045
2、3、5的最小公倍数是30。[ 1, 30]内符合条件的数有22个。如果能看出[ 31, 60]内也有22个符合条件的数,那问题就容易解决了。也就是说,这些数具有周期性,且周期为30.
第1500个数是:1500/22=68 1500%68=4。也就是说:第1500个数相当于经过了68个周期,然后再取下一个周期内的第4个数。一个周期内的前4个数:2,3,4,5。
故,结果为68*30=2040+5=2045
设A是n*n的对称矩阵,将A的对角线及对角线上方的元素以列为主的次序存放在一维数组B[1…n(n+1)/2]中,对上述任一元素aij (1≤i,j≤n,且i≤j)在B中的位置为(j(j-1)/2+i
)
用数组r存储静态链表,结点的next域指向后继,工作指针j指向链中结点,使j沿链移动的操作为j=r[j].next
已知数组D的定义是int D[4][8];现在需要把这个数组作为实参传递给一个函数进行处理。下列可以作为对应的形参变量说明的是(int(*s)[8]
、int D[][8]
)。
JDK8之前版本,HashMap的数据结构是怎样的?数组+链表/红黑树
在C语言中,顺序存储长度为3的字符串,需要占用(4
)个字节。
int A[2][3]={1,2,3,4,5,6};
则A[1][0]
和*(*(A+1)+1)
的值分别是(4,5)
A是首地址,
*(A+1)
是第二行,+1
表示第二列,即A[1][1]
给出一个由 n 个数组成的序列 A[1…n] ,要求找出它的最长单调上升子序列,设 m[i] ( 1 ≤ i ≤ n ),表示以 A[i] 结尾的最长单调上升子序列的长度,则 m[1] = 1 , m[i] ( 1 < i ≤ n )为(m[i] = 1 + max{0,m[k](A[k] < A[i],1≤k < i )}
)。【子序列不一定连续】
在一个有8个int数据的数组中,随机给出数组的数据,找出最大和第二大元素一定需要进行几次比较(9
)
将8个数据俩俩分组比较,需要7次得到最大元素,假设为E,那么第二大元素的取值范围为图中的黄色部分,需要2次比较得出,所以一共是9次。
第一次分组8个分成4组 两两一组 比4次 第二次分组为上面比较下来的4个还是两两分组 分2组 第三次分组就剩下两个直接比较出了最大 这时一共比较了4+2+1=7次 接着拿第三次分组中与最大比较的那个数分别和 之前与最大那个数比较过的数比较 分别是第一次分组有一个 第二次分组有一个 7+2=9
定义char b[],既没有确定数组长度,又没有初始值,无法编译通过。
【字符数组如果没有初始化,就必须指定大小,比如char c[10];如果进行了初始化,无须指定大小】
- 采用new关键字创建对象时,每次new出来的都是一个新的对象,也即是说引用s1和s2指向的是两个不同的对象,因此语句System.out.println(s1 == s2)输出:false;
- new创建对象时,首先在字符串池中创建一个字符串对象,然后再在堆中创建一个"aaa"字符串对象,然后将堆中这个"aaa"字符串对象的地址返回引用,这样,s1,s2指向了堆中创建的这个"nowcoder"字符串对象,所以第二个输出语句也是输出false
串中任意个连续的
字符组成的子序列称为该串的子串。
字符串是一种对象
字符串是一种基本数据类型❌
字符串是一种数据类型
字符串是一种引用数据类型【除了基本数据类型,其他都是引用数据类型】
StringBuffer 是线程安全的,它比 String 快。
字符串 “qiniu” 根据顺序不同有60
种排列组合的方式。
可以分成2个种情况。第一种,把两个i绑着一起,那么就是A(4,4)=24。
第二种,两个i不在一起,三个字符先全排列为A(3,3)再插空,C(4,2)所以为:A(3,3 )* C(4,2)=36。
空串没有分配内存空间。
在 C/C++ 中,有字符数组 a[80] 和 b[80],则正确的输出语句是:puts(a); puts(b);
sizeof
计算的是已经分配的字节空间数目(若实现规定其长度为N,则sizeof所得数值将一直是N,不会随着现有字符数发生变化),包括"\n"和空格
。【sizeof(“abc”)=4】【用运算符sizeof 可以计算出数组的容量(字节数
)】
strlen
计算的是字符长度,包括空格不包括"\n"
,以\0作为结束符,即当字符串遇到\0的时候会自动停止后续字符串的统计。双引号括起来的是字符串,后面会自动加"\n"做分割,单引号括起来的是字符。"I am \0good"
,那么使用strlen统计该字符串长度得到的结果为5
。在 C 语言中有如下声明:char color = ‘B’; 请问’B’和color分别占(1字节、1字节
)内存。【是char定义的,不是string定义的,所以后面没有\0】
以下程序段的输出结果是12
。
#include
void main() {
char s[] = "\\123456\123456\t";
printf("%d\n", strlen(s));
}
strlen() 函数用于获取字符串的长度(即字符串中字符的个数,不包括\0)。\、\123、\t是转义字符。所以长度是 12。
\ 表示单个字符
\123 表示八进制的ASCII码值为123对应的字符
\t 表示制表符
MFC 中 CString 是类型安全的类。
String s1 = “nowcoder”;字符串s1存储在内存的哪个区域:字符串常量区
。
所有的字符串常量都被放在静态内存区。因为字符串常量很少需要修改,放在静态内存区会提高效率
String s1 =new String(“nowcoder”) ;字符串s1存储在内存的哪个区域:堆。
不能将字符串 “Good!” 存放到数组 s 中的代码是char s[8];s = "Good!";
对于数组来说,数组的名字,等价于数组的首地址。s不是指针,它指的是字符串的首地址。不能做右值。
数组作为函数参数传递的是
若一个完整程序中的一部分是printf("This book is only $%2.2f for sale.",3.246e2);
,则正确的打印结果是This book is only $324.60 for sale.
。
对字符串 “mabnmnm” 的二进制进行哈夫曼编码有13
位。
字符串 "mabnmnm"每个字符的频率分别为:m 3/7,a 1/7,b 1/7,n 2/7。a:111,b:110,n:10,m:0。
字符串 “mabnmnm” 的哈夫曼编码为 0 111 110 10 0 10 0。一共13位
在存储数据时,通常不仅要存储各数据元素的值,而且还要存储数据元素之间的关系
。
串是一种特殊的线性表,其特殊性体现在元素只能是一个字符
。
打印一个字段宽度为6的左对齐字符串中的前3个字符,应使用%-6.3s
转换说明。
%s:打印字符串时使用的转换说明。
%后加(数字)修饰符:表示打印的最小字段宽度,如%6s。
%后加(.数字)修饰符:对%s转换来说,表示待打印字符的最大数量,如%.3s。
%后加(-)修饰符:表示待打印项左对齐,即从字段的左侧开始打印该项,如%-6s。
综上,使用%-6.3s转换说明,可成功打印出一个字段宽度为6的左对齐字符串中的前3个字符。
有如下语句序列:
char str[10];
cin>>str;
当从键盘输入 “I love this game” 时,str 中的字符串是"I"
。
cin遇到空格 结束输入,所以只读取I。
在 C/C++ 中,若有定义 char a [10], *p=a; ,那么下列赋值语句正确的是p="abcdefg ";
。
用二进制来编码字符串“xyzwxyxx”,需要能够根据编码解码回原来的字符串,则我们最少需要多长的二进制字符串14
。
C/C++ 语言中,在一个存放字符串的数组 char p_str[],要把字符串中第 4 个字符的值改为 ‘a’,正确的做法是?p_str[3]='a'
、*(p_str+3)='a'
。
串既可以采用顺序存储,也可以采用链式存储
strcmp为字符串比较函数
字符串是一种特殊的线性表
「空串」与「空白串」不是同一个含义。
空串是零个字符的串,它的长度为零。
空白串是指由一个或多个空格组成的串,它的长度为串中空格字符的个数。
字符常量,由单引号扩起来,以数字、字母、下划线、$. 组成,不能以数字开头 ,还有转义字符。
在 C 语言中有如下语句:char mark = ‘#’; 则’#’和”#”分别占1字节、2字节
内存。【字符串”#”使用2个字节来存储】
由 4 个 “1” 和 4 个 “0” 组成的 8 位二进制补码,能表示的最小整数是:-121
。
补码:10000111
反码:10000110
原码:11111001,即-121
==
判断的是地址是否相等
字符串"www.qq.com"所有非空子串(两个子串如果内容相同则只算一个)个数是50
。
若串 S=“software”,则其子串的数目是37
。【本题包括空串】
总结一下计算公式:
子串: n(n+1)/2 + 1
非空子串:n(n+1)/2
非空真子串:n(n+1)/2 - 1
试分析下面代码运行后,内存空间的状态有 "hello" 和 "hello world"
String s1="hello";
s1=s1+" world";
在Java中String类被设计成不可改变的类,所以String类的所有对象都是不可变的。对string的操作只是改变了指向,并没有改变内容,所以内存中会存在“hello”和“hello world”两个字符串,代码执行完之后s1指向的“hello world”。
某程序的主函数如下图所示,该程序的打印结果是:
m
om
Tom
int main(void){
char name[] = "Tom";
char *ptr;
ptr = name + strlen(name);
while(--ptr >= name)
puts(ptr);
return 0;
}
ptr=name数组的首地址+3,假设首地址为1000,ptr=1003,
第一次循环:ptr=1002,指向的是数组中第3个元素m
第二次循环,ptr=1001,指向的是o
第三次循环,ptr=1000,重新回到了首地址1000,指向t
字符串 “YONYOU”,有180
种不同的全排列方式。
一共有N、U、Y、O四种字符
N:54321/(22)= 30 // (22)是因为Y和O分别重复出现两次
U:54321/(22)= 30 // (22)是因为Y和O分别重复出现两次
Y:54321/2 = 60 // /2是因为O重复出现两次
O:54321/2 = 60 // /2是因为Y重复出现两次
所以总共30+30+60+60=180
用 0 - 9 这 10 个数字组成一个首尾相连的字符串,每个数字可以重复出现多次,并且字符串中任意 2 个数字都相邻出现过。此字符串最小长度是(50
)。
很简单,数字0必须与其他9个数字相邻,则0最少出现5次。每个数字的地位均等,根据对称性,50。
假设字符串char s[20] = “abc123”,char* p = s。那么表达式(*p+3)的结果为(d
)
假设字符串char a[] = “abc123”,char b[] = “xyz”,那么使用字符串连接函数strcat(a, b)连接字符串a和b之后得到的结果是(内存溢出
)【字符串a的空间不足导致字符串b连接到字符串a后面的时候出现内存溢出的错误】
已知一段文本有1382个字符,使用了1382个字节进行存储,这段文本全部是由a、b、c、d、e这5个字符组成,a出现了354次,b出现了483次,c出现了227次,d出现了96次,e出现了232次,对这5个字符使用哈夫曼(Huffman)算法进行编码,则以下哪些说法正确:
首次出现的位置
的操作,我们称为模式匹配
。【模式匹配是串的一种重要运算】O(m)
。01123
KMP算法的核心就是next数组,有的也称为前缀表,作用就是,当模式串与主串不匹配时,指导模式串应该回退到哪个位置重新匹配。
011234223456
。O(m + n)
。
KMP算法的本质是在分析模式串的特点,从而使得在匹配过程中,当某个字符不匹配时,主串不需要回溯,从而降低了匹配所需的时间复杂度。
主串不需要回溯,而模式串在进行匹配前,要被遍历一遍进行预处理,目的是为了生成next数组。
所以,时间复杂度为O(N+M)。
i=5,j=2
) 。长度可变
线性表分为顺序存储
和链式存储
。
线性表是具有 n 个数据元素
的有限序列(n>0)【数据元素是数据的基本单位】
广义表A=((x, (a ,b))
, (x,(a,b),y)
), 则运算Head(Head(Tail(A)))
结果为X
。
Tail
((x, (a ,b))
,(x,(a,b),y))
=((x,(a,b),y))
Head((x,(a,b),y))
=(x,(a,b),y)
Head(x,(a,b),y)
=x
可以是原子,也可以是子表
。表尾一定是子表
。
head() 返回列表的第一个元素,同时去除外层括号;
tail() 返回列表的删去第一个元素之后的剩余列表,注意:外层的括号不能去除;
考察的是广义表以及递归广义表的原理。
广义表是由n个元素组成的序列。
n是广义表的长度,广义表所包含的元素的个数,叫广义表的长度,数第一层括号内的逗号数目。
广义表的深度: 广义表中括号的最大层数
。
F=(a,F)的长度为2,由于属于递归表
,所以深度为无穷
,F相当于一个无限的表(a,(a,(a,(…))))。】
若某线性表最常用的操作是存取任一指定序号的元素
和在最后进行插入和删除运算,则利用(顺序表
)存储方式最节省时间。【涉及到存取任一指定序号的元素就选顺序表】
在等概率情况下,顺序表
的插入操作要移动一半
结点。
哪些容器可以使用数组,但不能使用链表来实现?Map或者Dict
。
线性结构的存储结点也可以有多个指针
Which statement is true for the class java.util.ArrayList?
下面那个选项有关java.util.ArrayList是正确的
A.The elements in the collection are ordered.集合中的元素是排序的
B.The collection is guaranteed to be immutable.集合不可改变❌
C.The elements in the collection are guaranteed to be unique.集合中的元素必须唯一❌
D.The elements in the collection are accessed using a unique key.集合中元素的键是唯一的❌
E.The elements in the collections are guaranteed to be synchronized.集合中的元素是线程同步的❌
在有n个结点的二叉链表中,值为非空的链域的个数为n-1
,值为空的链域个数是n+1
,共有2n
个链域。
2N
邻接表是图的一种主要存储结构,用来描述图上的每一个点。对图的每个顶点建立一个容器(n个顶点建立n个容器),第i个容器中的结点包含顶点Vi的所有邻接顶点。
对于无向图来说,使用邻接表进行存储也会出现数据冗余,表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点。
无向图的邻接表中,第i个边表的结点是表示关联于顶点i的边。同一条无向边关联于两个顶点,因此同一条边在邻接表中用了两个边表结点表示。
因此有N 条边的无向图的邻接表,其边表结点总数为2N 。
邻接矩阵
和邻接表
;需经常修改线性表L中的结点值适合采用链式结构❌【顺序结构】
线性表中,顺序表物理相邻逻辑相邻,链表逻辑相邻物理不一定相邻
在以下哪种操作中,使用顺序表比链表好?根据序号查找
【根据元素值查找❌】
在LinkedList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在ArrayList的中间插入或删除一个元素的开销是固定的❌
ArryList和LinkedList都实现了List接口,ArrayList的内存结构是数组,本质是顺序存储的线性表,插入和删除操作都会引起后续元素移动,效率低,但是随机访问效率高
LinkedList的内存结构是双向链表存储的,链式存储结构插入和删除效率高,不需要移动,但是随机访问效率低,需要从头开始向后依次访问
LinkedeList和ArrayList都实现了List接口
ArrayList是可改变大小的数组,而LinkedList是双向链接串列
LinkedList不支持高效的随机元素访问
在链表中,如果每个结点有两个指针域,则该链表一定是非线性结构❌【类似于传统的双链表,一个指向前一个结点,一个指向后一个结点,这种双链表还是一个线性结构。】
在链表中,如果有两个结点的同一个指针域的值相等,则该链表一定是非线性结构。【如果有两个结点的同一个指针域的值,那么被指向的这个点,有两个前驱,违背了唯一的特点,所以必须是非线性结构。】
在链表中,如果每个结点有两个指针域,则该链表一定是线性结构❌。【例如变种的双链表,一个指向后继结点,一个指向链表中的任意结点。】
在链表中,如果有两个结点的同一个指针域的值相等,则该链表一定是线性结构❌。【一个普通的链表中,不同的结点值可以相等,但是这种链表是线性结果。】
线性表中包含的数据元素个数不是任意的。【线性表元素个数有限】
设用链表作为栈的存储结构则退栈操作,必须判别栈是否为空
。【必须判断是否为空, 否则为空还弹栈 ,有可能返回null 】
new 创建对象时,对象的内存
和指向对象的指针
分别分配在堆区
,栈区
。【创建的对象属于自己申请的内存,应该位于堆区;指针处于栈区】
设输入序列是1,3,5…m,经过栈的作用后输出序列的第一个元素是m,则输出序列中第i个输出元素是m-2(i-1)
。
一个栈的输入序列为连续整数1,2,3…n,若输出序列的第一个元素是n,输出第 i(1<=i<=n)个元素是( n-i+1
)。
每次进栈一个元素,在下一个元素进栈之前都把它弹出来,这种情况出栈顺序与入栈顺序是一样的。
在栈中,栈底
保持不变。栈底固定,而栈顶浮动。
栈应用的典型实例是用“算符优先法进行表达式求值”
。‘
栈的典型应用:表达式求值,括号匹配,递归函数调用,数制转换等,将数学表达式转换为后辍形式,高级编程语言的过程调用
队列的典型应用:打印队列,事件排队,操作系统分配资源(如CPU)【资源分配有多种分配策略,简单的可以是任务先到先执行,可以使用队列来完成】
调用函数时,入参及返回地址使用了栈
。
想让谁入队,就得先让他出栈,他要想出栈,一定要在栈的两端。
在向1988个有序顺序表中插入一个新元素,并保持原来的顺序不变,平均要移动的元素次数是994
。
不需要判断栈满但需要判断栈空。
一个空栈,如果有顺序输入序列:a1,a2,a3…an(个数大于3),而且输出第一个元素为 a(n-1)
, 那么所有元素都出栈后,a(n-2) 一定比 a(n-3) 先出
。
已知操作符包括‘ +’、‘ -’、‘ ’、‘ /’、‘ (’和‘ )’。将中缀表达式 a+b-a((c+d)/e-f)+g 转换为后缀表达式 ab+acd+e/f-*-g+时,用栈来存放暂时还不能确定运算次序的操作符。若栈初始时为空,则转换过程中同时保存在栈中的操作符的最大个数是5
。
假设栈初始为空,将中缀表达式 转换为等价后缀表达式的过程中,当扫描到f时,栈中的元素依次是 (+(-*
)。
某表达式的前缀形式为"±*^ABCD/E/F+GH",它的中缀形式为(A^B*C-D+E/(F/(G+H))
)。
递归先序遍历一个节点为n,深度为d的二叉树,需要栈空间的大小为O(d)
。
设栈的输入序列为 123……n ,输出序列为 a1,a2,a3,……,an ,若存在 1<=k<=n ,使得 ak=n ,则当 k<=i<=n 时, ai 为不确定
。
元素出栈顺序未知。除非 a1=n,则说明元素全部入栈之后再弹出,此时出栈顺序则为a(n),a(n-1)…a(1)。
堆栈可用数组来实现。
可以访问栈顶元素,不可访问栈底元素。
和顺序栈相比,链栈有一个比较明显的优势是通常不会出现栈满的情况
。【链栈与顺序栈相比,其特点之一是通常不会出现栈满的情况。】
在栈中,栈顶指针的动态变化决定栈中元素的个数。
若采用带头、尾指针的单向链表表示一个栈,那么该堆栈的栈顶指针top应该如何设置将表头项设置为top
。
若一个栈以向量 V… 存储,初始栈顶指针 top 为 n+1,则下面x入栈的正确操作是:top=top-1; V[top]=x
以下哪些对象是分配在栈上
的:函数内局部变量、函数内局部指针变量、函数内指向动态申请的对象的局部指针变量。
设指针变量top指向当前链式栈的栈顶,则删除栈顶元素的操作序列为(top=top->next;
)。【top=top-1;❌】
单链表实现的栈,栈顶指针为Top(仅仅是一个指针),入栈一个P节点时,其操作步骤为:p->next=Top->next;Top->next=p;
。
向一个栈顶指针为 hs (hs为栈的第一个元素地址)的链栈中插入一个 s 结点时,应执行s->next=hs; hs=s;
。
在栈空的情况下,不能做退栈运算,否则产生下溢。
两个栈共享一片连续内存空间时,为提高内存利用率,减少溢出机会,应把两个栈的栈底分别设在这片内存空间的两端。
4个圆盘的Hanoi塔,总的移动次数为15
。
汉诺塔、梵塔
规则是:一次移动一个盘;无论何时,小盘在上,大盘在下。
在游戏中,总共有n个金属盘片的塔叫做n阶汉诺塔,若要完成n阶汉诺塔,则最少要移动2^n -1
次
设栈的顺序存储空间为 S(0:49) ,栈底指针 bottom=49 ,栈顶指针 top=30 (指向栈顶元素)。则栈中的元素个数为(20
)。
元素依次存储在单元 30 : 49 中,个数为 20 。
一个栈的入栈序列为1,2,3,…,n ,其出栈序列是 p 1 ,p 2 ,p 3 ,…p n 。若p 2 = 3,则 p 3 可能取值的个数是(n-1
)
如果第一个出栈的是2则p3可能的取值为1、4、5、…、n,如果第一个出栈的是4则p3可能的取值为2、5、…、n,综上p3可能的取值为1、2、4、5、…、n,即p3可能的取值有
n-1
个
用俩个栈模拟实现一个队列,如果栈的容量分别是O和P(O>P),那么模拟实现的队列最大容量是2P+1
。
两个关键点:一个是大栈不能一次进太多,否则影响小栈的出栈顺序;二是可以把p+2到2p+1入小栈,大栈栈底的p+1可以直接出大栈去排队
若数组S[1…n]作为两个栈S1和S2的存储空间,对任何一个栈,只有当[1…n]全满时才不能进行进栈操作。为这两个栈分配空间的最佳方案是(S1的栈底位置为1,S2的栈底位置为n
)
【先进后出,栈底的位置为该存储空间满的位置。】
s->link=top; top=s;
)操作。【s的link指向原top,新的top指向s。】循环队列的相关条件和公式:
队尾指针是rear,队头是front,其中QueueSize为循环队列的最大长度
。
在循环队列中,当队列为空时,有front=rear,而当所有队列空间全占满时,也有front=rear。为了区别这两种情况,规定循环队列最多只能有MaxSize-1个队列元素。当循环队列中只剩下一个空存储单元时,队列就已经满了。
队空条件:rear==front
队满条件:(rear+1) %QueueSIze==front
计算队列长度:(rear-front+QueueSize)%QueueSize
入队:(rear+1)%QueueSize
出队:(front+1)%QueueSize
循环队列中元素个数计算方法是固定的,即(尾-头)%长度,但是由于是循环队列,所以尾可能会小于头,所以要加上长度,使尾-头保持是正整数,然后再对长度求余,即元素个数。
循环队列存储在数据A[0…m]中,则入队时的操作为rear=(rear+1)%(m+1)
。【注意:数组长度为m+1
】
已知循环队列存储在一维数组A[0…n-1]中,且队列非空时 front 和 rear 分别指向队头和队尾元素。若初始时队列为空,且要求第 1 个进入队列的元素存储在 A[0]处,则初始时 front和 rear 的值分别是0, n-1
。
插入时,队头指针不变,队尾指针后移一位。该题约定队列非空时 front 和 rear 分别指向队头和队尾元素,即插入第一个元素在下标为0的位置后,队头队尾指针皆指向A[0],此时可以反推出插入前,队头指针仍旧指向下标0,而队尾指针应指向前一位,也就是下标n-1的位置。注意,这道题的约定与大多数题约定队列为空时front=rear=0是不一样的约定,都是根据题意解题。
若用一个大小为 6 的数组来实现循环队列,且当 rear 和 front 的值分别为 0和3
。当从队列中删除一个元素,再加入两个元素后,rear 和 front 的值分别为2和4
。
1、队列添加元素是在对尾,删除元素是在对头;
2、添加元素,尾指针rear+1;删除元素,头指针front+1;
3、本题中,删除一个元素,front+1,也就是3+1=4;添加2个元素,rear+2,也就是0+2=2;
最适合创建一个优先级队列
的数据结构:堆
。
循环队列的存储空间为 Q(1:40) ,初始状态为 front=rear=40 。经过一系列正常的入队与退队操作后, front=rear=15 ,此后又退出一个元素,则循环队列中的元素个数为39,或0且产生下溢错误
。
front==rear代表着队列满,或者空,或只有一个元素。然后又退出去了一个数据,所以当为空的时候,就会产生下溢。为满的时候,就是总个数-1也就是39个
栈
数据结构实现比较好。4231
。
可以得到的是:1234、4132、4213。
输入受限 的 双端队列 是指元素只能从 队列 的一 端输入 ,但可以从 队列 的两端 输出;
1,2,3,4为输入受限的输出队列。
4,1,2,3为输入受限的输出队列
输出受限 的 双端队列 是指只有一端可以进行出队操作而从两端都可以进行入队操作的 队列。
4,2,1,3为输出受限的输出队列
注意是出队受限的队列(单方向出队),入队可以两端入队,并且只限制进队的顺序,不限制是否全部进入才出队,
cadb
过程: a进队列——b从入队口进队——c从出队口出队——a出队——d从出队口入队——d出队——b出队;
bdac
过程: a进队列——b从出队口进队——b出队——c从入队口入队——d从出队口入队——d出队——a出队——c出队;
60
的数据开始建初始堆。
建初始堆意思是从这里开始调整
1
。
带链队列为空时,
dufront = rear= NULL
,
插入第1个元zhi素时,rear+1 =1,front+1 = 1
,
插入第2个元素时,rear+1 =2,front不变
,
删除dao第2个元素时,front+1 = 2,rear=2
,即 front = rear= 2,
而带链队列中还剩有1个元素 。
数组
实现,也可以用链表
实现。Redis、kafka
不包括:
MongoDB 是非关系型数据库
Memcached 是分布式缓存系统
list、map、set
。
1:vector 底层数据结构为数组,支持快速随机访问
2:list 底层数据结构为双向链表,支持快速增删
3:map、set都是STL关联容器,支持快速增删
头、尾指针可能都要修改
。XABCD-*E/+=
(r-f+MAX+1)%MAX
。10
。
FIFO,发生缺页时的调入顺序即为淘汰顺序
1、访问1,缺页
,调入1,内存中为 1, ,;
2、访问2,缺页
,调入2,内存中为 1,2,;
3、 访问3,缺页
,调入3,内存中为 1,2,3;
4、 访问4,缺页
,调入4,淘汰1,内存中为 4,2,3;
5、 访问5,缺页
,调入5,淘汰2,内存中为 4,5,3;
6、 访问1,缺页
,调入1,淘汰3,内存中为 4,5,1;
7、 访问2,缺页
,调入2,淘汰4,内存中为 2,5,1;
8、 访问5,不缺页
,内存中为 2,5,1;
9、 访问1,不缺页
,内存中为 2,5,1;
10、 访问2,不缺页
,内存中为 2,5,1;
11、访问3,缺页
,调入3,淘汰5,内存中为 2,3,1;
12、访问4,缺页
,调入4,淘汰1,内存中为 2,3,4;
13、访问5,缺页
,调入5,淘汰2,内存中为 5,3,4;
n-1
)条边。2^k-1
)个节点。2i+1
)降序序列
。下列关于该平衡二叉树的叙述中,正确的是:
树中最大元素一定是无左子树, (可能有右子树)
树中最小元素不一定是叶结点
不一定是叶结点
)排序树有两种排序方法:
1.左孩子 < 根节点 < 右孩子(常见)
这种情况中序遍历得到的是一个升序序列
2.左孩子 > 根节点 > 右孩子
这种情况中序遍历得到的是一个降序序列
2,8,6,3,7,4,5
不可能是键的检查序列。
可能是的有:10,9,8,7,6,5、
1,2,9,3,8,7,4,6,5
2,3,10,4,8,5
4,9,8,7,5
rchild
)域存储后继结点的地址。【lchild用于存储前驱,rchild用于存储后继】n+1
)【】在二叉排序树中,最小元素所在结点一定是中序遍历序列中第一个被访问的结点,即是二叉树的最左下结点,但可能有右子树。最大元素所在结点一定是中序遍历序列中最后一个被访问的结点,即是二叉树的最右下结点,但可能有左子树。5是最小元素,25是最大元素,但5和25都不是叶子结点。
左子树
的值、小于其右子树
的值。100, 60, 80, 90, 120, 110, 130
。
相同的是:100, 80, 90, 60, 120, 110, 130
100, 120, 110, 130, 80, 60, 90
100, 80, 60, 90, 120, 130, 110
46、36、18、28、35
。384
)一棵树含有n个结点,则最后一个结点的编号必为n,它的父结点则为n/2,且为上一层最右边的一个结点。所以根结点的个数就为:n-n/2。
此题中,n = 767,n-n/2 = 767 - 767/2 = 384。
二叉搜索树中序遍历的结果一定是从小到大的有序序列
设某棵二叉树中只有度数为0和度数为2的结点且度数为0的结点数为n,则这棵二叉中共有(2n-1
)个结点。
二叉树中一个性质:度为0的结点(叶子节点)个数n0等于度为2的结点个数n2加1,即N2=N0-1。总结点数N=N0+N1+N2=N0+0+(No-1)=2N0-1。N1表示树中度为1的结点。
二叉树第K层上至多有(2^(K-1)
)个节点。(根从第1层开始)
若X是后序线索二叉树中的叶结点,且X存在左兄弟结点Y,则X的右线索指向的是(X的父结点
)。【后序遍历,左右根,X左线索指前驱,右线索指后继,X有左兄弟,那么X的后继就是父结点】
含有n个叶子结点的最优二叉树中共有(n-1
)个分支结点。【最优二叉树就是从已给出的目标带权结点(单独的结点) 经过一种方式的组合形成一棵树,使树的权值最小,最优二叉树是带权路径长度最短的二叉树,或哈夫曼树。如下图所示:a、b、c、d四个叶子节点构成的最优二叉树,除了叶子节点,其他3个均为分支节点。根据题目已知n=4代入】
对任意一棵二叉树T,H(T)表示树的高度。若树T含有n个结点,那么(H(T)≥O(logn)
)
若平衡二叉树的高度为6,且所有非叶结点的平衡因子均为 1,则该平衡二叉树的结点总数为(20 )。除叶节点外平衡因子都为1,说明左子树都比右子树高度多1,也就是说,是左子树撑起了该局部根节点的高度,所以可以按照这样的思路,自顶而下画这棵树:(节点标号是为了区分各个节点)
某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为(FEDCBA
)
设二叉树的先序遍历序列和后序遍历序列正好相反,则该二叉树满足的条件是(高度等于其结点数)。先序后序刚好相反,说明一个结点只有一个孩子,也就是结点数就是高度了
对于一棵排序二叉树:(中序)遍历可以得到有序序列。
24,10,5 和 24,14,6
) 。1 )都是 24 ,说明 24 是根
2 ) 24 是由子节点之和组成,排除 AB
3 )子节点不能比父节点大或者相等(子节点之和为父节点值)
Huffman 树的带权路径长度WPL等于(各叶子结点的带权路径长度之和
)
设某哈夫曼树中有199个结点,则该哈夫曼树中有(100
)个叶子结点。
记住两点:哈夫曼树中没有度为1的结点,n0=n2+1
节点数=分叉数+1;
设度为2的节点有x,度为0的节点有y(叶子节点);
则:分叉数:2x+0y=2x;
节点数:199;
得:2x+1=199;x=99;y=100;
赫夫曼树不一定是一棵完全二叉树,完全二叉树只允许最后一层右边有空缺,赫夫曼树可以左边空缺。
选项B:赫夫曼树与二叉排序树不是同义词,赫夫曼树的构建不需要规定左边小右边大。
选项C:正确
选项D:在赫夫曼树中,结点的度数不可能为1。
一般在哈夫曼树中,权值越大的叶子离根结点越近
哈夫曼树中没有度数为1的分支结点
若初始森林中共有n棵二叉树,最终求得的哈夫曼树共有2n-1个结点
查找效率最高的二叉排序树是平衡二叉树
。
查找效率最低的二叉排序树是单枝树
。
当前节点有右子节点时必须有左节点
已知一棵完全二叉树中共有626个结点,叶结点的个数应为(313
)
完全二叉树最后一个结点的编号为n,则它的父结点编号为[n/2],则叶结点个数为n-[n/2]。
626-[626/2]=313完全二叉树共有700结点,该二叉树有350个叶子结点?因为12/2等于6,等于父节点值,所以是最后一个带子节点的,拿总数减去6,即为叶子节点数,同理,所以700作为最后一个节点,他的父节点是350,所以序号350是最后一个非叶子节点,以下的都没有子节点,700-350 = 350
一棵深度为5的完全二叉树最少有(16)个节点(第一层深度视为1)
前4层为满二叉树结构,最后一层最左边的根节点只有一个左子树。所以为:2^4-1+1=16
满二叉树,所有的分支结点都存在左子树和右子树,并且所有叶子都在同一层上。深度为n的满二叉树的节点数为2^n - 1 ; 完全二叉树是除了叶子层,其他层都符合满二叉树定义的二叉树,所以完全二叉树最少的结点为2^(n-1) -1 +1 ;在赫夫曼树中,结点的度数只可能为0、2一颗完全二叉树第六层有8个叶结点(根为第一层),则结点个数最多有(111)个。
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
3
)所以需要比较的数是:54,73,62,共需要3次。
25,10,15,20
25,10,15,18,20
10,30,20
10,30,25,20
A:首先根节点为25,20比25小搜索其左子树,找到10比25小不矛盾,20比10大搜索其右子树,找到15比10大不矛盾,20比15大搜索其右子树找到20,正确 B:首先根节点25,20比25小搜索其左子树,找到10比25小不矛盾,20比10大搜索其右子树,找到15比10大不矛盾, 20比15大搜索其右子树,找到18比15大不矛盾, 20比***搜索其右子树,找到20,正确 C:首先根节点为10,20比10大搜索其右子树,找到30比10大不矛盾,20比30小搜索其左子树,找到20,正确 D:首先根节点为10, 20比10大搜索其右子树,找到30比10大不矛盾,20比30小搜索其左子树,找到25比30小不矛盾,20比25小搜索其左子树找到20,正确。二叉排序树不一定是平衡二叉树
4
。在二叉排序树(二叉搜索树)中,最小值结点的左孩子一定为空指针。
一个无序的元素序列可以通过构造一棵二叉排序树而变成一个有序的元素序列
已知数据元素为(34,76,45,18,26,54,92,65),按照依次插入节点的方法生成一棵二叉排序树,则该树的深度(根深度为1)为5
。
设森林F中有三棵树,第一,第二,第三棵树的结点个数分别为M1,M2和M3。与森林F对应的二叉树根结点的右子树上的结点个数是(M2+M3 )。森林转换为二叉树: 1.将森林中每棵树转换为相应的二叉树 2.第一颗二叉树不动,依次吧后一棵二叉树的跟节点作为前一颗二叉树根节点的右孩子,当所有二叉树链在一起后,所得的二叉树就是森林对应的二叉树
二叉搜索树,用于搜索,因此 内部节点没有重复的元素 。
在一般包含n个节点的二叉搜索树中查找的最差时间复杂度是O(n)
有向图和无向图都可以进行遍历操作
基本遍历算法两种:深度遍历
和广度遍历
图的遍历必须用递归实现❌【深度、广度都有递归
和非递归
的实现方法。】
图的遍历算法可以执行在有回路的图中
用邻接表表示图进行深度优先遍历时,通常是采用(栈
)来实现算法的。
深度用栈,广度遍历用队列
深度DFS:需要递归,使用顺序栈;
广度BFS:类似层次遍历;需要循环队列
拓扑排序算法流程如下:
(1)在有向图中选一个没有前驱的顶点且输出之。
(2)从图中删除该顶点和所有以它为尾的弧。
重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况则说明有向图中存在环。
应用如上算法:
输出1号顶点并删除1号顶点和所有以它为尾的弧;
输出2号顶点并删除2号顶点和所有以它为尾的弧;
输出3号顶点并删除3号顶点和所有以它为尾的弧;
输出4号顶点并删除4号顶点和所有以它为尾的弧;
输出5号顶点并删除5号顶点和所有以它为尾的弧;
此时,全部顶点均已输出,算法结束,输出序列1,2,3,4,5为该图拓扑排序序列。
有向无环图
。