链表

1

以下函数对于给定的链表(第一个节点为 head)执行什么操作?

void fun1(struct node* head) {
    if (head == NULL)
        return;
    fun1(headnext);
    printf("%d  ", headdata);
}
正确答案:B

此函数 fun1 通过递归方式以逆序打印链表的元素。其执行逻辑如下:

  1. 递归终止条件:当传入的 headNULL 时,函数直接返回,表示已到达链表末尾。
  2. 递归调用:函数优先调用自身处理下一个节点(head→next),这一过程会持续到链表最后一个节点。
  3. 回溯打印:当递归调用栈开始回退时,依次从最后一个节点向第一个节点打印每个节点的值(printf("%d ", head→data);)。

因此,该函数通过先递归遍历至链表末尾,再在回溯阶段按逆序访问节点的方式,实现链表的逆序输出。

2

当将链表数据结构与数组进行比较时,以下哪项或多项描述是正确的?( )

正确答案:E

当比较链表数据结构与数组时,以下几点成立:

  • 插入和删除操作
    链表允许在列表的任何位置高效地进行插入和删除操作,因为只需调整指针;而在数组中,这些操作可能代价较高,因为插入或删除点之后的所有元素都需要移动。

  • 内存分配
    链表使用动态内存分配,因此可以根据需要增长或缩小;而数组具有固定大小,需要提前分配连续的内存块。

  • 访问时间
    数组提供对任意元素的常数时间访问(假设已知索引),而链表访问元素的时间与列表中的元素数量成线性关系,因为需要从开头遍历列表才能找到目标元素。

  • 随机访问
    数组支持随机访问,即可以通过索引直接访问任意元素;而链表不支持随机访问,需要遍历列表才能找到特定元素。

3

考虑以下函数,该函数以双链表头节点的引用作为参数。假设双链表节点包含前驱指针 prev 和后继指针 next

void fun(struct node** head_ref) {
    struct node* temp = NULL;
    struct node* current = *head_ref;
    while (current != NULL) {
        temp = currentprev;
        currentprev = currentnext;
        currentnext = temp;
        current = currentprev;
    }
    if (temp != NULL)
        *head_ref = tempprev;
}

假设将以下双链表 1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 ↔ 6 的头节点引用传递给上述函数。函数调用后,修改后的链表应是什么样子?( )

正确答案:C
  • 函数行为分析
    函数通过迭代方式实现双链表的反转:

    1. 从头节点开始,逐个节点交换 prevnext 指针
    2. 每次迭代后,current 移动到原本的前驱节点(此时已变为后继)
    3. 循环终止时,temp 指向原链表的尾节点(现为新头节点的前驱)
    4. 最终将头指针更新为 temp→prev(即原链表的尾节点)
  • 执行过程示例
    原始链表:1 ⇄ 2 ⇄ 3 ⇄ 4 ⇄ 5 ⇄ 6
    反转后结果:6 ⇄ 5 ⇄ 4 ⇄ 3 ⇄ 2 ⇄ 1

  • 关键细节

    • 每个节点的 prevnext 指针被直接交换
    • 头指针最终指向原链表的最后一个节点
    • 时间复杂度 O(n),空间复杂度 O(1)
      因此选项 (C) 是正确答案。
4

以下函数 reverse() 旨在反转一个单链表。函数末尾缺少一行代码。

/* Link list node */
struct node {
    int data;
    struct node* next;
};

/* head_ref 是指向链表头(或起始)指针的双指针 */
static void reverse(struct node** head_ref) {
    struct node* prev = NULL;
    struct node* current = *head_ref;
    struct node* next;
    while (current != NULL) {
        next = currentnext;
        currentnext = prev;
        prev = current;
        current = next;
    }
    /*MISSING STATEMENT HERE*/
}

应添加什么语句来替代 /*MISSING STATEMENT HERE*/,以使该函数能正确反转链表?( )

正确答案:A
缺失的语句应该将 *head_ref(这是一个双指针)的值设置为 prev,因为 prev 是反转后的链表的新头节点
因此正确选项是 (A)。
5

当 start 指向以下链表的第一个节点 1→2→3→4→5→6 时,以下函数的输出是什么?( )

void fun(struct node* start) {
    if (start == NULL)
        return;
    printf("%d  ", startdata);
    if (startnext != NULL)
        fun(startnextnext);
    printf("%d  ", startdata);
}
正确答案:D

函数执行流程分析:

  1. 前序遍历阶段

    • 首次访问节点时打印数据:1 → 3 → 5
    • 通过 fun(start→next→next) 跳过相邻节点,形成间隔访问
  2. 回溯阶段

    • 到达末尾节点5后,递归栈开始回退
    • 每层递归返回时再次打印当前节点数据:5 → 3 → 1
  3. 完整输出序列

    • 前序遍历结果:1 3 5
    • 回溯遍历结果:5 3 1
    • 合并后总输出:1 3 5 5 3 1
6

以下 C 函数以一个单链列表作为输入参数。它通过将最后一个元素移动到列表的前面来修改该列表,并返回修改后的列表。代码中有一部分被留空。请选择包含正确伪代码的选项以填充空白行。

typedef struct node {
    int value;
    struct node *next;
} Node;

Node* move_to_front(Node* head) {
    Node *p, *q;
    if ((head == NULL : || (headnext == NULL)) return head;
    q = NULL;
    p = head;
    while (pnext != NULL) {
        q = p;
        p = pnext;
    }
    _____________________________
    return head;
}
正确答案:D

要将最后一个元素移到列表的前面,需要执行以下步骤:

  1. 将倒数第二个节点设为最后一个节点
    qnext 指针设为 NULL,这样原来的最后一个节点就变成了倒数第二个节点。

  2. 设置新头节点的连接
    将最后一个节点 pnext 指向原来的头节点 head,这样 p 成为新的头节点后,仍然保留原有链表的其余部分。

  3. 更新头节点
    head 指向 p,完成移动操作。

7

以下函数以一个整数的单链表作为参数,并重新排列列表中的元素。该函数被调用时,列表包含按顺序排列的整数 1, 2, 3, 4, 5, 6, 7。函数执行完成后,列表的内容会是什么?( )

struct Node {
    int value;
    struct Node* next;
};
void rearrange(struct Node* list) {
    struct Node* p;
    struct Node* q;
    int temp;
    if (list == NULL || listnext == NULL) {
        return;
    }
    p = list;
    q = listnext;
    while (q != NULL) {
        temp = pvalue;
        pvalue = qvalue;
        qvalue = temp;
        p = qnext;
        q = (p != NULL) ? pnext : NULL;
    }
}
正确答案:B

函数 rearrange() 的工作原理如下:

  1. 交换机制
    函数通过两个指针 pq 遍历链表,每次交换相邻节点的值。具体流程为:

    • p 指向当前节点,q 指向下一个节点;
    • 交换 p→valueq→value
    • p 移动到 q→nextq 移动到 p→next(若存在)。
  2. 执行过程分析
    初始链表为 1 → 2 → 3 → 4 → 5 → 6 → 7,函数操作步骤如下:

    • 第一次交换:1 ↔ 22 → 1 → 3 → 4 → 5 → 6 → 7
    • 第二次交换:3 ↔ 42 → 1 → 4 → 3 → 5 → 6 → 7
    • 第三次交换:5 ↔ 62 → 1 → 4 → 3 → 6 → 5 → 7
    • 循环终止条件满足后,最终结果为 2, 1, 4, 3, 6, 5, 7
  3. 验证示例
    若输入为 3, 5, 7, 9, 11,函数执行后输出为 5, 3, 9, 7, 11,与本题逻辑一致。

8

假设每个集合都表示为一个元素顺序任意的链表。在并集、交集、成员资格和基数这些操作中,哪一项会是最慢的?( )

正确答案:D
  • 基数和成员资格操作肯定不是最慢的

    • 基数操作只需统计链表中的节点数量
    • 成员资格操作只需遍历链表查找匹配项
  • 交集操作
    需要逐个检查 L1 的元素是否存在于 L2 中,并输出在 L2 中找到的元素

  • 并集操作
    实现方式可能有多种:

    1. 输出 L1 的所有节点,并仅输出那些不在 L2 中的节点
    2. 输出 L2 的节点
  • 时间复杂度分析
    上述两种操作的时间复杂度均为 O(n²),因此并集和交集是速度最慢的操作

9

考虑以下定义的函数 f

struct item {
    int data;
    struct item *next;
};

int f(struct item *p) {
    return ((p == NULL) || (pnext == NULL) || ((pdata <= pnextdata) && f(pnext)));
}

对于给定的链表 p,函数 f 返回 1 当且仅当( )。

正确答案:B

解释:

该函数通过递归检查链表中每个节点与其后继节点的数据关系。具体逻辑如下:

  1. 基本情况:

    • 如果当前节点 pNULL 或其后继节点为 NULL,函数返回 1。这表示链表已到达末尾,无需进一步比较。
  2. 递归条件:

    • 若当前节点的数据值小于或等于后继节点的数据值(p→data <= p→next→data),则继续递归检查后继节点(f(p→next))。
    • 若上述条件不成立(即当前节点数据值大于后继节点),则整个表达式返回 0
  3. 整体判断:

    • 函数仅在所有相邻节点均满足非递减顺序时,才能保证每层递归返回 1,最终结果为 1
    • 若存在任意一对相邻节点违反非递减顺序,递归会在该层返回 0,导致整个函数返回 0

因此,函数 f 返回 1 的充要条件是链表中的元素严格按数据值的非递减顺序排列。

10

使用一个循环链表表示队列,仅通过一个变量 p 来访问该队列。p 应指向哪个节点,才能使入队(enQueue)和出队(deQueue)操作均能在常数时间内完成?( )

正确答案:A

答案不是"(b) 首节点",因为从首节点无法在 O(1) 时间内获取尾节点。但如果 p 指向尾节点,则可以通过尾节点在 O(1) 时间内同时实现入队和出队操作,因为从尾节点可以立即定位到首节点。以下是示例函数(注意这些函数仅为示例且不完整,缺少基础情况处理代码)。

/* p 是指向尾节点地址的指针(双指针)。此函数在尾节点后添加新节点,并更新尾节点*p 指向新节点 */
void enQueue(struct node **p, struct node *new_node) {
    /* 缺少对边界情况的处理代码,如*p 为 NULL */
    new_nodenext = (*p)next;
    (*p)next = new_node;
    (*p) = new_node; /* 新节点现在是尾节点 */
    /* 注意:p→next 仍然是首节点,p 是尾节点 */
}

/* p 指向尾节点。此函数移除首元素并返回出队元素 */
struct node *deQueue(struct node *p) {
    /* 缺少对边界情况的处理代码,如 p 为 NULL、p→next 为 NULL 等 */
    struct node *temp = pnext;
    pnext = pnextnext;
    return temp; /* 注意:p→next 仍然是首节点,p 是尾节点 */
}
11

在单链表中查找从头开始的第 8 个元素和从尾部开始的第 8 个元素的时间复杂度分别是多少?假设链表节点数为 n,且 n > 8。

正确答案:A
  • 头部第8个元素:只需从头节点开始依次访问8次即可定位目标节点,操作次数固定为常数(与n无关),因此时间复杂度为 O(1)
  • 尾部第8个元素
    1. 需先遍历整个链表统计节点总数(O(n))
    2. 再从头节点向后移动 (n-8) 次定位目标节点(O(n))
      总体时间复杂度为 O(n)
  • 综合两种情况,最终答案为 A
12

是否可以仅使用每个节点一个指针来创建双向链表( )。

正确答案:B

是的,可以通过在每个节点中存储前驱和后继节点地址的异或值来使用单个指针实现双向链表。这种技术利用了异或运算的性质:

  • 异或特性:若已知当前节点地址与相邻节点地址中的任意一个,
  • 计算方式:即可通过异或计算得到另一个节点的地址,
  • 实现效果:从而实现双向遍历功能。

该方案通过巧妙的数据结构设计,在节省空间的同时保留了双向链表的核心操作能力。

13

给定一个单链表中某个节点 X 的指针。仅提供该节点的指针,未提供头节点的指针,能否删除该链表中的节点 X?( )

正确答案:A

解析:
当且仅当 X 不是第一个节点时,可通过以下操作实现:

struct node *temp = Xnext;
Xdata = tempdata;
Xnext = tempnext;
free(temp);

核心思想是通过覆盖当前节点数据并断开后续连接的方式模拟删除操作。此方法依赖于存在可被覆盖的下一个节点(即 X 不是尾节点),但题目限定条件仅为"X 不是第一个节点",因此需特别注意边界情况处理。

14

以下哪项是异或链表的应用?( )

正确答案:C
  • 异或链表是一种内存高效的链表表示方法
  • 它通过存储前一个节点与后一个节点地址的异或组合实现双向遍历
  • 相比传统双向链表(需存储两个指针),能显著减少内存开销
15

N 个元素存储在一个已排序的双向链表中。对于删除操作,会提供一个指向待删除记录的指针;对于减小键值操作(decrease-key),会提供一个指向执行该操作的记录的指针。某个算法按以下顺序对链表执行这些操作:Θ(N) 次删除、O(log N) 次插入、O(log N) 次查找和Θ(N) 次减小键值。所有这些操作的总时间复杂度是多少?( )

正确答案:C
  • 各操作时间复杂度分析

    • 删除操作:需遍历链表定位目标节点,时间复杂度为 Θ(N)
    • 插入/查找操作:利用有序特性通过二分查找定位位置,时间复杂度为 O(log N)
    • 减小键值操作:需重新定位新键值位置并调整指针,时间复杂度为 Θ(N)
  • 总操作次数计算
    $$ M = \Theta(N) + O(\log N) + O(\log N) + \Theta(N) = O(N) $$

  • 最终时间复杂度推导
    所有操作均作用于大小为 N 的链表,因此总时间复杂度为: $$ O(M \log N) = O(N \log N) $$
    虽然单次删除和减小键值操作耗时较高,但其总次数与线性规模相关,整体仍满足 O(N log N) 上界。

16

要在 O(1) 时间内完成两个列表的连接操作,应使用以下哪种列表实现方式?( )

正确答案:C

解析

  • 列表连接操作通常需要定位到列表的末尾节点
  • 单链表和双链表需要从头节点开始遍历至末尾(O(n)时间复杂度)
  • 循环双链表中,每个节点包含前驱和后继指针,且首尾节点相互指向
  • 通过循环双链表的头节点可以直接访问尾节点(前驱指针),无需遍历
  • 因此循环双链表可在常数时间内完成连接操作
17

假设存在两个单链表,它们在某一点相交后合并为一个链表。已知这两个链表的头节点,但不知道相交节点和链表长度。求从两个相交链表中找出相交节点的最优算法的最坏情况时间复杂度是多少?( )

正确答案:C

该算法在最坏情况下具有以下特性:

  • 时间复杂度:Θ(m + n)
  • 空间复杂度:O(1)

实现步骤

  1. 首次遍历两个链表,分别计算其长度 $ m $ 和 $ n $
  2. 将较长链表的起始指针向前移动 $ |m - n| $ 个节点,使两链表剩余长度对齐
  3. 同步移动两个链表的指针,逐节点比较地址直至找到首个相同节点

此方法通过两次线性扫描完成定位,无需额外存储空间,因此是最优解法。选项(C)正确。

18

一个队列使用非循环单链表实现,该队列具有头指针和尾指针(如图所示)。设 n 表示队列中的节点数量。若“入队”操作通过在头部插入新节点实现,“出队”操作通过删除尾部节点实现,则对于这种数据结构,以下哪项是“入队”和“出队”操作最高效的时间复杂度?

正确答案:B

对于入队操作,其执行时间为常数时间(即Θ(1)),因为它仅修改两个指针:创建节点 P → P.Data = 数据;P.Next = Head → Head = P。
对于出队操作,需要获取单链表倒数第二个节点的地址以将其 next 指针置空。由于单链表无法访问前驱节点,必须遍历整个链表才能找到倒数第二个节点:

temp = head;  
While(temp-Next-→Next != NULL){ temp = temp-Next; }  
temp-→next = NULL; Tail = temp;  

由于每次出队都需要遍历整个链表,因此时间复杂度为Θ(n)。选项 (B) 正确。

19

在双向链表中,插入操作会影响多少个指针?( )

正确答案:D

解释 - 实际上,受影响的指针数量取决于插入的位置。但最多有以下三种情况:

  • 插入到开头 - 影响 3 个指针
  • 插入到中间 - 影响 4 个指针
  • 插入到结尾 - 影响 3 个指针
20

考虑一个无序单链表的实现。假设该链表通过头指针和尾指针进行表示(即指向链表第一个节点和最后一个节点的指针)。基于这种表示方式,以下哪个操作无法在 O(1) 时间内完成?( )

正确答案:D
  • 删除链表的末节点时,需要获取倒数第二个节点的地址以将其 next 指针置为 NULL
  • 由于单链表无法直接访问前驱节点,必须从头节点开始遍历至倒数第二个节点
  • 遍历操作的时间复杂度为 $O(n)$,因此该操作无法在常数时间内完成
21

考虑一个单链表,其中 F 和 L 分别是链表第一个和最后一个元素的指针。以下哪些操作的执行时间取决于链表的长度?( )

链表结构如下:F → 1 → 2 → 3 → L

正确答案:C

若 F 和 L 分别为链表首尾元素的指针,则:

  • 删除第一个元素:无需遍历链表,只需将 F = F→next 并释放原首节点;
  • 交换前两个元素:可通过临时节点直接操作,无需依赖链表长度;
  • 删除最后一个元素:需要从头遍历至倒数第二个节点才能修改指针,因此时间复杂度与链表长度相关;
  • 在末尾添加元素:可直接通过 L→next = 新节点 完成,无需遍历。

综上,正确选项为(C)。

22

以下链表操作步骤:

p = getnode();
info(p) = 10;
next(p) = list;
list = p;

会实现哪种类型的操作?( )

正确答案:C

上述步骤实现了在链表头部插入新节点的操作。具体过程如下:

  1. p = getnode()
    通过 getnode() 函数(假设已在别处定义)创建了一个新节点 p

  2. info(p) = 10
    将新节点 p 的信息域设置为值 10

  3. next(p) = list
    将新节点 p 的指针域指向当前链表的头节点(由 list 指针指向)。

  4. list = p
    更新 list 指针使其指向新插入的节点 p,从而将其作为链表的新头节点。

这一系列操作等效于在链表的头部插入节点,也称为“压入”(push)操作。

23

在 DNA 序列比对中,哪种字符串匹配算法常用于高效识别两个 DNA 序列之间的相似性?( )

正确答案:D
  • 标准答案解析
    在 DNA 序列比对领域,更常用的是 Smith-Waterman 算法Needleman-Wunsch 算法 这类专门设计的动态规划算法。
    • Rabin-Karp 算法适用于多模式串匹配,但不适合处理 DNA 序列的局部比对需求。
    • Knuth-Morris-Pratt 算法针对单模式串匹配优化,缺乏对序列相似性评估的能力。
    • Z 函数主要用于字符串前缀分析,与 DNA 序列全局/局部比对目标不符。
24

以下哪种操作在双向链表中比在线性链表中执行得更高效?( )

正确答案:A
  • 双向链表的优势:当已知指向某个节点的指针时,删除该节点的操作效率更高
  • 线性链表的局限性
    • 需要遍历列表以找到待删除节点及其前驱节点
    • 前驱节点需更新next指针跳过待删除节点
    • 最坏情况下时间复杂度为 O(n)(n 为链表节点数量)
  • 核心差异:双向链表通过prev指针可直接访问前驱节点,无需额外遍历
25

在长度为 n 的链表中查找一个元素所需的时间是( )

正确答案:B
解析
在最坏情况下,需要遍历链表中的所有元素才能完成查找操作。由于链表的存储结构决定了无法通过下标直接访问元素,因此必须从头节点开始逐个比较。
这种线性扫描的方式使得时间复杂度与链表长度成正比,即 O(n)。选项 (B) 是唯一符合这一特性的答案。
26

双链表中每个节点的最小字段数是( )

正确答案:C
通常情况下,双链表的每个节点始终包含 3 个字段,即前驱节点指针、数据域和后继节点指针。因此答案应为选项(C)3。然而,通过使用异或(XOR)指针技术,双链表的每个节点可以仅包含 2 个字段:异或指针域和数据域。该异或指针域可同时指向前后节点,这是包含数据域的最佳情况。这种结构称为内存高效的双链表。此外,如果从异或链表中移除数据节点,则双链表的每个节点可以仅包含 1 个字段:异或指针域。但此时没有数据域,因此这种双链表在实际中并无意义。
27

一个双向链表的声明如下:

struct Node {
    int Value;
    struct Node* Fwd;
    struct Node* Bwd;
};

其中 FwdBwd 分别表示链表中相邻元素的前向和后向链接。假设 X 指向的既不是链表的第一个节点也不是最后一个节点,以下哪段代码可以从双向链表中删除 X 所指向的节点?( )

正确答案:A

要从双向链表中删除一个节点,需要更新前一个节点和后一个节点的链接,使它们相互指向对方,从而将待删除的节点从链表中移除。具体来说:

  1. 将前驱节点的 Fwd 指针指向当前节点的后继节点(X→Bwd→Fwd = X→Fwd
  2. 将后继节点的 Bwd 指针指向当前节点的前驱节点(X→Fwd→Bwd = X→Bwd

通过这两个操作,当前节点 X 被逻辑上从链表中移除,后续可通过内存回收机制进行物理删除。

28

考虑一个形式为 $ F \rightarrow \text{elements} \rightarrow L $ 的单链表,其中 $ F $ 是链表中第一个元素的指针,$ L $ 是链表中最后一个元素的指针。以下哪种操作的时间复杂度取决于链表的长度?( )

正确答案:D
原因:单链表无法从尾部直接找到前一个节点,需从头遍历到倒数第二个节点,时间复杂度为 $O(n)$,取决于链表长度。其他操作均为 $O(1)$。
29

以下哪种排序算法可以在最短的时间复杂度内对随机链表进行排序?( )

正确答案:D
  • 归并排序是最适合链表的排序算法之一,时间复杂度为 $O(n \log n)$,且在链表中不依赖随机访问。
  • 链表无法高效地进行下标访问,因此像快速排序堆排序在链表上效率较低。
  • 插入排序在链表上是 $O(n^2)$,不适合随机数据。
30

将 n 个元素插入到一个空的链表中,如果需要保持链表始终有序,那么最坏情况下的时间复杂度是多少?( )

正确答案:C
  • 解析
    在最坏情况下,每次插入操作都需要从链表头开始逐个比较节点,直到找到合适的位置。对于第 $ i $ 个元素($ i = 1, 2, \dots, n $),其插入所需的时间复杂度为 $ O(i) $。因此,总时间复杂度为: $$ \sum_{i=1}^{n} O(i) = O\left(\frac{n(n+1)}{2}\right) = \Theta(n^2) $$ 这种情况发生在所有元素按逆序插入时(例如升序链表中依次插入降序元素)。
31

考虑以下陈述:

i. 先进先出(FIFO)类型的操作由 STACKS 高效支持。
ii. 使用链表实现 LISTS 比使用数组实现 LISTS 在几乎所有基本操作中更高效。
iii. 使用循环数组实现 QUEUES 比使用两个索引的线性数组实现 QUEUES 更高效。
iv. 后进先出(LIFO)类型的操作由 QUEUES 高效支持。

以下哪项是正确的?( )

正确答案:C

正确陈述是:iii. 使用循环数组实现 QUEUES 比使用两个索引的线性数组实现 QUEUES 更高效。
解释:

  • i. STACKS 用于实现后进先出(LIFO)操作,而非先进先出(FIFO)操作。因此,陈述 i 错误。
  • ii. 使用链表实现 LISTS 在某些操作(如在列表中间添加或删除元素)上更高效,但在其他操作(如通过索引访问元素)上效率更低。因此,该陈述错误。
  • iii. 使用循环数组实现 QUEUES 比使用线性数组更高效。这是因为循环数组的 front 和 rear 指针在到达数组末尾时会绕回开头,从而更高效地执行入队和出队操作。因此,该陈述正确。
  • iv. QUEUES 用于实现先进先出(FIFO)操作,而非后进先出(LIFO)操作。因此,陈述 iv 错误。
    综上,选项 (C) 为正确答案。
32

考虑队列 Q1 包含四个元素,Q2 为空(如图中的初始状态所示)。允许在这两个队列上进行的操作只有 Enqueue(Q, element)Dequeue(Q)。在不使用任何额外存储的情况下,将 Q1 的元素以相反顺序放入 Q2 中所需的最少 Q1 入队操作次数是(  )。

1
2
3
4
Q1
Q2
head
head
Initial State
Q1
4
3
2
1
Q2
head
head
Final State
正确答案:D

理解操作 Enq(Qx, Deq(Qy)) 表示从队列 Qy 中出队一个元素并将其入队到队列 Qx。该操作不会占用额外空间,因为一个函数的返回值会立即作为另一个函数的参数传递。请注意,函数的返回值和参数存储在函数栈帧中,因此不需要额外空间,即这些数据的空间开销已包含在函数调用/操作本身中。
解题步骤
按照以下步骤解决此问题:

步骤操作队列状态(操作后)
0-初始状态
1Enq(Q2, Deq(Q1))Q1: [B, C, D]; Q2: [A]
2Enq(Q2, Deq(Q1))Q1: [C, D]; Q2: [A, B]
3Enq(Q2, Deq(Q2))Q1: [C, D]; Q2: [B, A]
4Enq(Q2, Deq(Q1))Q1: [D]; Q2: [B, A, C]
5Enq(Q2, Deq(Q2))Q1: [D]; Q2: [A, C, B]
6Enq(Q2, Deq(Q2))Q1: [D]; Q2: [C, B, A]
7Enq(Q2, Deq(Q1))Q1: []; Q2: [C, B, A, D]
8Enq(Q2, Deq(Q2))Q1: []; Q2: [B, A, D, C]
9Enq(Q2, Deq(Q2))Q1: []; Q2: [A, D, C, B]
10Enq(Q2, Deq(Q2))Q1: []; Q2: [D, C, B, A]

最终状态表明,无需对 Q1 执行任何入队操作即可完成目标。因此,答案为 0