0% found this document useful (0 votes)
3 views13 pages

Introduction

The document discusses Abstract Data Types (ADTs), specifically focusing on Linear Lists and their implementations, including stacks and queues. It covers operations on lists, stack permutations, and challenges related to data structure design, such as implementing a SpecialStack with O(1) operations. Additionally, it addresses the concept of circular queues and the use of linked lists for stack and queue implementations.

Uploaded by

tanujast09
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views13 pages

Introduction

The document discusses Abstract Data Types (ADTs), specifically focusing on Linear Lists and their implementations, including stacks and queues. It covers operations on lists, stack permutations, and challenges related to data structure design, such as implementing a SpecialStack with O(1) operations. Additionally, it addresses the concept of circular queues and the use of linked lists for stack and queue implementations.

Uploaded by

tanujast09
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Introduction

Abstract Data Type


• An abstract data type (ADT) is a programmer-defined data type, comprising a specification and at
least one implementation.
• The specification gives an abstract description of the behavior of instances of the type,
independently of any particular implementation.
• The purpose of abstract data types is to allow the modular construction of software systems
through the composition of components whose individual behavior is well understood.

Linear List ADT


• A list is an ordered sequence of elements 𝑎1, 𝑎2, ⋯ , 𝑎𝑛 .
• The size or length of such a list is 𝑛. Typical operations on list include:
1. get(i): Returns element 𝑎𝑖.
2. set(𝒊, 𝒙): Sets the 𝑖-th element to 𝑥.
3. length(): Returns the length of the list.
4. insert(𝒊, 𝒙): Insert element x just prior to element 𝑎𝑖 (causing the index of all subsequent
items to be increased by one).
5. delete(𝒊): Delete the 𝑖-th element (causing the indices of all subsequent elements to be decreased by 1).
Linear List Implementation
Lists can be allocated in many ways. For example:
• Sequential allocation – as an array
• Singly linked – nodes, each referencing its successor
• Doubly linked – nodes, each referencing its successor and predecessor
Linear List: Stack
• Stacks–Supports insertion/removal from one end, called the top
−push
−pop
−isEmpty
−top

Warm-up problem:
Design a data structure SpecialStack that supports all the stack operations like push(), pop(), isEmpty(), top()
and an additional operation getMin() which should return minimum element from the SpecialStack. All these
operations of SpecialStack must be O(1). To design SpecialStack, you should only use standard Stack data
structure and no other data structure like arrays, list, etc
Growable Stack
Q. Suppose we use an array to implement a stack. What if the array is full and we need to push a new element?
A. We can allocate a new larger array, copy the old one over, and then go on from there.

Cost model: Let’s say that inserting into the array costs 1, taking an element out of the array costs 1, and the
cost of resizing the array is the number of elements moved.

Amortized cost: The amortized cost per operation for a sequence of n operations is the total cost of the
operations divided by n.

Q. What if when we resize we just increase the size by 1? Is that a good idea?
A. If our n operations consist of n pushes then we will incur a total cost 1 + 2 + 3 + 4 + . . . + n = n(n + 1)/2.
That’s an amortized cost of (n + 1)/2 per operation.
Q. What if we instead decide to double the size of the array when we resize?
A. In any sequence of n operations, the total cost for resizing is 1 + 2 + 4 + 8 + . . . + 2i for some 2i < n (if all
operations are pushes then 2i will be the largest power of 2 less than n). This sum is at most 2n − 1. Adding in
the additional cost of n for inserting/removing, we get a total cost < 3n, and so our amortized cost per
operation is < 3.
Stack Permutation
A stack permutation is a ordering of numbers from 1 to n that can be obtained from the
initial ordering 1, 2, ... 𝑛 by a sequence of stack operations
Let us denote push as 1 and pop as 0.
We get 325641 by the sequence of stack operations 111001101000
154623 is not a stack permutation.
Admissible sequence: a sequence of 𝑛 0’s and 𝑛 1’s in which the number of 0’s never exceeds the number of 1’s
If we read the sequence from left to right.
Inadmissible sequence: a sequence of 𝑛 0’s and 𝑛 1’s in which the number of 0’s exceeds the number of 1’s
If we read the sequence from left to right.
Counting the number of stack permutation:
2𝑛
There are sequences of 1’s and 0’s that contain 𝑛 of each. In any inadmissible sequence, locate the
𝑛
first 0 for which the 0’s outnumber the 1’s. Then interchange 1’s and 0’s in the partial sequence up to the
first violation. This results a sequence of (𝑛+1) 1’s and (𝑛-1) 0’s. Conversely, for every sequence of
(𝑛+1) 1’s and (𝑛-1) 0’s we can reverse the process and find the inadmissible sequence. For example, the
sequence 001011100111 must have come from 110100000111. Hence, the number of inadmissible
2𝑛
Sequence is
𝑛−1 2𝑛 2𝑛
#stack permutation = −
𝑛 𝑛−1
Fun Challenge
Given a large integer 𝑛, implement an array data structure 𝐴[1,…, 𝑛] of some type 𝑇, with the following
operations:
• init(𝑣): all elements of 𝐴 are defined to be 𝑣
• get(𝑖): return the value of 𝐴[𝑖], where 1 ≤ 𝑖 ≤ 𝑛
• set(𝑖, 𝑥): set 𝐴[𝑖] = 𝑥, where 1 ≤ 𝑖 ≤ 𝑛
Objective: All the above operations must run in time 𝑂(1), irrespective of the value of 𝑛.
Rules:
1. You may use additional arrays, but you cannot assume they are initialized
2. No fancy data structures other than arrays are allowed
3. No bit manipulation is allowed

Solution:
• Maintain a stack 𝑆 containing the indices of defined elements.
• Maintain a parallel array 𝐵[1,…, 𝑛], where 𝐵[𝑖] indicates the
element of the stack that witnesses that 𝐴[𝑖] is defined.
• Note that 𝐵 may contain garbage, but the stack validates its
“legitimate” entries.
Fun Challenge

1. The command init(𝑣) saves the value of 𝑣 and sets the stack empty (top ← 0).
2. When an entry 𝐴[𝑖] is first defined a value 𝑥, we push index 𝑖 onto the stack, signaling that this entry has been
initialized. We set 𝐵[𝑖] ← top, which validates this entry. (Note that, 1 ≤ 𝐵[𝑖] ≤ top and 𝑆[𝐵[𝑖]] = 𝑖.)
Finally, we set 𝐴[𝑖] ← 𝑥.
3. To test whether 𝐴[𝑖] is defined, test whether 1 ≤ 𝐵[𝑖] ≤ top and 𝑆[𝐵[𝑖]] = 𝑖.
4. The command set(𝑖, 𝑥) applies Step 3 to test whether 𝐴[𝑖] is already defined. If not, we apply Step 2 to define it.
If it was defined, we set 𝐴[𝑖] ← 𝑥
5. The command get(𝑥) applies Step 3 to test whether 𝐴[𝑖] is defined. If so, it returns 𝐴[𝑖]. Otherwise it returns the
default value 𝑣.
Linear List: Queue
• Queue -- a collection of elements that are inserted and removed according to the first-in
first-out (FIFO) principle. Elements enter a queue at the rear and are removed from the front.
−enqueue(e): Insert element e at the rear of the queue
−dequeue(): Remove and return from the queue the object at the front; an error occurs if the
queue is empty
−size(): Return the number of objects in the queue.
−isEmpty(): Return a Boolean value that indicates whether the queue is empty.

• Array-based implementation:

Queue is full 10 20 30 40 50
Front Rear

Queue is full even after


30 40 50
removing 2 elements
Front Rear
Linear List: Circular Queue

Problem: 𝑓 = 𝑟 indicates both full and empty queue!


Linear List: Circular Queue
Ambiguity: 𝑓 = 𝑟 indicates both full and empty queue!

Breaking the ambiguity:


We assume the queue is full when there is one empty slot left in the circular array

Initialization: 𝑓 = 𝑟 = 0
Dequeue(𝑥) Empty queue
Enqueue(𝑥) {
{ if (𝑓 == 𝑟)
if (𝑓 == (𝑟+1) % Size) {
{ Queue empty
Queue full Return
return }
} 𝑥 = array[𝑟]
array[𝑟] = 𝑥 𝑓 == (𝑓+1) % Size
𝑟 == (𝑟+1) % Size return 𝑥
} }
Full queue

Q. Can we implementing circular queue without wasting a space?


Linked List: Stack and Queue

Rear Front

Front/Rear

Push: 25, 32, 50, 99


Multilists and Sparse Matrices
• Lists can be combined to perform more complex structures
• Example: Sparse matrices
• Suppose you have a very large matrix, say 𝑛 × 𝑚, where 𝑛 and 𝑚 are in the tens of thousands.

You might also like