【问题标题】:Dequeue an item from a set of 1 elements从一组 1 个元素中取出一个项目
【发布时间】:2016-06-08 07:33:23
【问题描述】:

给定一个带有前后指针的队列类型的单链表实现,当你从一组 1 个元素中取出一个项目时,是否需要将后指针设置为 null?

我正在阅读 Nell Dale 的 C++ Plus Data Structures,在第 5.2 章中,他在他的 Dequeue 方法中写道:

if (front == NULL)
  rear == NULL;

我想知道为什么这是必要的。我能想到的唯一原因是他针对空集实现 Enqueue 的方式:

if (rear == NULL)
  front = newNode;
else
  rear->next = newNode;
rear = newNode;

但是这个条件不能改成if (front == NULL)

【问题讨论】:

    标签: linked-list queue


    【解决方案1】:

    对于正确的代码,您需要维护每个操作的数据结构不变量。原始代码的编写方式,不变量如下所示:

    • A1:front 指向第一个元素,如果存在的话; NULL 否则
    • A2:rear 指向最后一个元素,如果存在的话; NULL 否则

    您声称不变量可能是这些:

    • B1:front 指向第一个元素,如果存在的话; NULL 否则
    • B2:rear 指向最后一个元素(如果存在)

    现在这些是相似的,但完全不同。当列表为空时,B 不变量不需要任何特定的 rear 值。现在您可以使用 B 不变量来实现列表,当然,因为它承认空状态和非空状态之间的区别,这足以提供正确的实现。

    但这并没有说明 A 或 B 在实践中是否更好。如果使用 B 不变量,则查看最后一个元素的函数不能简单地返回 rear 的值,因为如果队列为空,它的值可以是任何值;您必须先测试 front 的值。

    // for the A2 invariant
    return rear ;
    
    // for the B2 invariant
    return ( front == NULL ) ? NULL : rear ;
    

    这归结为是否要在出列时进行测试和分配,或者是否要在查看最后一个元素时进行测试。这是一个优化问题,而不是正确性问题。如果您永远不需要查看最后一个元素,您可以针对它进行优化。

    综上所述,这是过早优化危害的主要案例。对NULL 的额外指针分配几乎不会成为性能问题。当现有代码使用 B 不变量时,当有人依赖 A 不变量时,更可能成为问题的是在维护期间引入缺陷。 A 不变量在认知上更简单,因为 frontrear 是类似设置的,两者都没有特殊行为。 B 不变量可能表现更好,但代价是复杂性。根据您的具体情况,任何一种选择都可能更好。

    这个故事的寓意:总是记录你的不变量。

    【讨论】:

    • 您能解释一下记录不变量的重要性吗?
    • 我认为答案已经提供了这一点。代码 sn-p 提供了假设不同不变量的函数的替代实现。如果你写错了,你就写了有缺陷的代码。总是有多种方法可以实现任何给定的类,但是这些不同的方法(通常)是不相互兼容的。如果您不指定不变量,则很容易错误地将实现 sn-ps 本身正确但不正确的混合在一起。
    猜你喜欢
    • 1970-01-01
    • 2019-10-19
    • 2011-06-13
    • 1970-01-01
    • 2020-03-03
    • 1970-01-01
    • 1970-01-01
    • 2019-06-25
    • 1970-01-01
    相关资源
    最近更新 更多