注:以下习题参考 计算机网络(第八版)谢希仁 编著,数据结构与算法 王曙燕 主编。
一、计算机网络(物理层)习题与解答
2-01 物理层要解决哪些问题?物理层的主要特点是什么?
【答案】
要解决的问题:
尽可能屏蔽掉传输媒体和通信手段的差异,使上层数据链路层感觉不到这些差异
-
在传输媒体上传输比特流(0/1信号)
-
定义接口特性(机械、电气、功能、规程)
-
解决同步问题(发送方和接收方时钟一致)
-
规定传输方式(单工、半双工、全双工)
-
确定信号编码方式(如NRZ、曼彻斯特编码)
主要特点:
-
屏蔽底层传输差异,为数据链路层提供透明的比特流传输服务
-
协议与具体的传输媒体无关
-
关注比特传输的物理实现,不关心比特含义
2-02 规程与协议有什么区别?
【答案】
-
协议:整个网络体系结构中各层对等实体通信规则的总称
-
规程:早期对物理层协议的称呼,主要指物理层的通信规则
区别:规程是协议的一个子集,特指物理层的协议。现一般统称"协议"。
2-03 试给出数据通信系统的模型并说明其主要组成构件的作用。
【答案】
数据通信系统模型:
源点 → 发送器 → 传输系统 → 接收器 → 终点
各构件作用:
| 构件 | 作用 |
|---|---|
| 源点 | 产生原始数据的设备(计算机、传感器) |
| 发送器 | 将数据转换为适合传输的信号(调制解调器、网卡) |
| 传输系统 | 传输信号的物理媒体(双绞线、光纤、无线电) |
| 接收器 | 将信号还原为原始数据 |
| 终点 | 接收并处理数据的设备 |
2-04 试解释以下名词:数据、信号、模拟数据、模拟信号、基带信号、带通信号、数字数据、数字信号、码元、单工通信、半双工通信、全双工通信、串行传输、并行传输。
【答案】
| 名词 | 解释 |
|---|---|
| 数据 | 传送信息的实体 |
| 信号 | 数据的电气/电磁表示 |
| 模拟数据 | 连续变化的数据(如声音、温度) |
| 模拟信号 | 连续变化的信号(如正弦波) |
| 基带信号 | 未经调制的原始信号,频率从0开始 |
| 带通信号 | 经调制后搬移到高频的信号 |
| 数字数据 | 离散取值的数据(如文本、整数) |
| 数字信号 | 离散取值的信号(如方波) |
| 码元 | 信号波形的一个基本单元,代表一个离散值 |
| 单工通信 | 单向传输(广播、电视) |
| 半双工通信 | 双向交替传输(对讲机) |
| 全双工通信 | 双向同时传输(电话) |
| 串行传输 | 比特按时间顺序逐位传输 |
| 并行传输 | 多比特同时传输(多根线并行) |
2-05 物理层的接口有哪几个方面的特性?各包含些什么内容?
【答案】
| 特性 | 内容 |
|---|---|
| 机械特性 | 接口形状、引脚数目、排列顺序 |
| 电气特性 | 电压范围、信号电平、阻抗匹配 |
| 功能特性 | 各引脚的功能定义(数据、控制、时钟) |
| 规程特性 | 信号时序、事件顺序、协议流程 |
2-06 数据在信道中的传输速率受哪些因素的限制?信噪比能否任意提高?香农公式在数据传输中的意义是什么?"比特/秒"和"码元/秒"有何区别?
【答案】
传输速率的限制因素:
-
信道带宽(单位时间不能无限传送)
-
信噪比(信号功率与噪声功率之比)
-
码元状态数(调制等级)
信噪比能否任意提高?
不能。受以下限制:
-
硬件设备(功放能力、线性度)
-
信道非线性失真
-
背景噪声(热噪声、干扰)
香农公式的意义:
-
给出了信道容量的理论极限:
C = B log₂(1 + S/N) -
指导通信系统设计,表明带宽和信噪比可以互换
"比特/秒"和"码元/秒"的区别:
-
比特/秒(bit/s):数据传信速率
-
码元/秒(Baud):调制速率
-
关系:
数据率 = 波特率 × log₂(码元状态数)
2-07 假定某信道受奈氏准则限制的最高码元速率为 20000 码元/秒。如果采用振幅调制,把码元的振幅划分为16个不同等级来传送,那么可以获得多高的数据率(bit/s)?
【答案】
数据率 = 20000 × log₂16 = 20000 × 4 = 80000 bit/s
答案:8 kbit/s
2-08 假定要用3 kHz带宽的电话信道传送64 kbit/s的数据(无差错传输),试问这个信道应具有多大的信噪比(分别用比值和分贝表示)?这个结果说明了什么问题?
【答案】
香农公式:C = B log₂(1 + S/N)
64000 = 3000 × log₂(1 + S/N)
log₂(1 + S/N) = 64000/3000 ≈ 21.33
1 + S/N = 2²¹·³³ ≈ 2.64 × 10⁶
S/N ≈ 2.64 × 10⁶
用分贝表示:
(S/N)dB = 10 lg(2.64 × 10⁶) = 10 × (lg2.64 + 6) ≈ 64.22 dB
结论:普通电话信道无法达到64 kbit/s(理论上需要极高的信噪比64.22 dB,实际电话信道的信噪比通常只有30-40 dB)
2-09 用香农公式计算一下,假定信道带宽为3100 Hz,最大信息传输速率为35 kbit/s,那么若想使最大信息传输速率增加60%,问信噪比S/N应增大到多少倍?如果在刚才计算的基础上将信噪比S/N再增大到10倍,问最大信息传输速率能再增加20%吗?
【答案】
已知:B = 3100 Hz,C₁ = 35 kbit/s = 35000 bit/s
由 C = B log₂(1 + S/N) 得:
log₂(1 + S₁/N) = 35000/3100 ≈ 11.29
S₁/N = 2¹¹·²⁹ - 1 ≈ 2503
第一问:C₂ = 35000 × 1.6 = 56000 bit/s
log₂(1 + S₂/N) = 56000/3100 ≈ 18.06
S₂/N = 2¹⁸·⁰⁶ - 1 ≈ 2.74 × 10⁵
倍率 = (2.74×10⁵) / 2500 ≈ 109.6倍
第二问:S₃/N = 10 × (S₂/N) = 2.74 × 10⁶
log₂(1 + S₃/N) ≈ log₂(2.74×10⁶) ≈ 21.39
C₃ = 3100 × 21.39 ≈ 66309 bit/s
C₃ / C₂ = 66309 / 56000 ≈ 1.184,即增加约18.4%,不到20%
答案:需要增大到约110倍;再增大10倍后速率增加约18.4%,不到20%。
2-10 常用的传输媒体有哪些?各有何特点?
【答案】
| 传输媒体 | 特点 |
|---|---|
| 双绞线 | 价格低、安装方便、抗干扰能力一般,适用于局域网 |
| 同轴电缆 | 抗干扰能力强、带宽较高,用于有线电视、早期以太网 |
| 光纤 | 带宽极高、损耗小、抗电磁干扰、安全保密,用于主干网 |
| 无线(无线电、微波、红外) | 无需物理线路、传播距离远、易受干扰、安全性较差 |
2-11 假定有一种双绞线的衰减是0.7 dB/km(在1 kHz时),若容许有20 dB的衰减,试问使用这种双绞线组成的通信距离有多长?如果要使这种双绞线的工作距离增加到100 km,问应当使衰减降低到多少?
【答案】
第一问:衰减系数 α = 0.7 dB/km,最大允许衰减 A = 20 dB
距离 L = A / α = 20 / 0.7 ≈ 28.57 km
第二问:L' = 100 km,求新的衰减系数 α'
α' = A / L' = 20 / 100 = 0.2 dB/km
答案:28.57 km;衰减应降低到0.2 dB/km。
2-12 试计算工作在1200~1400 nm之间以及工作在1400~1600 nm之间的光波的频率宽度。假定光在光纤中的传播速率为2×10^8 m/s。
【答案】
公式:频率宽度 Δf = v × |1/λ₁ - 1/λ₂|
v = 2×10⁸ m/s
1200~1400 nm:
λ₁ = 1200 nm = 1.2×10⁻⁶ m
λ₂ = 1400 nm = 1.4×10⁻⁶ m
Δf = 2×10⁸ × |1/(1.4×10⁻⁶) - 1/(1.2×10⁻⁶)|
= 2×10⁸ × (1/1.2 - 1/1.4)×10⁶
= 2×10⁸ × (0.8333 - 0.7143)×10⁶
= 2×10⁸ × 0.119×10⁶
= 2.38×10¹³ Hz = 23.8 THz
1400~1600 nm:
λ₁ = 1400 nm = 1.4×10⁻⁶ m
λ₂ = 1600 nm = 1.6×10⁻⁶ m
Δf = 2×10⁸ × (1/1.4 - 1/1.6)×10⁶
= 2×10⁸ × (0.7143 - 0.625)×10⁶
= 2×10⁸ × 0.0893×10⁶
= 1.786×10¹³ Hz = 17.86 THz
答案:23.8 THz 和 17.86 THz。
2-13 为什么要使用信道复用技术?常用的信道复用技术有哪些?
【答案】
为什么要使用复用技术?
-
提高信道利用率,让多个用户共享同一传输媒体
-
降低通信成本
常用的信道复用技术:
| 技术 | 全称 |
|---|---|
| FDM | 频分复用 |
| TDM | 时分复用 |
| STDM | 统计时分复用 |
| WDM | 波分复用 |
| DWDM | 密集波分复用 |
| CDMA | 码分多址 |
2-14 试写出下列英文缩写名称,并进行简单的解释。
【答案】
| 缩写 | 全称 | 解释 |
|---|---|---|
| FDM | Frequency Division Multiplexing | 频分复用,不同用户使用不同频率 |
| FDMA | Frequency Division Multiple Access | 频分多址,用户按频率分配信道 |
| TDM | Time Division Multiplexing | 时分复用,用户按时间片轮流使用信道 |
| TDMA | Time Division Multiple Access | 时分多址,用户按时隙分配信道 |
| STDM | Statistical Time Division Multiplexing | 统计时分复用,动态分配时隙 |
| WDM | Wavelength Division Multiplexing | 波分复用,光纤中不同波长传输不同信号 |
| DWDM | Dense Wavelength Division Multiplexing | 密集波分复用,更密集的波长间隔 |
| CDMA | Code Division Multiple Access | 码分多址,不同用户使用不同正交码 |
| SONET | Synchronous Optical Network | 同步光纤网(ANSI标准) |
| SDH | Synchronous Digital Hierarchy | 同步数字体系(ITU-T标准) |
| STM-1 | Synchronous Transport Module Level 1 | SDH的基本速率模块(155.52 Mbit/s) |
| OC-48 | Optical Carrier Level 48 | SONET的光载波等级(2488.32 Mbit/s) |
2-15 码分多址 CDMA 为什么可以使所有用户在同样的时间使用同样的频谱进行通信而不发生相互干扰?这种复用方法有何优缺点?
【答案】
原理:
-
每个站分配一个唯一的正交码片序列
-
不同站的码片序列相互正交(内积为0)
-
发送时用码片序列调制信号
-
接收端用相同码片序列进行相关运算,可恢复原信号
优点:
-
抗干扰能力强
-
保密性好
-
频率规划简单
-
软容量(用户越多,质量下降越平缓)
缺点:
-
存在"远近效应"(靠近基站的强信号会压制远处弱信号)
-
需要功率控制
-
实现复杂度较高
2-16 共有四个站进行码分多址 CDMA 通信。四个站的码片序列为:
A: (-1 -1 -1 +1 +1 -1 +1 +1)
B: (-1 -1 +1 -1 +1 +1 +1 -1)
C: (-1 +1 -1 +1 +1 +1 -1 -1)
D: (-1 +1 -1 -1 -1 -1 +1 -1)
现收到这样的码片序列:(-1 +1 -3 +1 -1 -3 +1 +1)。问哪个站发送数据了?发送数据的站发送的是1还是0?
【答案】
计算方法:接收序列与各站码片序列计算内积,内积 = +1 表示发送1,内积 = -1 表示发送0,内积 = 0 表示未发送。
与A内积:
= [(-1)×(-1)] + [1×(-1)] + [(-3)×(-1)] + [1×(+1)] + [(-1)×(+1)] + [(-3)×(-1)] + [1×(+1)] + [1×(+1)]
= 8
除以8得:+1 → A发送了1
与B内积:
= [(-1)×(-1)] + [1×(-1)] + [(-3)×(+1)] + [1×(-1)] + [(-1)×(+1)] + [(-3)×(+1)] + [1×(+1)] + [1×(-1)]
= -8
除以8得:-1 → B发送了0
与C内积:
= [(-1)×(-1)] + [1×(+1)] + [(-3)×(-1)] + [1×(+1)] + [(-1)×(+1)] + [(-3)×(+1)] + [1×(-1)] + [1×(-1)]
= 0
→ C未发送
与D内积:
= [(-1)×(-1)] + [1×(+1)] + [(-3)×(-1)] + [1×(-1)] + [(-1)×(-1)] + [(-3)×(-1)] + [1×(+1)] + [1×(-1)]
= 8
→ D发送了1
答案:A发送1,B发送0,C未发送,D发送1。
2-17 试比较 ADSL、HFC 以及 FTTx 接入技术的优缺点。
【答案】
| 技术 | 优点 | 缺点 |
|---|---|---|
| ADSL | 利用现有电话线、上下行不对称适合家庭 | 距离受限、速率受线路质量影响 |
| HFC | 带宽较宽、可同时传电视和数据 | 上行带宽小、共享介质、用户多时拥塞 |
| FTTx | 带宽极高、稳定性好、可扩展性强 | 建设成本高、光纤铺设周期长 |
2-18 在 ADSL 技术中,为什么在不到1 MHz的带宽中却可以使传送速率高达每秒几个兆比特?
【答案】
ADSL 在不到 1 MHz 的带宽中实现每秒几兆比特的传输速率,主要依靠以下技术:
原因:
-
ADSL使用离散多音调制(DMT),将1 MHz带宽划分为256个子信道
-
每个子信道根据信噪比分配不同的比特数(2~15 bit/Hz)
-
非对称设计:下行占更多子信道和更高调制等级
-
可达速率:下行8 Mbit/s(ADSL)或24 Mbit/s(ADSL2+)
2-19 什么是 EPON 和 GPON?
【答案】
| 技术 | 全称 | 特点 |
|---|---|---|
| EPON | Ethernet Passive Optical Network | 基于以太网的无源光网络,速率对称(1.25 Gbit/s),成本低 |
| GPON | Gigabit Passive Optical Network | 千兆无源光网络,ITU-T标准,速率更高(2.5 Gbit/s下行),支持多业务 |
相同点:都是无源光网络(PON)技术,采用点对多点结构
二、数据结构(栈和队列)习题与解答
1.单项选择题
(1) (2010考研真题)若元素 a,b,c,d,e,f依次进栈,允许进栈、退栈操作交替进行,但不允许连续三次进行退栈操作,则不可能得到的出栈序列是___
A d,c,e,b,f,a
B c,b,d,a,e,f
C b,c,a,e,f,d
D a,f,e,d,c,b
【答案】D
解析:D选项 e, f, a, b, c, d 要求:
-
e第一个出栈,则e进栈时a,b,c,d已在栈中,顺序为a,b,c,d,e
-
弹出e后,栈顶为d,但第二个出的是f
-
要出f,必须先让f进栈,但f进栈前必须把a,b,c,d都弹出或部分弹出
-
连续出栈序列分析可知不可能实现
(2) 若某堆栈的输人序列为1,2,3,...,n-1,,输出序列的第1个元素为n,则第i个输出元素为__
A n-i+I
B n-1
C i
D.哪个元素都有可能
【答案】A
解析:第一个输出为n,说明n是最后一个进栈并立即出栈。此时栈中剩下1,2,...,n-1(1在栈底,n-1在栈顶),出栈顺序只能是n-1, n-2, ..., 1。所以第i个输出为 n-i+1。
(3) 以数组Q[0..m-1]存放循环队列中的元素,变量 rear 和 qulen 分别指示循环队列中队尾元素的实际位置和当前队列中元素的个数,队列第一个元素的实际位置是___
A. rear-qulen
B. rear-qulen+m
C.m-qulen
D.(1+(rear-qulen+m))mod m
【答案】D
解析:队头位置 = (rear - qulen + 1 + m) mod m。选项D中有(1+(rear-qulen+m)) mod m,等价。
(4) 设输人元素为1,2,3,A,B,输人次序为123AB,元素经过栈后到达输出序列,当所有元素均到达输出序列后,序列___是不可能的。
A.BA321
B.A3B21
C.B32A1
D.AB321
【答案】C
解析:C选项 B32A1 要求:
-
B第一个出栈,则1,2,3,A,B进栈顺序:1,2,3,A进,B进后立即出
-
此时栈为1,2,3,A(A在栈顶),下一个出3,但A在栈顶,出3必须先出A,矛盾
(5) (2012年考研真题)已知操作符包括 " + " " - " " * " " / " " ( " 和 " ) "。将中缀表达式 a+b-a*((c+d)/e-f)+g 转换为等价的后缀表达式 ab+acd+e/f -*-g+ 时,用栈来存放暂时还不能确定运算次序的操作符。若栈初始时为空,则转换过程中同时保存在栈中的操作符的最大个数是___
A.5
B.7
C.8
D.11
【答案】A
解析 :转换过程中栈内最多同时有5个操作符(如处理到 ((c+d)/e-f) 时)。
2.综合题
(1)循环队列的优点是什么?如何判别它的空和满?
【答案】
优点:解决顺序队列的"假溢出"问题,实现空间的循环利用。
判空判满方法:
-
方法一:少用一个元素空间
判空:(rear+1)%m == front
判满:(rear+2)%m == front -
方法二:设置标志位
判空:front == rear && tag == 0
判满:front == rear && tag == 1 -
方法三:记录队列长度
判空:qulen == 0
判满:qulen == m
(2)回文是指正读反读均相同的字符序列,如"abba"和"abdba"均是回文,但"good"不是回文。试写一个算法判定给定的字符串是否为回文。
【核心思路】:利用栈的"后进先出"特性,将字符串前半部分入栈,然后与后半部分比较。
C语言
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool isPalindrome(char* str) {
int len = strlen(str);
char stack[100];
int top = -1;
// 前半部分入栈
for (int i = 0; i < len / 2; i++) {
stack[++top] = str[i];
}
// 若长度为奇数,跳过中间字符
int start = (len % 2 == 0) ? len / 2 : len / 2 + 1;
// 与后半部分比较
for (int i = start; i < len; i++) {
if (stack[top--] != str[i]) return false;
}
return true;
}
C++
#include <iostream>
#include <stack>
#include <string>
using namespace std;
bool isPalindrome(string str) {
stack<char> st;
int len = str.length();
for (int i = 0; i < len / 2; i++) {
st.push(str[i]);
}
int start = (len % 2 == 0) ? len / 2 : len / 2 + 1;
for (int i = start; i < len; i++) {
if (st.top() != str[i]) return false;
st.pop();
}
return true;
}
(3)利用栈的基本操作,写一个返回栈S中结点个数的算法 int StackSize,并说明S为何不作为指针参数?
【核心思路】 :遍历栈内元素,计数返回。S不作为指针参数是因为可以直接访问栈结构体中的长度字段(如果栈有记录)或遍历计数。
C语言(顺序栈)
typedef struct {
int data[100];
int top;
} Stack;
int StackSize(Stack* s) {
return s->top + 1;
}
C++(链栈)
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
class LinkStack {
private:
Node* top;
int count;
public:
LinkStack() : top(nullptr), count(0) {}
void push(int val) {
Node* newNode = new Node(val);
newNode->next = top;
top = newNode;
count++;
}
int size() { return count; }
};
(4)数制转换:将十进制数 N转换为二进制数。
【核心思路】:除2取余法,余数依次入栈,最后出栈得到二进制结果
C语言
#include <stdio.h>
#include <stdlib.h>
void decToBinary(int n) {
int stack[32];
int top = -1;
if (n == 0) {
printf("0");
return;
}
while (n > 0) {
stack[++top] = n % 2;
n /= 2;
}
while (top >= 0) {
printf("%d", stack[top--]);
}
}
C++
#include <iostream>
#include <stack>
using namespace std;
void decToBinary(int n) {
stack<int> st;
if (n == 0) {
cout << 0;
return;
}
while (n > 0) {
st.push(n % 2);
n /= 2;
}
while (!st.empty()) {
cout << st.top();
st.pop();
}
}
(5)设算术表达式中有圆括弧、方括弧和花括弧,设计一个算法,判断表达式中的各种括弧是否配对(将表达式存于一字符数组a[m]中)。
【核心思路】:遍历字符数组,遇到左括号入栈,遇到右括号时与栈顶匹配,匹配则弹出,不匹配则报错。结束时栈空则配对正确。
C语言
#include <stdio.h>
#include <stdbool.h>
bool isMatch(char left, char right) {
return (left == '(' && right == ')') ||
(left == '[' && right == ']') ||
(left == '{' && right == '}');
}
bool checkBrackets(char* expr) {
char stack[100];
int top = -1;
for (int i = 0; expr[i] != '\0'; i++) {
char c = expr[i];
if (c == '(' || c == '[' || c == '{') {
stack[++top] = c;
} else if (c == ')' || c == ']' || c == '}') {
if (top == -1 || !isMatch(stack[top--], c)) {
return false;
}
}
}
return top == -1;
}
C++
#include <iostream>
#include <stack>
#include <unordered_map>
using namespace std;
bool checkBrackets(string expr) {
stack<char> st;
unordered_map<char, char> match = {{')', '('}, {']', '['}, {'}', '{'}};
for (char c : expr) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else if (c == ')' || c == ']' || c == '}') {
if (st.empty() || st.top() != match[c]) return false;
st.pop();
}
}
return st.empty();
}
(6)假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结点,试编写相应的置空队、判队空、入队和出队算法。
【核心思路】:
-
置空队:
rear->next = rear -
判空:
rear->next == rear -
入队:新结点插入 rear 之后,更新 rear
-
出队:删除 rear->next(头结点之后)
C语言
typedef struct Node {
int data;
struct Node* next;
} Node;
typedef struct {
Node* rear;
} Queue;
void initQueue(Queue* q) {
q->rear = (Node*)malloc(sizeof(Node));
q->rear->next = q->rear; // 空循环
}
bool isEmpty(Queue* q) {
return q->rear->next == q->rear;
}
void enqueue(Queue* q, int val) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = val;
newNode->next = q->rear->next;
q->rear->next = newNode;
q->rear = newNode;
}
bool dequeue(Queue* q, int* val) {
if (isEmpty(q)) return false;
Node* head = q->rear->next->next; // 第一个有效结点
*val = head->data;
q->rear->next->next = head->next;
if (head == q->rear) q->rear = q->rear->next; // 删除最后一个
free(head);
return true;
}
C++
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
class CircularQueue {
private:
Node* rear;
public:
CircularQueue() {
rear = new Node(0);
rear->next = rear;
}
bool isEmpty() { return rear->next == rear; }
void enqueue(int val) {
Node* newNode = new Node(val);
newNode->next = rear->next;
rear->next = newNode;
rear = newNode;
}
bool dequeue(int& val) {
if (isEmpty()) return false;
Node* head = rear->next->next;
val = head->data;
rear->next->next = head->next;
if (head == rear) rear = rear->next;
delete head;
return true;
}
};
(7)假设循环队列中只设rear 和 quelen 来分别指示队尾元素的位置和队中元素的个数,试给出判别此循环队列的队满条件,并写出相应的人队和出队算法,要求出队时需返回队头元素。
【核心思路】:
-
判满:
qulen == m -
判空:
qulen == 0 -
队头位置:
(rear - qulen + 1 + m) % m -
入队:在 rear+1 位置插入,更新 rear 和 qulen
-
出队:计算队头位置取出,更新 qulen
C语言
#define MAX_SIZE 100
typedef struct {
int data[MAX_SIZE];
int rear; // 队尾位置
int qulen; // 元素个数
} Queue;
void initQueue(Queue* q) {
q->rear = -1;
q->qulen = 0;
}
bool isFull(Queue* q) {
return q->qulen == MAX_SIZE;
}
bool isEmpty(Queue* q) {
return q->qulen == 0;
}
int getFront(Queue* q) {
return (q->rear - q->qulen + 1 + MAX_SIZE) % MAX_SIZE;
}
bool enqueue(Queue* q, int val) {
if (isFull(q)) return false;
q->rear = (q->rear + 1) % MAX_SIZE;
q->data[q->rear] = val;
q->qulen++;
return true;
}
bool dequeue(Queue* q, int* val) {
if (isEmpty(q)) return false;
int front = getFront(q);
*val = q->data[front];
q->qulen--;
return true;
}
C++
template<typename T>
class Queue {
private:
T* data;
int rear, qulen, capacity;
public:
Queue(int cap) {
capacity = cap;
data = new T[capacity];
rear = -1;
qulen = 0;
}
bool isFull() { return qulen == capacity; }
bool isEmpty() { return qulen == 0; }
int getFront() {
return (rear - qulen + 1 + capacity) % capacity;
}
bool enqueue(T val) {
if (isFull()) return false;
rear = (rear + 1) % capacity;
data[rear] = val;
qulen++;
return true;
}
bool dequeue(T& val) {
if (isEmpty()) return false;
int front = getFront();
val = data[front];
qulen--;
return true;
}
};
(8)在循环队列中,可以设置一个标志域1ag,以区分当尾指针和头指针相等时,队列状态是"空"还是"满"(tag 的值为0表示"空",tag的值为1表示"满"),编写此结构相应的队列初始化、人队、出队算法。
【核心思路】:
-
初始化:
front = rear = 0, tag = 0 -
判空:
front == rear && tag == 0 -
判满:
front == rear && tag == 1 -
入队:插入后若 front == rear 则 tag = 1
-
出队:取出后若 front == rear 则 tag = 0
C语言
typedef struct {
int data[100];
int front, rear;
int tag; // 0空 1满
} Queue;
void initQueue(Queue* q) {
q->front = q->rear = 0;
q->tag = 0;
}
bool isEmpty(Queue* q) {
return q->front == q->rear && q->tag == 0;
}
bool isFull(Queue* q) {
return q->front == q->rear && q->tag == 1;
}
bool enqueue(Queue* q, int val) {
if (isFull(q)) return false;
q->data[q->rear] = val;
q->rear = (q->rear + 1) % 100;
if (q->rear == q->front) q->tag = 1;
return true;
}
bool dequeue(Queue* q, int* val) {
if (isEmpty(q)) return false;
*val = q->data[q->front];
q->front = (q->front + 1) % 100;
q->tag = 0;
return true;
}
C++
template<typename T>
class QueueTag {
private:
T* data;
int front, rear, tag, capacity;
public:
QueueTag(int cap) {
capacity = cap;
data = new T[capacity];
front = rear = 0;
tag = 0;
}
bool isEmpty() { return front == rear && tag == 0; }
bool isFull() { return front == rear && tag == 1; }
bool enqueue(T val) {
if (isFull()) return false;
data[rear] = val;
rear = (rear + 1) % capacity;
if (rear == front) tag = 1;
return true;
}
bool dequeue(T& val) {
if (isEmpty()) return false;
val = data[front];
front = (front + 1) % capacity;
tag = 0;
return true;
}
};
注:以上习题的解答基于作者自己的理解和计算,如果有任何错误,希望各位读者和大佬指出改正,非常感谢!!!