如何递归地反转队列

How to reverse a queue recursively?

本文关键字:队列 递归 何递归      更新时间:2023-10-16

我有下面的类模板,我希望在不使用任何外部/附加数据结构的情况下递归地实现reverse()函数。

template <class T> struct Node {
T value; Node<T> ∗next;
};
template <class T> class Dynque
{
protected :
Node<T> ∗front;
Node<T> ∗rear;
int numItems ; public :
Dynque();
Dynque ( Dynque & ) ;
virtual  ̃Dynque ( ) ;
virtual void enqueue (T) ; // add an element
T dequeue();// remove an element from front
bool isEmpty ( ) ;
int getNumItems ( ) ;
void clear (); // remove all elements
void reverse(); // reverse order of elements
};

从概念上考虑如何递归地反转队列可能会很有用。

与所有好的递归函数一样,您需要一个基本情况,而且越简单越好!这里的一个合理选择是选择空队列。扭转空队列很容易——你可以完全恢复到原来的状态。所以我们可以开始这样写这个函数:

template <typename T> void Dynque<T>::reverse() {
if (!isEmpty()) {
/* ... handle the recursive case ... */
}
}

现在我们必须考虑递归的情况。假设我们有一个非空队列,为了简单起见,让我们假设元素编号为1、2、3。。。,n、 像这样:

1   2   3   4   5   6   ...   n-2  n-1  n

在处理队列时,我们只能访问前面的元素。因此,让我们想象一下,我们将这个队列分为两组:第一个元素和其他所有元素:

|
1 | 2   3   4   5   6   ...   n-2  n-1  n
|

现在,看看当我们反转队列时会得到什么:

|
n  n-1  n-2  ...  6   5   4   3   2 | 1
|

请注意,这里的内容与队列中除第一个元素外的所有元素相反,后面是队列的第一个元素。

这引出了一个绝妙的小见解。如果我们从队列的前面出列,我们剩下的是"其他所有东西"。如果我们反转"其他所有的东西",我们就可以将删除的元素入队,这样我们就得到了原始队列的反转。这可能是什么样子:

template <typename T> void Dynque<T>::reverse() {
if (!isEmpty()) {
T first = dequeue(); // Store the first element...
reverse();           // ... reverse everything else ...
enqueue(first);      // ... and enqueue the element we removed.
}
}

看到一个没有参数的递归函数是很奇怪的,因为递归操作是通过入队和出队操作对队列状态进行更改的结果。

如果您想知道为什么这样做,请想想如果您尝试反转队列[1, 2, 3]会发生什么。逻辑如下:

To reverse `[1, 2, 3]`, we dequeue 1, then recursively reverse `[2, 3].
To reverse `[2, 3]`, we dequeue 2, then recursively reverse `[3]`.
To reverse `[3]`, we dequeue 3, then recursively `[]`.
To reverse `[]`, we don't need to do anything!
Now we enqueue the 3 we removed to get `[3]`.
Now we enqueue the 2 we removed to get `[3, 2]`.
Now we enqueue the 1 we removed to get `[3, 2, 1]`.

需要注意的是,这不是一种非常有效的反转队列的方法。它使用大量的堆栈空间来容纳每一步移除的所有元素。您最好使用显式std::stack对象或类似的对象来执行反转(如果您将反转作为一个自由函数来实现),或者利用可以在内部手动重新布线指针以重新排列所有内容的事实。您可以尝试对这个解决方案进行一些优化,但这样做远不如选择一个根本不同的解决方案策略更有价值。

Node reverse(Node front)
{
if(front==NULL)
return NULL;
if(front->next==null)
return front;
Node rev=reverse(front->next);
front->next->next=front;
front->next=null;
return rev; 
}

它应该像反转链表一样工作。希望这能有所帮助。

应该是这样的(未测试):

void reverse() {
// Base case
if (isEmpty()) return;
// Get head
T head = dequeue();
// Reverse everything else
reverse();
// Add head after reversion of everything else
enqueue(head);
}

由于入队()和出队()都对值进行操作,因此它们会创建副本。你真的不想使用这些方法。您没有添加或删除数据,它只是四处移动,所以让我们这样做吧。

void Dynque::reverse()
{
// No effect on 0 or 1 elements' count.
if (isEmpty() || !front->next)
return;
// Remember the element at the front by keeping a pointer to it.
Node<T>* move_me_to_back = front;
// "Erase" the first element.
front = front->next;
// Call again on now shortened queue. 
// In the most nested call, the queue should have only one element, with front == back.
reverse();
// "Insert" the last remembered element.
back->next = move_me_to_back;
// Update the pointer to back.
back = back->next;
}