TAOCP 2.2.2 Exercise 8

We are asked to extend the sequential allocation scheme for multiple stacks, as described by equations (9) and (10) and the repacking algorithm (Algorithm G), to the case in which one or more of the l...

Section 2.2.2: Sequential Allocation

Exercise 8. [26] Explain how to modify (9), (10), and the repacking algorithms for the case that one or more of the lists is a queue being handled circularly as in (6a) and (7a).

Verified: no
Solve time: -


Setup

We are asked to extend the sequential allocation scheme for multiple stacks, as described by equations (9) and (10) and the repacking algorithm (Algorithm G), to the case in which one or more of the lists is a queue handled circularly as in (6a) and (7a).

Let $n$ denote the total number of lists, each occupying a contiguous segment of memory within $L_0 < L \le L_\infty$. Let $X[i]$ denote the $i$th list. For each stack, we have variables BASE[i] and TOP[i] as in Section 2.2.2. For each circular queue, we introduce pointers F[i] and R[i] corresponding to the front and rear, with wraparound modulo $M[i]$, the allocated capacity of queue $i$. The goal is to describe insertion, deletion, and repacking in a manner analogous to Algorithm G, ensuring that OVERFLOW occurs only when the total memory is exhausted.

We must provide modifications of (9), (10), and the repacking algorithm to accommodate both stack and queue lists.

Solution

Let us classify each list $X[i]$ as either a stack or a circular queue. Define a flag $Q[i]$ such that $Q[i] = 1$ if $X[i]$ is a queue and $Q[i] = 0$ if it is a stack. Let BASE[i] denote the starting location for stack $i$ or the allocated segment start for queue $i$, and let TOP[i] denote the top of stack $i$ or the rear R[i] for queue $i`.

Insertion

  1. Stack insertion (as in (9)):

For $X[i]$ a stack (Q[i] = 0), insertion proceeds exactly as in (9):

$$ \text{TOP}[i] \leftarrow \text{TOP}[i] + 1;\quad \text{if TOP}[i] > \text{BASE}[i+1],\ \text{then OVERFLOW; otherwise set } \text{CONTENTS(TOP}[i]) \leftarrow Y. $$

  1. Queue insertion (modification of (6a)):

Let $X[i]$ be a circular queue (Q[i] = 1) with allocated segment from BASE[i] to BASE[i+1] - 1, of size $M[i] = \text{BASE}[i+1] - \text{BASE}[i]$. Let F[i] and R[i] denote the front and rear pointers, initialized as F[i] = R[i] = BASE[i]. Then

$$ \text{if } R[i] = \text{BASE}[i+1]-1,\ \text{then } R[i] \leftarrow \text{BASE}[i],\ \text{else } R[i] \leftarrow R[i]+1; $$

$$ \text{if } R[i] = F[i],\ \text{then OVERFLOW;} $$

$$ X[R[i]] \leftarrow Y. $$

This preserves the circular allocation of (6a) within the memory segment assigned to queue $i$.

Deletion

  1. Stack deletion (as in (10)):

$$ \text{if TOP}[i] = \text{BASE}[i],\ \text{then UNDERFLOW; otherwise set } Y \leftarrow \text{CONTENTS(TOP}[i]),\ \text{TOP}[i] \leftarrow \text{TOP}[i]-1. $$

  1. Queue deletion (modification of (7a)):

$$ \text{if } F[i] = R[i],\ \text{then UNDERFLOW;} $$

$$ \text{if } F[i] = \text{BASE}[i+1]-1,\ \text{then } F[i] \leftarrow \text{BASE}[i],\ \text{else } F[i] \leftarrow F[i]+1; $$

$$ Y \leftarrow X[F[i]]. $$

This maintains circular semantics within the segment allocated to queue $i$.

Repacking

Algorithm G must be modified to account for queue lists:

  1. Step G1 and G2 (compute available space): For each queue $i$ of allocated length $M[i]$, the occupied space is

$$ \text{OCC}[i] = \begin{cases} \text{TOP}[i]-\text{BASE}[i], & Q[i]=0\ (\text{stack})\[2mm] (R[i]-F[i] + M[i]) \bmod M[i], & Q[i]=1\ (\text{queue}) \end{cases} $$

and the free space is $\text{FREE}[i] = \text{BASE}[i+1]-\text{BASE}[i] - \text{OCC}[i]$. Then set

$$ \text{SUM} \leftarrow L_\infty - L_0 - \sum_{j=1}^n \text{OCC}[j]. $$

For stack $i$, define $D[i] = \max(0, \text{TOP}[i]-\text{OLDTOP}[i])$ as in the original algorithm. For queues, define $D[i] = \max(0, \text{OCC}[i]-\text{OLDOCC}[i])$, where OLDOCC[i] stores the occupied space after the previous allocation.

  1. Step G3 (check for full memory): If SUM < 0, then OVERFLOW occurs. Otherwise, proceed to compute allocation factors.
  2. Step G4 (allocate extra space): Compute $\alpha$ and $\beta$ as in the original algorithm:

$$ \alpha \leftarrow 0.1 \times \text{SUM}/n,\qquad \beta \leftarrow 0.9 \times \text{SUM}/\sum_j D[j]. $$

  1. Move segments:
  • For stacks, move elements up or down as in parts (a) and (b) of Section 2.2.2.
  • For queues, the circular segment is treated as contiguous memory from BASE[i] to BASE[i+1]-1. If more space is needed, shift subsequent queues rightward (or preceding queues leftward) to expand BASE[i+1] - BASE[i]. Elements are copied preserving their relative circular order; for queue $i$, elements from F[i] to R[i] are moved as a contiguous block starting at the new BASE[i], and F[i] and R[i] are updated to the new offsets.
  1. Update bookkeeping: Set BASE[i] and TOP[i] for stacks, BASE[i], F[i], and R[i] for queues. Update OLDTOP[i] or OLDOCC[i] accordingly.
  2. Insertion after repacking: Perform the original insertion (CONTENTS(TOP[i]) \leftarrow Y for stacks, X[R[i]] \leftarrow Y for queues) safely, since memory has been repacked.

Verification

We check the correctness for both stacks and queues:

  1. Memory bounds: All lists remain within BASE[i] < L \le BASE[i+1], satisfying sequential allocation constraints.
  2. OVERFLOW detection: If SUM < 0 after gathering statistics, no further insertion is possible without exceeding L_\infty - L_0, satisfying the condition that overflow occurs only when total memory is exhausted.
  3. Circular semantics: For queues, F[i] and R[i] wrap around modulo the allocated segment size, preserving the behavior of (6a) and (7a).
  4. Stack semantics: Stack insertions and deletions follow (9) and (10), ensuring correct LIFO behavior.
  5. Repacking correctness: Shifting stack or queue segments either up or down preserves element order and relative positions, ensuring that subsequent operations remain valid.

Notes

This generalization allows any mixture of stacks and circular queues to coexist in a shared sequential memory area. The crucial modifications are:

  • Compute occupied space differently for stacks and circular queues.
  • Shift queues as contiguous blocks during repacking, updating F[i] and R[i] accordingly.
  • Maintain separate OLDTOP[i] and OLDOCC[i] for incremental allocation calculations.

No fundamental change to Algorithm G is required, only careful treatment of queues as segments with modular indexing. This approach extends naturally to any number of mixed lists.

This completes the solution.