本篇为笔者学习数据结构时,在牛客网站的刷题笔记。
数据结构分为:
- 逻辑结构【
面向问题的
】- 集合结构
- 线性结构(一对一)
- 树形结构(一对多的层次关系)
- 图形结构(多对多)
- 物理结构(存储结构)【
面向计算机的
】- 顺序结构
链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。
- 链式结构
链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。
循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数。
数组——长度固定
-
- 数组是一种
对象
,不属于原生类
,数组的大小确定之后不可改变。【原生类指未被实例化的类,数组一般指实例化,被分配空间的类】 - 数组常用的两种基本操作是
查找与修改
不适合插入、删除
- 数组元素在栈区,链表元素在堆区
- 数组一旦建立,结构的元素个数和元素间的物理存储关系就不再变化
- 二维以上的数组其实是一种特殊的广义表,均
不是
线性结构。【线性结构(串、链式存储栈、顺序存储栈)指逻辑上是线性的,而不是存储上的线性。线性是线性,顺序是顺序,线性是逻辑结构,顺序是存储结构,两者不是一个概念,线性是指一个元素后继只有唯一的一个元素或节点,非线性是一个元素后面可以有多个后继或前继节点,顺序是指存储结构连续,例如数组是顺序的,链表不是顺序的,但他们都是线性的。当然顺序也可以是非线性的,例如顺序结构存储非线性结构的二叉树!!!】 - 线性表是
一个有限序列,可以为空
。 - 数组可看作基本线性表的一种推广,因此与线性表一样,可以对它进行插入、删除等操作❌【
数组越界不能插入
】 - 线性表L=(a 1 ,a 2 , … ,a n ),除第一个和最后一个元素外,其余每个元素都有且仅有一个直接前驱和直接后继。
- 数组是一种
-
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列。
-
- 设一维数组中有n个数组元素,则读取第i个数组元素的平均时间复杂度为(
O(1)
)。【数组具有随机存取的特性,按下标访问元素,所以时间复杂度为O(1)
】【线性表的顺序存储结构
是一种随机存取
的存储结构。】 - 链表的访问时间复杂度是
O(n)
- 数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)❌【链表有单向O(n)和双向链表O(1)】
- 设一维数组中有n个数组元素,则读取第i个数组元素的平均时间复杂度为(
-
设有数组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]
。 -
-
数组元素的地址计算与数组的存储方式有关。
-
二维数组有按列存储和按行存储的差别。
-
若二维数组 a 有 m 列,则在数组元素
a[i][j]
前的元素个数为i * m + j
。 -
若数组
A[0…m-1][0…n-1]
按列优先顺序存储,则a[i][j]
地址为LOC(a00)+[j*m+i]
。 -
数组A=array[1…100,1…100]以行序为主序存储,设每个数据元素占2个存储单元,基地址为10,则LOC[5,5]应为
818
。
-
-
如果矩阵中大多数元素均为零元素,则矩阵为稀疏矩阵。稀疏矩阵有两种压缩存储方式,一个是
三元组
(行, 列, 非零元素值),一个是十字链表
,两者各有适用环境。-
采用压缩存储之后,下三角矩阵的存储空间可以节约一半。❌【假设是2*2的矩阵,下三角矩阵有3个元素,只是少了一个元素,没有达到减少一半的空间。】
-
若稀疏矩阵采用
三元组
表形式进行压缩存储,若要完成对三元组表进行转置,只要将行和列对换❌。【①三元组转置(行列互换),②然后再按行排序。】- 已知一稀疏矩阵的三组元表为:(1,2,3),(1,6,1),(3,1,5)(3,2,-1),(5,4,5)(5,1,-3),则其转置矩阵的三元组表中第3个三元组为
(2, 1, 3)
。
- 有一个
100*90
的稀疏矩阵,非0元素有10个,设每个整型数占2字节,则用三元组表示该矩阵时,所需的字节数是66
。【(1032)=60 还得拿出3个来存储行,列,非零元素,3*2=6; 所以:60+6=66】
- 已知一稀疏矩阵的三组元表为:(1,2,3),(1,6,1),(3,1,5)(3,2,-1),(5,4,5)(5,1,-3),则其转置矩阵的三元组表中第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≤n+1)之前插入一个元素时,需
向后移动
(n-i+1
) 个元素。【前面i-1个元素不动,那么就要移动n-(i-1)个元素了】 - 在一个长度为n的顺序表中删除第i个元素(1<=i<=n)时,需
向前移动
(n-i
) 个元素。
- 向一个长度为n的向量的第i个元素(1≤i≤n+1)之前插入一个元素时,需
-
若定义: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遍历列
-
-
- 已知 10*12 的二维数组 A ,以行序为主序进行存储,每个元素占 1 个存储单元,已知
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
】 - 设有一个 10 阶的下三角矩阵 A (包括对角线),按照行优先的顺序存储到连续的 55 个存储单元中,每个数组元素占 1 个字节的存储空间,则 A[5][4] 地址与 A[0][0] 的地址之差为(
19
)。 - 假设以行优先顺序存储三维数组
A[5][6][7]
,其中元素A[0][0][0]
的地址为1100,且每个元素占2个存储单元,则A[4][3][2]
的地址是1482
- 设有数组A[i,j],数组的每个元素长度为3字节,i的值为1到8,j的值为1到10,数组从内存首地址BA开始顺序存放,当用以列为主存放时,元素A[5,8]的存储首地址为(
BA+180
)。【(7*8+5-1)*3 +BA
】 - 若二维数组 a 有 m 列,则计算任一元素 a[i][j] 在数组中的
位置
公式为(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开始
】 - 假设有60行70列的二维数组 a[1 …60, 1…70 ] 以列序为主序顺序存储,其基地址为10000,每个元素占2个存储单元,那么第32行第58列的元素 a[ 32,58 ] 的存储地址为
16902
- 一个顺序表第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是(
108
)。【起始是100之后还有四个】 - 数组A的每个元素需要4个字节存放,数组有8行 10列,若数组以行为主顺序存放在内存SA开始的存储区中,则A中8行5列的元素的位置是(
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中,每个元素的长度为3个字节,行下标i从1到8,列下标j从1到10,从首地址SA开始连续存放在存储器内,该数组按行存放时,元素
A[8][5]
的起始地址为(SA+222
)。【陷阱:起始地址为上一地址的结束。小心点】【首先,本题的数组是从A[1][1]开始的,也就是说和所谓的数组从零开始没什么关系。
A[1][1]的起始地址为SA,那么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
) - 二维数组k[1…7,1…9],每元素大小占2个字节,而且使用列存储,a[5,4]的偏移量为(
50
)个字节。
- 已知 10*12 的二维数组 A ,以行序为主序进行存储,每个元素占 1 个存储单元,已知
-
对于一维整形数组 a ,以下描述正确的是:
#define SIZE 10 int a[SIZE]
define是设置常量,之后不能更改。
【宏定义后面不需要
分号】
错误的是:int n=10,a[n]
【注意不管啥语言,数组定义中等号左边括号内如果有则必须为常数(你想想要是这句代码后面n变了咋办,数组长度不能改) 】- 在 C 语言中,C99除外,一维数组的定义方式为:
元素类型 数组名[E];
,E 为(整型常量表达式
)。【常量表达式❌,小数也可以是常量】
- 在 C 语言中,C99除外,一维数组的定义方式为:
-
定义语句"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
都是取地址】- 数组定义为”int a[4][5];”, 引用”*(a+1)+2″表示(
a数组第1行第2列元素的地址
)(从第0行开始)
-
gets(字符数组名或指针)
gets能够接受空格、制表符Tab和回车等。
gets和sacnf函数,在字符串接受结束后自动加’\0’ -
数组指针只是一个指针变量,它占有内存中一个指针的存储空间
指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间 -
-
unshift()
向数组的开头添加一个或多个元素,并返回新的长度 -
strcpy(s1, s2);
,strcpy函数执行copy作用,将s2
指针所指数组负值给s1
- strcpy(s1,s2)这个函数是把s2字符串拷贝到s1这个字符串,同时也把s2的 ‘\0’ 拷过去,所以覆盖了s1的所有字符(在空间足够的情况下,当然遇到s1的空间不足以存放s2,另考虑),所以strcpy执行完后是“1234” strlen(“1234”) 就是4了。
-
push()向数组的末尾添加一个或更多元素,并返回新的长度
-
join()把数组的所有元素放入一个字符串
-
已知定义数组char a[6],若要用来接收输入5个字符:
gets(&a[0]);
、gets(a);
、scanf("%s", a + 1);
- gets和scanf输入都是需要取地址,数组名a等价于首元素地址&a[0],a+1相当于&a[1]。
- 错误的是:
scanf("%s", a[0]);
【a[0]不是地址,而是数组元素】
-
-
在表长为n的顺序表上做插入运算,平均要移动的结点数为
n/2
-
当申明类型为char而传入一个数值时,会默认那个数值为某char类型的ASCII编码。例如char a=48;打印出来a=‘0’.
-
-
char a[] = {0, 1, 2, 3, 4, 5};
-
char a[] = {‘0’, ‘1’, ‘2’, ‘3’, ‘\0’};
-
char a = {‘A’, ‘B’, ‘C’};❌【a并没有定义成数组,而是字符】
-
int a[] = “0123”;❌【整型数组不能被赋值字符串;】
- 编译出错。除非是在声明时 可以 char s[5]=“good”;这样赋值。此外, 以后赋值 要么strcpy 要么memcpy。
-
int a[5] = {0.1, 2, 3, 4};❌【 int 为整型,元素0.1为浮点型】
-
int a[10] = {10a};❌【a 是整型数组,10a 操作非法】
-
-
设二维数组
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是地址,地址定义后不能赋值
-
a[0]代表了以a[0][0]为首的一维数组
,也就是将这个char a[2][3]摊成一维数组了,而printf(“%s”,a[0])就是打印这个字符串数组,打印遇’\0’结束。 -
a数组里面的内容为ab\0cd\0,printf输出\0前面的内容,所以结果是
ab
。【遇到\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];如果进行了初始化,无须指定大小】 -
false false
- 采用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);
- printf(“格式控制字符串”, 输出列表); 如:printf(“%s”, string);
- putchar(字符数据); 如:char a_c=‘h’;putchar(a_c);
- puts(字符串); 如:puts(“hello girl”);
-
- StringBuffer是一个线程安全的可修改的字符序列。
- StringBuilder是线程不安全的可修改的字符序列。
- String的值是不可变的,所以是线程安全的哦。
- StringBuilder相比StringBuffer效率更高,但多线程不安全;
- 在单线程中字符串的频繁拼接使用StringBuilder效率更高,对于多线程使用StringBuffer则更安全;
字符串简单操作时没必要使用上述两者,还是用String类型提高速度;
-
sizeof
计算的是已经分配的字节空间数目(若实现规定其长度为N,则sizeof所得数值将一直是N,不会随着现有字符数发生变化),包括"\n"和空格
。【sizeof(“abc”)=4】【用运算符sizeof 可以计算出数组的容量(字节数
)】- 64位机器下地址占8个字节
- 32位机器下地址占4个字节
- 线性表的长度是线性表所占用的存储空间的大小。 ❌
strlen统计字符串长度,遇到\0结束,且不统计\0。sizeof计算开辟的空间大小,a数组开辟的空间为6。所以本题sizeof(a)=6,而strlen(a)=4
strlen
计算的是字符长度,包括空格不包括"\n"
,以\0作为结束符,即当字符串遇到\0的时候会自动停止后续字符串的统计。双引号括起来的是字符串,后面会自动加"\n"做分割,单引号括起来的是字符。- 假设现在有一个字符串,其内容为
"I am \0good"
,那么使用strlen统计该字符串长度得到的结果为5
。
-
在 C 语言中有如下声明:char color = ‘B’; 请问’B’和color分别占(
1字节、1字节
)内存。【是char定义的,不是string定义的,所以后面没有\0】 -
以下程序段的输出结果是
12
。#include<stdio.h> 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
。xyzwxyxx:x:4位、y:2位、z:1位、w:1位。用4、2、1、1构造哈夫曼树。
-
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)算法进行编码,则以下哪些说法正确:
- 使用哈夫曼算法编码后,用编码值来存储这段文本将花费最少的存储空间
- 使用哈夫曼算法进行编码,a、b、c、d、e这5个字符对应的编码值是唯一确定的❌
- 使用哈夫曼算法进行编码,a、b、c、d、e这5个字符对应的编码值可以有多套,但每个字符编码的位(bit)数是确定的
- b这个字符的哈夫曼编码值位数应该最短,d这个字符的哈夫曼编码值位数应该最长【1382个字符占用1382个字节,每个字符占一个字节存储,出现次数最多的,先用内存字节数也最多,因此,根据哈夫曼的数学原理,即树的带权路径和最小,编码最优。因此离树根最近的,必然应该编码它占用位数最少。】
模式匹配
- 假设有两个串 A 和 B ,求 B 在 A 中
首次出现的位置
的操作,我们称为模式匹配
。【模式匹配是串的一种重要运算】 - 设串长为 n,模式串长为 m,则 KMP 算法所需的附加空间
O(m)
。 - 已知串 S= "babab ", 其 Next 数值序列为
01123
KMP算法的核心就是next数组,有的也称为前缀表,作用就是,当模式串与主串不匹配时,指导模式串应该回退到哪个位置重新匹配。
- 字符串 “ababaaababaa” 的 next 数组为
011234223456
。
- KMP算法下,长为n的字符串中匹配长度为m的子串的复杂度为
O(m + n)
。KMP算法的本质是在分析模式串的特点,从而使得在匹配过程中,当某个字符不匹配时,主串不需要回溯,从而降低了匹配所需的时间复杂度。
主串不需要回溯,而模式串在进行匹配前,要被遍历一遍进行预处理,目的是为了生成next数组。
所以,时间复杂度为O(N+M)。 - 已知字符串S 为“abaabaabacacaabaabcc”,模式串 t 为“abaabc”。采用 KMP 算法进行匹配,第一 次出现“失配”(s[i]≠t[j]) 时,i=j=5,则下次开始匹配时,i 和 j 的值分别是 (
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() 返回列表的删去第一个元素之后的剩余列表,注意:外层的括号不能去除;
- F=(a,F)是一个递归的广义表,它的深度是1,长度是2。❌
考察的是广义表以及递归广义表的原理。
广义表是由n个元素组成的序列。
n是广义表的长度,广义表所包含的元素的个数,叫广义表的长度,数第一层括号内的逗号数目。
广义表的深度: 广义表中括号的最大层数
。
F=(a,F)的长度为2,由于属于递归表
,所以深度为无穷
,F相当于一个无限的表(a,(a,(a,(…))))。】- 广义表即我们通常所说的列表(lists)。它放松了对表元素的原子性限制,允许他们有自身结构。那么广义表E((a,(a,b),((a,b),c)))的长度和深度分别为:
1和4
- 若广义表A满足Head(A) = Tail (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
个链域。 -
- 邻接表是图的一种顺序存储结构❌【是链式存储结构】【邻接表指的是:为图的每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点Vi的边(对于有向图是以Vi为尾的弧)。】
- 在具有 N 个顶点和 N 条边的无向图的邻接表存储中,邻接表中结点的总数为
2N
邻接表是图的一种主要存储结构,用来描述图上的每一个点。对图的每个顶点建立一个容器(n个顶点建立n个容器),第i个容器中的结点包含顶点Vi的所有邻接顶点。
对于无向图来说,使用邻接表进行存储也会出现数据冗余,表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点。
无向图的邻接表中,第i个边表的结点是表示关联于顶点i的边。同一条无向边关联于两个顶点,因此同一条边在邻接表中用了两个边表结点表示。
因此有N 条边的无向图的邻接表,其边表结点总数为2N 。 - 如果某个有向图的邻接表中第i条单链表为空,则第i个顶点的出度为零。
- 图的表示方法有两种,分别是
邻接矩阵
和邻接表
;
-
需经常修改线性表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)。
-
- 设输入序列为1,2,3,则经过栈的作用后可以得到
5
种不同的输出序列。 - 若一序列进栈顺序为e1,e2,e3,e4,e5,问存在多少种可能的出栈序列(
42
)。
- 设输入序列为1,2,3,则经过栈的作用后可以得到
-
堆栈可用数组来实现。
-
可以访问栈顶元素,不可访问栈底元素。
-
和顺序栈相比,链栈有一个比较明显的优势是
通常不会出现栈满的情况
。【链栈与顺序栈相比,其特点之一是通常不会出现栈满的情况。】 -
在栈中,栈顶指针的动态变化决定栈中元素的个数。
-
- 在循环链表中,头指针和链尾指针的动态变化决定链表的长度❌【链表没有限定插入删除,中间任何地方都可以,单单头尾无法决定】。
- 在循环队列中,头指针和链尾指针的动态变化决定链表的长度
-
若采用带头、尾指针的单向链表表示一个栈,那么该堆栈的栈顶指针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
)
【先进后出,栈底的位置为该存储空间满的位置。】
- 设链式栈中结点的结构为(data ,link),且top是指向栈顶的指针,若想在链式栈的栈顶插入一个由指针s所指的结点,则应执行(
s->link=top; top=s;
)操作。【s的link指向原top,新的top指向s。】
栈和堆
- 堆是自己申请的,栈是系统自动分配
- 堆:自己做菜自己吃,什么时候收盘子自己知道,但是可能会浪费(产生碎片),因为可能自己一个人吃不完。
- 堆的使用容易产生碎片,但是用起来最方便
- 堆需要自己决定何时去释放
- 堆的大小受限于系统中有效的虚拟内存
- 堆的生长方向是向下的,即向着内存地址减小的方向增长
- 堆都是动态分配的,没有静态分配的堆;
- 对堆的频繁new/delete会造成内存空间的不连续,从而造成大量的碎片;栈则不会存在这个问题
- 堆的释放工作由程序员控制,轻易产生内存泄露。
- 栈:公司食堂,你吃饭由食堂工作人员帮你打饭和分配位置,吃完了工作人员帮你收盘子。你浪费粮食(碎片)那是不可能的,因为食堂会把碎片拿去喂猪。
- 栈的空间由系统决定何时释放
- 栈的大小是固定的
- 栈有静态分配和动态分配2种分配方式。
- 栈是由编译器自动治理
- 堆:自己做菜自己吃,什么时候收盘子自己知道,但是可能会浪费(产生碎片),因为可能自己一个人吃不完。
队列
循环队列的相关条件和公式:
队尾指针是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个
- 解析XML时,需要校验节点是否闭合,如必须有与之对应,用
栈
数据结构实现比较好。 - 若以1234作为双端队列的输入序列,则既不能由输入受限的双端队列得到,也不能由输出受限的双端队列得到的输出序列是
4231
。可以得到的是:1234、4132、4213。
输入受限 的 双端队列 是指元素只能从 队列 的一 端输入 ,但可以从 队列 的两端 输出;
1,2,3,4为输入受限的输出队列。
4,1,2,3为输入受限的输出队列
输出受限 的 双端队列 是指只有一端可以进行出队操作而从两端都可以进行入队操作的 队列。
4,2,1,3为输出受限的输出队列 - 已知输入序列为abcd经过输出受限的双向队列后能得到的输出序列有:cadb、bdac。不包括:dacb、dbca。
注意是出队受限的队列(单方向出队),入队可以两端入队,并且只限制进队的顺序,不限制是否全部进入才出队,
cadb
过程: a进队列——b从入队口进队——c从出队口出队——a出队——d从出队口入队——d出队——b出队;
bdac
过程: a进队列——b从出队口进队——b出队——c从入队口入队——d从出队口入队——d出队——a出队——c出队;
- 对于序列( 12 , 13 , 11 , 18 , 60 , 15 , 7 , 19 , 25 , 100 ),用筛选法建堆,必须从值为
60
的数据开始建初始堆。建初始堆意思是从这里开始调整
- 某带链的队列初始状态为 front=rear=NULL 。经过一系列正常的入队与退队操作后, front=rear=10 。该队列中的元素个数为
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关联容器,支持快速增删 - 栈和队列都是线性表,只是在插入和删除时受到了一些限制。只允许在端点处插入和删除元素。栈、队列的插入和删除操作时间复杂度应该都是O(1)。
- 线性表是线性结构。
- 循环队列队头指针可以大于队尾指针,也可以小于队尾指针。
- 队列进出的顺序不会因为进出的交替进行而有所改变。
- 用链接方式存储的队列,在进行插入或删除运算时
头、尾指针可能都要修改
。 - 表达式"X=A+B*(C-D)/E"的后缀表示形式可以是
XABCD-*E/+=
- 队列的链式存储结构成为链队列,它是限制仅在表头删除和表尾插入的单链表,队头在链头位置。
- 大小为MAX的循环队列中,f为当前对头元素位置,r为当前队尾元素位置(最后一个元素的位置),则任意时刻,队列中的元素个数为
(r-f+MAX+1)%MAX
。 - 有一个虚拟存储系统,若进程在内存中占3页(开始时内存为空),若采用先进先出(FIFO)页面淘汰算法,当执行如下访问页号序列后1,2,3,4,5, 1,2,5,1,2,3,4,5,会发生多少缺页?
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个节点,那么这颗树有(
n-1
)条边。 - 设一棵二叉树的深度(根节点为1)为k,则该二叉树最多有(
2^k-1
)个节点。 - 完全二叉树中设根结点的编号为1,编号为 i 的结点存在右孩子,则右孩子结点的编号为(
2i+1
) - 现有一棵无重复关键字的平衡二叉树(AVL 树),对其进行中序遍历可得到一个
降序序列
。下列关于该平衡二叉树的叙述中,正确的是:树中最大元素一定是无左子树, (可能有右子树)
树中最小元素不一定是叶结点
- 最后插入的结点可能会导致平衡调整,而
不一定是叶结点
) - 【根结点的度一定为 2❌】当树只有1个或两个结点的时候,根结点的度不可能为2。
排序树有两种排序方法:
1.左孩子 < 根节点 < 右孩子(常见)
这种情况中序遍历得到的是一个升序序列
2.左孩子 > 根节点 > 右孩子
这种情况中序遍历得到的是一个降序序列 - 假设某棵二叉查找树的所有键均为1到10的整数,现在我们要查找5。下面
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个节点的二叉链表中,值为空的链域的个数为(
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 - 在一棵二叉排序树上查找值为35的数据,以下比较的数据序列正确的为
46、36、18、28、35
。 - 拓扑排序算法适用于有向无环图。
- 具有 n 个结点的二叉排序树有多种,其中树高最小的二叉排序树是最佳的。
- 哈弗曼树带权路径长度最短的树,路径上权值较大的结点离根较近。
- 当从一个最小堆中删除一个元素时,需要把堆尾元素填补到堆顶位置,然后再按条件把它逐层向下调整到合适位置。
- 满二叉树中,叶子节点个数 = 2 ^ (深度 - 1),即2 ^ (5 - 1) = 16;
- 平衡二叉树的插入节点比较快❌【在平衡二叉树中插入结点要随时保证插入后整棵二叉树是平衡的,所以可能需要通过一次或多次树旋转来重新平衡这个树】
- 二叉查找树的查找效率与二叉树的树型有关,但结点是否复杂不影响查找效率。
- 一个具有767个节点的完全二叉树,其叶子节点个数为(
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
) -
节点按中序遍历为syz的二叉树可能有(
5
)种
-
设二叉树的先序遍历序列和后序遍历序列正好相反,则该二叉树满足的条件是(高度等于其结点数)。先序后序刚好相反,说明一个结点只有一个孩子,也就是结点数就是高度了
对于一棵排序二叉树:(中序)遍历可以得到有序序列。
哈夫曼树
- 下列选项给出的是从根分别到达两个叶结点路径上的权值序列,能属于同一棵哈夫曼树的是 (
24,10,5 和 24,14,6
) 。
1 )都是 24 ,说明 24 是根
2 ) 24 是由子节点之和组成,排除 AB
3 )子节点不能比父节点大或者相等(子节点之和为父节点值)
-
由权值分别为1、12、13、4、8的叶子节点生成一颗哈夫曼树,它的带权路径长度为(
81
)
-
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)左、右子树也分别为二叉排序树;
- 已知 10 个元素 (54,28,16,34,73,62,95,60,26,43) ,按照依次插入的方法生成一棵二叉排序树,查找值为 62 的结点所需比较次数为(
3
)
所以需要比较的数是:54,73,62,共需要3次。
-
- 在含有10个结点的二叉排序树上,查找关键字为20的结点,则依次比较的关键字有可能是``
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,正确。二叉排序树不一定是平衡二叉树
- 由关键字序列(12,7,36,25,18,2)构造一棵二叉排序树(初始为空,第一个关键字作为根结点插入,此后对于任意关键字,若小于根结点的关键字,则插入左子树中;若大于根结点的关键字,则插入右子树中,且左、右子树均为二叉排序树),该二叉排序树的高度(层数)为
4
。
-
在二叉排序树(二叉搜索树)中,最小值结点的左孩子一定为空指针。
-
一个无序的元素序列可以通过构造一棵二叉排序树而变成一个有序的元素序列
-
已知数据元素为(34,76,45,18,26,54,92,65),按照依次插入节点的方法生成一棵二叉排序树,则该树的深度(根深度为1)为
5
。
-
设森林F中有三棵树,第一,第二,第三棵树的结点个数分别为M1,M2和M3。与森林F对应的二叉树根结点的右子树上的结点个数是(M2+M3 )。森林转换为二叉树: 1.将森林中每棵树转换为相应的二叉树 2.第一颗二叉树不动,依次吧后一棵二叉树的跟节点作为前一颗二叉树根节点的右孩子,当所有二叉树链在一起后,所得的二叉树就是森林对应的二叉树
二叉搜索树,用于搜索,因此 内部节点没有重复的元素 。
在一般包含n个节点的二叉搜索树中查找的最差时间复杂度是O(n)
红黑树
- 关于红黑树和AVL树:
- 两者都属于自平衡二叉树【红黑树 并不追求“完全平衡 ”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。】
- 两者查找,插入,删除的时间复杂度相同
- 包含n个内部节点的红黑树的高度是O(log(n))
- TreeMap是一个红黑树的实现,能保证插入的值保证排序
图
-
有向图和无向图都可以进行遍历操作
-
基本遍历算法两种:
深度遍历
和广度遍历
-
图的遍历必须用递归实现❌【深度、广度都有
递归
和非递归
的实现方法。】 -
图的遍历算法可以执行在有回路的图中
-
用邻接表表示图进行深度优先遍历时,通常是采用(
栈
)来实现算法的。深度用栈,广度遍历用队列
深度DFS:需要递归,使用顺序栈;
广度BFS:类似层次遍历;需要循环队列
拓扑
1,2,3,4,5
是下图的正确的拓扑排序序列。
拓扑排序算法流程如下:
(1)在有向图中选一个没有前驱的顶点且输出之。
(2)从图中删除该顶点和所有以它为尾的弧。
重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况则说明有向图中存在环。
应用如上算法:
输出1号顶点并删除1号顶点和所有以它为尾的弧;
输出2号顶点并删除2号顶点和所有以它为尾的弧;
输出3号顶点并删除3号顶点和所有以它为尾的弧;
输出4号顶点并删除4号顶点和所有以它为尾的弧;
输出5号顶点并删除5号顶点和所有以它为尾的弧;
此时,全部顶点均已输出,算法结束,输出序列1,2,3,4,5为该图拓扑排序序列。
- 拓扑排序运算只能用于
有向无环图
。