S

powerpoint notes

Overview of Stacks and Queues in Various Programming Languages

  • Stacks and queues are essential data structures used for organizing and manipulating data efficiently.

  • Different programming languages provide various container types to implement these structures, and their efficiencies differ based on the operations performed.

Container Choices Across Languages

C++
  • vector

    • Good for Stack? Yes

    • Good for Queue? No

    • Why:

    • Back operations are O(1), but front operations are O(n) due to linear time complexity when shifting elements.

    • Notes: Cache-friendly implementation.

  • deque

    • Good for Stack? Yes

    • Good for Queue? Yes

    • Why:

    • Both enqueue (adding) and dequeue (removing) operations are O(1) at both ends.

    • Notes: Preferred choice for queues due to its efficiency in both operations.

  • list

    • Good for Stack? Works

    • Good for Queue? Works

    • Why:

    • Insertions and deletions are O(1), though traversal is O(n) due to no inherent indexing.

Java
  • ArrayList

    • Good for Stack? Yes

    • Good for Queue? No

    • Why:

    • Front removal is O(n) as it requires shifting elements to maintain order.

  • LinkedList

    • Good for Stack? Yes

    • Good for Queue? Yes

    • Why:

    • Both ends have O(1) complexity; however, operations in the middle are O(n).

Python
  • list

    • Good for Stack? Yes

    • Good for Queue? No

    • Why:

    • Pop from front (pop(0)) is O(n) due to element shifting, but append and pop from end are O(1).

  • deque (from collections module)

    • Good for Stack? Yes

    • Good for Queue? Yes

    • Why:

    • Both enqueue and dequeue operations at each end are O(1), making it an efficient choice for both stacks and queues.

Circular Queue

  • Defines a circular queue as treating the underlying array as a circular structure, where the ends are connected.

  • Utilizes two indices:

    • front: Position from which to remove an element.

    • rear: Position at which to add an element.

  • Advantages:

    • Prevents shifting of elements with every enqueue or dequeue operation.

    • When the queue is full, an internal array resize is necessary (resize logic is abstracted here).

    • Ensures that all enqueue and dequeue operations maintain an amortized time complexity of O(1) due to array resizing.

Circular Queue Pseudo-Code

Initialize
  • Create an array of size k.

  • Set front to 0, rear to 0, and size to 0.

Enqueue(x)
  • If size == array.length, call resizeArray().

  • Insert x at position rear in the array:

    • array[rear] = x

  • Update rear index:

    • rear = (rear + 1) mod array.length

  • Increment size:

    • size = size + 1

Dequeue()
  • If size == 0, return or throw an exception.

  • Retrieve value at front position:

    • value = array[front]

  • Update front index:

    • front = (front + 1) mod array.length

  • Decrement size:

    • size = size – 1

  • Return the retrieved value.

Peek()
  • If size == 0, return or throw an exception.

  • Return the value at front position.

isEmpty()
  • Return boolean indicating if size equals 0.

length()
  • Return current size of the queue.

Circular Queue Representation

  • Visualize a circular queue with various elements to demonstrate positions of front and rear.

    • Front:

    • Element at index representing front position (e.g., 3).

    • Rear:

    • Element at index representing rear position (e.g., 7).

Amortization

  • Concept of amortization explains that most operations are inexpensive over a series of executes, but occasionally one is expensive (such as resizing operations).

  • It derives an averaged cost for all operations performed over time.

  • For data structures like dynamic arrays or hash tables, the amortized time complexity can be often considered O(1).