链表插入数据位移
/*
在一个长度为n的顺序表中向第i个元素(0< i<n+l )之前插入一个新元素时,需向后移动______个元素。
n-i
n-i+l
n-i-1
i
正确答案:B
如果是插入到第i个位置,或者说插入到第i个元素之前,则需要移动n-i+1个元素,公式就是n-(i-1) = n-i+1;
如果是插入到第i个元素之后,则需要移动n-i个元素;
在第i个位置插入数据,其实是将数据放在数组下标(i-1)的位置
所以,原数组要从下标(i-1)开始向后移动
*/
循环链表时间复杂度
/*
若某线性表中最常用的操作是在最后一个元素之后插入一个元素和删除第一个元素,则采用()存储方式最节省运算时间。
单链表
仅有头指针的单循环链表
双链表
仅有尾指针的单循环链表
因为是循环单链表,
如果只要知道尾指针p,则通过计算一次p->next就能获得头指针;插入和删除算法复杂度O(1)+O(1)
而如果只知道头指针,则需要遍历整个链表来获得尾指针的位置;插入和删除算法复杂度O(1)+O(N)
所以D.仅有尾指针的单循环链表存储方式最节省运算时间
*/
循环链表延申的数学问题
/*
一个长度为100的循环链表,指针A和指针B都指向了链表中的同一个节点,
A以步长为1向前移动,B以步长为3向前移动,
一共需要同时移动多少步A和B才能再次指向同一个节点____。
正确答案: E
99
100
101
49
50
类似数学计算问题
答案是E,因为有100个节点,可以先假设先都在节点1,B经过33步刚刚到达节点100,而A在34,
第34步B到达节点3,A在35
所以接下来是3+3n=35+n,
所以n=16,所以总共=34+16=50
所以:B比A多跑了一圈
*/
双向循环链表插入元素相关操作
/*
已知不带头结点的双向循环链表中的结点结构为(data,last,next)
在指针p所指结点之后插入由指针s指向的新结点,相应操作为
正确答案: D
p-> next=s; s-> last=p; p->next-> last=s; S->next=p->next
p=> next=s; p-> next->last=s; S-> last=p;s-> next=p->next
s-> last=p; S-> next-p->next; p-> next s; p->next->last=s
s-> last=p; s-> next=p->next; p->next->last=s; p->next=s
先处理要插入结点的前驱和后继,在处理后继结点的前驱,最后才是前驱结点的后继
具体操作如下:
s的前驱节点指向p s.last
s的后继节点指向p的后继节点 s.next
p的后继节点的前驱节点指向s p.next.last
p的后继节点指向s p.next
第三步的详细说明:
p.next的前驱节点,就是原本是p的后继节点,我们设为p+1
p+1的前驱节点指向s
*/
两栈共享空间
/*
若栈采用顺序存储方式存储,现两栈共享空间V[1..m],
top[i]代表第i个栈( i =1,2)栈顶,栈1的底在v[1],栈2的底在V[m],则栈满的条件是( )。
正确答案: B
top[1]+top[2]=m
top[1]+1=top[2]
top[2]-top[1]|=0
top[1]=top[2]
根据题意
top[1],top[2]分别表示栈1和栈2
栈1向上增长,栈2向上增长
的栈顶
可以这样理解:
栈1从v1开始压栈
栈2从vm开始压栈
直到两个栈帧相遇为止
这样B就出来了
D选项
假设top[1] = top[2]
那么在此之前
两个栈必须全部满了
下一次任意一个栈压栈
这样两个栈就重叠了
就会导致D选项
C选项明显错误
A选项属于无法判断
假设一种极端情况
第一种:
栈1只压了一次,指针指向0
栈2压到了m-6
此时两者相加等于m-6
第二种:
如果栈1压到了5
栈2压到了m-1
此时两者相加等于m+4
第三种:
如果栈1只压了2个
指针指向索引位置1
栈2只压了一个
指针指向索引位置m-1
此时
1+m-1 = m
但两个栈明显没满
*/
存储结构问题
/*
下列叙述中错误的是
正确答案:B
二叉链表是二叉树的存储结构
循环链表是循环队列的存储结构
栈是线性结构
循环队列是队列的存储结构
说明:
队列是在队头插入
队尾删除
而循环链表没有尾节点 不存在尾指针
插入的时候需要遍历一遍
时间复杂度为O(n) 不方便
拓展:
线性结构包括:线性表、栈、队列、串;
线性表的存储结构分为:顺序存储结构和链式存储结构
*/
栈数据结构的记忆功能
/*
下列数据结构具有记忆功能的是?
正确答案: C
队列
循环队列
栈
顺序表
栈经常用于History的记录而已
你想象下,用什么方式来实现浏览器的历史?
我浏览了一个页面,然后入栈,再浏览一个,再入栈,
当想回退一个页面的时候,就弹出栈顶的数据就可以
如果想清空1小时内的浏览记录
正好符合栈的后进先出原则
可以这样来理解:“记忆功能”表示数据结构能够知道数据的进入顺序;
顺序表:数组允许数据在任意位置插入,因此它无法知道数据的进入顺序;
栈和队列:二者都对存储结构进行了输入输出约束,只允许数据在顶端位置插入,
因此数据在结构中的排列顺序就是它们的进入顺序;
栈的特点是FILO,后进栈的先出栈,
所以你对一个栈进行出栈操作,出来的元素肯定是你最后存入栈中的元素,所以栈有记忆功能。
而队列是先进先出,你取队列的第一个元素,得到的是你最先存入队列的元素,
而不是上一个存入队列的元素,所以没有记忆功能
栈的记忆功能集中表现在知道你最后存进去的是什么元素
网页上的后退就是利用了栈的原理
*/
前缀表达式转中缀表达式
/*
某表达式的前缀形式为"+-*^ABCD/E/F+GH",它的中缀形式为()
正确答案: C
A^B*C-D+E/F/G+H
A^B*(C-D)+(E/F)/G+H
A^B*C-D+E/(F/(G+H))
A^B*(C-D)+E/(F/(G+H))
这道题类似于二叉树遍历
就是前缀转中缀的解法
中缀表达式实际上就是正常的运算表达式
根据从右至左扫描计算过程如下:
扫描到运算符直接将栈顶的两个元素进行该运算符的运算
然后再入栈
题目中的前缀形式为:+-*^ABCD/E/F+GH
1) 扫描 H,是数字 入栈 ,栈中为: H
2) 扫描 G 为数字 入栈 ,栈中为:G,H
3) 扫描+ 为运算符 ,依次弹出G ,H ,得到 G+H 的结果 入栈,栈中为: G+H(在这里方便讲解 标记为 G+H)
4) 扫描 F 为数字 ,入栈 ,栈中为 F, G+H
5) 扫描 / 为运算符, 依次弹出 F,G+H ,计算F/(G+H) 的结果入栈 ,栈中为 F/(G+H)
6) 扫描 E 为数字,入栈,栈中为 E, F/(G+H)
7) 扫描 / 为运算符, 依次弹出E, F/(G+H) ,计算 E/(F/(G+H))
8) 扫描 D 为数字,入栈 栈中为:D, E/(F/(G+H))
9) 扫描 C 为数字,入栈 栈中为:C,D, E/(F/(G+H))
10) 扫描 B 为数字,入栈 栈中为:B,C,D, E/(F/(G+H))
11) 扫描 A 为数字,入栈 栈中为:A,B,C,D, E/(F/(G+H))
12) 扫描^ 为运算符,依次弹出 A,B 计算 A^B的结果入栈, 栈中为:A^B ,C,D, E/(F/(G+H))
13) 扫描*为运算符,依次弹出 A^B,C 计算 A^B*C的结果入栈, 栈中为:A^B* C,D, E/(F/(G+H))
14) 扫描-为运算符,依次弹出 A^B*C,D 计算 A^B*C-D的结果入栈, 栈中为:A^B* C-D, E/(F/(G+H))
15) 扫描+为运算符,依次弹出 A^B*C-D, E/(F/(G+H)) 计算 A^B*C-D+E/(F/(G+H)) 的到结果
最后得到的表达式为: A^B* C-D+ E/(F/(G+H))
*/
入栈操作
/*
若一个栈以向量V...存储,初始栈顶指针top为n+1,则下面x入栈的正确操作是()。
A. top=topt1; VEtop]=x B. V[top]=x; top=top+1
C. top=top-1; V[top]=x D. V[top]=x; top=top-1
正确答案: C
top=top+1; V[top]=x
V[top]=x; top=top+1
top=top-1; V[top]=x
V[top]=x; top=top-1
具体操作如下:
1.如果初始化top=-1,则栈底地址到栈顶是由小到大的,这样top=-1的时候才表示栈为空
2.如果初始化top=n+1,n表示栈容量
则栈底地址到栈顶是由大到小的
这样top=n+1的时候才表示栈为空
题目显然符合2情况
所以top-- 才符合栈顶指针向上移动
所以先将指针向下移动一位
然后将x赋值
此时x位于栈顶位置V[n]
注意:这个栈是从大到小的
*/
拓展知识
/*
下面的一些说法哪些是正确的:( )
正确答案: B C
缓存策略中基于LRU的淘汰策略,在缓存满时,
会把最近进入缓存的数据先淘汰,以保持高的命中率
中缀表达式A+(B+C)*D的后缀表达式为:ABC+D*+
堆栈是一种LIFO的数据结构
高级语言通过编译或者即时编译(JIT)后成为汇编语言被机器装载执行
TCP协议和UDP协议都在IP协议之上,TCP是面向连接的,UDP是面向非连接的
但无论TCP还是UDP建立通信都需要一次握手,以确保对方的端口已经打开
现代的操作系统一般都分为用户态和内核态,
用户态和内核态的切换是经常发生的
程序员不需要对内核态和用户态的切换进行编程关注
说明:
A:刚好说反了,LRU的过程如下(其实很好理解,访问的频率越高越不该丢弃)
1.新数据插入到链表头部
2.每当缓存命中(即缓存数据被访问),则将数据移到链表头部
3.当链表满的时候,将链表尾部的数据丢弃
其实就是最小活跃的缓存淘汰算法
B:中缀表达式变后缀表达式,有种简单的方法:
1.先将中缀表达式加括号:(A + ((B + C) * D))
2.再把运算符移到括号后面(前缀移到前面):(A ((B C)+ D)*)+
注意:移动括号需要按层次,只允许高1个层次
3.把括号去掉:ABC+D*+
C:LIFO:Last In First Out 后进先出,堆栈就是指栈
D:汇编语言也并不能被机器执行,机器可以执行的是二进制的机器语言
E:TCP建立通信需要三次握手,而UDP,在传送数据前不需要先建立连接
远地的主机在收到UDP报文后也不需要给出任何确认
F:这个读起来就不像对的...程序员是可以通过调用fork()函数的方式进行切换的
*/