PSEB Complete Notes
PSEB Complete Notes
Covering:
Digital Logic Design • Programming Concepts
Data Structures • Computer Architecture
Analytical Questions
The test is for Digital IC Design & Verification, which strongly suggests that Digital Logic Design will carry the
highest weight, followed by Computer Architecture. Programming and Data Structures will test fundamentals
rather than advanced topics. Analytical questions test pattern recognition and basic quantitative reasoning
under time pressure.
Hexadecimal Base 16: uses 0–9 and A–F (where A=10, B=11, C=12, D=13, E=14,
F=15) — one hex digit = 4 binary bits
Sign-Magnitude Representation
The leftmost bit (MSB) represents the sign: 0 for positive, 1 for negative. The remaining bits store the
magnitude. For example, in 4 bits, +5 is 0101 and −5 is 1101. The problem with this scheme is that there are two
representations of zero (+0 = 0000 and −0 = 1000), which wastes a value and complicates arithmetic.
4 bits −8 to +7
OR Gate
An OR gate outputs 1 if ANY of its inputs is 1. It is equivalent to boolean addition: Y = A + B. Think of OR as a
parallel circuit — current flows if either switch is closed.
Truth Table: A B | Y = A+B ------|--------- 0 0 | 0 0 1 | 1 1 0 | 1 1
1 | 1
Derived Gates
NAND Gate (NOT-AND)
A NAND gate is an AND gate followed by an inverter. Its output is 0 only when ALL inputs are 1 — the exact
opposite of AND. NAND is extremely important because it is a universal gate: any boolean function can be
constructed using only NAND gates. This makes NAND ideal for manufacturing because circuits can be built using
a single gate type.
NOR Gate (NOT-OR)
A NOR gate is an OR gate followed by an inverter. Its output is 1 only when ALL inputs are 0. Like NAND, NOR is
also a universal gate.
XOR Gate (Exclusive OR)
An XOR gate outputs 1 when its inputs are different. It is written as Y = A ⊕ B = A'B + AB'. XOR is the heart of
binary addition, parity checking, and encryption. A nice property: A ⊕ A = 0, and A ⊕ 0 = A.
XNOR Gate (Exclusive NOR)
XNOR outputs 1 when its inputs are equal. It is the complement of XOR. XNOR is used in comparators to check if
two bits are the same.
0, 0 0, 0, 1, 1 0, 1
0, 1 0, 1, 1, 0 1, 0
1, 0 0, 1, 1, 0 1, 0
1, 1 1, 1, 0, 0 0, 1
These theorems are why NAND and NOR are universal gates — you can use De Morgan to express any AND as a
NOR of inverted inputs, and any OR as a NAND of inverted inputs, which means NAND alone (or NOR alone) is
enough to build anything.
📌 Universal Gates: Both NAND and NOR are universal gates — meaning any logic function, no matter how
complex, can be built using only NAND gates or only NOR gates. This is a very common exam question. The
intuition: NAND can produce NOT (by tying both inputs together), AND (NAND followed by NAND as inverter),
and OR (via De Morgan's). Once you have NOT, AND, and OR, you have everything.
Half Adder
A Half Adder adds two single bits and produces a sum bit and a carry bit. It is called 'half' because it cannot
account for a carry coming in from a previous bit position.
Inputs: A, B Outputs: Sum = A ⊕ B Carry = A · B Truth Table: A B | Sum
Carry ------|----------- 0 0 | 0 0 0 1 | 1 0 1 0 | 1 0 1 1 | 0
1
Full Adder
A Full Adder adds three bits: two operand bits (A, B) and a carry-in (Cin) from the previous stage. It produces a
sum bit and a carry-out (Cout). Full adders can be chained to add multi-bit numbers.
Inputs: A, B, Cin Outputs: Sum = A ⊕ B ⊕ Cin Cout = A·B + Cin·(A ⊕ B)
= A·B + A·Cin + B·Cin (equivalent form)
Multiplexer (MUX)
A multiplexer is a data selector — it takes 2^n data inputs, n select lines, and routes the chosen input to a single
output. A 4-to-1 MUX has 4 data inputs, 2 select lines, and 1 output. MUXes are used everywhere in CPUs to
route data from multiple sources to a single destination (for instance, choosing between the output of the ALU,
a register file, or memory).
4-to-1 MUX: Select | Output S1 S0 | --------|------- 0 0 | I0 0 1 | I1
1 0 | I2 1 1 | I3 Expression: Y = S1'·S0'·I0 + S1'·S0·I1 + S1·S0'·I2 +
S1·S0·I3
Demultiplexer (DEMUX)
A DEMUX is the opposite of a MUX — it takes one input and routes it to one of 2^n outputs based on n select
lines. A 1-to-4 DEMUX has 1 input, 2 select lines, and 4 outputs. DEMUXes are used for distributing data to
multiple destinations, such as enabling one of several memory banks.
Encoder
An encoder converts 2^n input lines into n output lines. If input line k is active (and only one input is active at a
time), the encoder outputs the binary code for k. An 8-to-3 encoder has 8 inputs and 3 outputs. A common
application is a keypad: 16 keys produce a 4-bit code identifying which key was pressed. A priority encoder
handles the case where multiple inputs might be active by outputting the code of the highest-priority active
input.
Decoder
A decoder is the inverse of an encoder — it converts n input lines into 2^n output lines, where exactly one
output is active based on the input binary code. A 3-to-8 decoder has 3 inputs and 8 outputs. Decoders are used
in memory address decoding: the address bits select exactly one memory location.
Comparator
A magnitude comparator compares two binary numbers and produces three outputs: A > B, A = B, and A < B. For
1-bit inputs A and B: A > B is given by A·B'; A = B is given by A XNOR B; A < B is given by A'·B. Multi-bit
comparators chain these together, starting from the most significant bit.
A flip-flop is edge-triggered — it responds to its inputs only at the instant the clock transitions (either the rising
edge or the falling edge, depending on the type). This makes flip-flops synchronous and predictable, which is
why virtually all modern digital designs use flip-flops instead of latches.
SR Flip-Flop (Set-Reset)
The SR flip-flop is the simplest type, built from two cross-coupled NAND or NOR gates. It has two inputs: S (Set)
and R (Reset). Setting S=1 makes Q=1; setting R=1 makes Q=0. The forbidden state S=R=1 is undefined and must
be avoided.
S R | Q(next) ------|-------- 0 0 | Q (hold, no change) 0 1 | 0 (reset) 1 0
| 1 (set) 1 1 | Invalid / Forbidden
D Flip-Flop (Data)
The D flip-flop has a single data input D. On every clock edge, the output Q takes the value of D. It is the most
widely used flip-flop in digital design — most registers are just arrays of D flip-flops. The D flip-flop is so
fundamental that when engineers say 'flip-flop' without qualification, they usually mean a D flip-flop.
D | Q(next) ---|-------- 0 | 0 1 | 1 Behavior: Q takes the value of D on
each rising clock edge.
JK Flip-Flop
The JK flip-flop improves on the SR flip-flop by defining the previously forbidden input combination. When
J=K=1, the output toggles (flips to its opposite). This gives JK flip-flops a useful property for building counters.
J K | Q(next) ------|-------- 0 0 | Q (hold) 0 1 | 0 (reset) 1 0 | 1 (set)
1 1 | Q' (toggle)
T Flip-Flop (Toggle)
The T flip-flop has a single input T. When T=0, the output holds its previous value; when T=1, the output toggles.
T flip-flops are ideal for building frequency dividers and counters.
Setup Time (tsu) The minimum time the data input must be stable BEFORE the active
clock edge. Violating setup causes incorrect capture.
Hold Time (th) The minimum time the data input must remain stable AFTER the
active clock edge. Violating hold causes the new value to leak in
prematurely.
Clock-to-Q Delay (tcq) The time from the active clock edge to when the output Q becomes
stable. This is an intrinsic property of the flip-flop.
Propagation Delay (tp) The time for a signal to travel through combinational logic between
flip-flops.
Clock Skew The difference in clock arrival time between different flip-flops in
the same design. Can cause hold-time violations.
Clock Jitter The variation in the clock period over time. Reduces the effective
clock period.
💡 Example: If tcq = 1 ns, tcomb = 5 ns, tsu = 0.5 ns, tskew = 0.2 ns, then T_min = 6.7 ns and Fmax = 1 / 6.7 ns ≈
149 MHz.
Asynchronous vs Synchronous Counters
An asynchronous counter (also called a ripple counter) uses the output of each flip-flop as the clock for the next.
It is simple but slow, because the clock signal ripples through the chain. A 4-bit ripple counter has a delay of
4×tcq before the final output is valid.
A synchronous counter clocks all flip-flops simultaneously using a common clock. The next-state logic is
computed combinationally. Synchronous counters are faster and more reliable, but use more logic.
Shift Registers
A shift register is a chain of flip-flops where data shifts from one stage to the next on each clock edge. There are
four variants depending on how data enters and exits.
Type Behavior
SISO Serial In, Serial Out — one bit enters, bits shift, one bit exits
SIPO Serial In, Parallel Out — serial data fills the register, then all outputs
are read at once
PISO Parallel In, Serial Out — all bits loaded at once, then shifted out
serially
Response speed Mealy: faster (output tracks input) | Moore: one cycle slower
Module Structure
Every Verilog design is organized into modules. A module has a name, a list of input/output ports, and a body
containing the design logic. Here is the simplest possible module — an AND gate:
module and_gate (input a, input b, output y); assign y = a & b; endmodule
Data Types
• wire: Represents a physical wire — used for combinational logic outputs. Cannot store a value.
• reg: Represents a variable that can hold a value. Used in procedural blocks (always blocks). Despite the
name, reg does not always mean a physical register — it only means the variable holds its value until
reassigned.
• Vectors: Declared with bit ranges, e.g. wire [7:0] data is an 8-bit wire.
Procedural Blocks
The always block is the main construct for describing sequential and complex combinational logic. The
sensitivity list determines when the block executes.
// Combinational logic: execute whenever any input changes always @(*) begin y =
a & b; end // Sequential logic: execute on rising clock edge always @(posedge clk)
begin q <= d; end // Asynchronous reset: execute on clock or reset edge always
@(posedge clk or posedge rst) begin if (rst) q <= 0; else q <= d; end
• Blocking (=): Executes sequentially, one statement at a time. Use in combinational logic (always @*
blocks).
• Non-blocking (<=): All right-hand sides are evaluated first, then all left-hand sides are updated
simultaneously. Use in sequential logic (always @(posedge clk) blocks).
📌 Golden Rule: Always use non-blocking (<=) in sequential logic, and blocking (=) in combinational logic. Mixing
them causes simulation-synthesis mismatches — bugs that appear in hardware but not in simulation.
💡 Example: Consider two non-blocking assignments in sequence inside a clocked block: a <= b; b <= a;.
Both execute simultaneously — they swap values. If you had used blocking: a = b; b = a;, then a would get
b's value, and then b would get the new a (which is the old b), so both end up with b's value — a bug.
D Flip-Flop in Verilog
module dff ( input wire clk, input wire rst, input wire d, output reg q
); always @(posedge clk or posedge rst) begin if (rst) q <= 1'b0; else
q <= d; end endmodule
Key Concepts
• DUT (Design Under Test): The circuit being verified.
• Testbench: A Verilog/SystemVerilog program that drives stimulus into the DUT and checks its outputs.
• Simulation: Running the testbench and DUT together in a simulator (like ModelSim, VCS, Xcelium) to
observe behavior.
• Directed Testing: Writing specific test cases to exercise known scenarios.
• Constrained Random Verification: Generating random stimuli within defined constraints to cover corner
cases the designer didn't think of.
Coverage Metrics
• Code Coverage: Measures how much of the RTL code was executed (line, branch, toggle, FSM state
coverage).
• Functional Coverage: User-defined metrics that track whether specific features and scenarios have been
tested.
• Assertion Coverage: Tracks whether assertions fired during simulation.
An interpreted language (Python, JavaScript, Ruby) executes code line-by-line through an interpreter.
Interpreted programs are slower but have faster development cycles and are platform-independent.
Java is a hybrid — source code compiles to bytecode, which runs on the Java Virtual Machine (JVM). This gives
Java both performance and portability ('write once, run anywhere').
Derived Types
• Arrays: Contiguous block of elements of the same type. Example: int arr[10].
• Pointers: Variables that store memory addresses.
• References (C++ only): Aliases for existing variables. Cannot be null, cannot be reassigned.
• Functions: Reusable blocks of code.
User-Defined Types
• struct: A collection of fields grouped under a single name. In C++, structs can also have methods and
access specifiers.
• union: Like a struct, but all fields share the same memory. Only one field is valid at a time.
• enum: A type representing a fixed set of named constants.
• class (C++): Like a struct but members are private by default. Used for OOP.
• typedef / using: Creates an alias for an existing type.
Scope Types
• Local scope: Variables declared inside a function or block. Destroyed when the block ends.
• Global scope: Variables declared outside any function. Accessible everywhere in the program.
• Block scope: Variables declared inside a {} block — visible only in that block.
register Hint to compiler to store in CPU register for speed (mostly ignored
in modern compilers)
Loops
• for loop: Used when the number of iterations is known in advance. Syntax: for (init; condition;
update) { body }.
• while loop: Executes as long as the condition is true. Check happens before each iteration.
• do-while loop: Like while, but the condition is checked AFTER the body, so the body always executes at
least once.
2.5 Functions
A function is a named block of reusable code that performs a specific task. Functions are the main tool for
decomposing large problems into smaller, manageable pieces. A function has a return type, a name, a
parameter list, and a body.
Parameter Passing
Call by Value
A copy of the argument is passed to the function. Changes made inside the function do not affect the original
variable. This is the default in C, Java, and many other languages.
void increment(int x) { x = x + 1; } int main() { int a = 5; increment(a); //
a is still 5 — the function modified only its local copy }
Call by Reference
The address of the argument is passed, so the function can modify the original variable. In C, this is done with
pointers; in C++, references are cleaner.
// C style with pointers void increment(int *x) { *x = *x + 1; } int main() { int
a = 5; increment(&a); // a is now 6 } // C++ style with references void
increment(int &x) { x = x + 1; }
Recursion
A recursive function is one that calls itself. Every recursive function must have a base case (a condition under
which it stops calling itself) to prevent infinite recursion. Recursion is powerful for problems that can be broken
into smaller subproblems — tree traversal, divide-and-conquer algorithms, mathematical definitions.
💡 Example: Factorial — the classic recursive function:
int factorial(int n) { if (n <= 1) return 1; // base case return n *
factorial(n - 1); // recursive case } // factorial(5) = 5 × 4 × 3 × 2 × 1 = 120
Pillar 1: Encapsulation
Encapsulation is the bundling of data and the methods that operate on that data into a single unit (class),
combined with restricting direct access to the internal state. This is achieved through access specifiers: private
(accessible only within the class), protected (accessible in the class and its derived classes), and public
(accessible anywhere). Encapsulation protects object integrity — you cannot directly modify internal state, only
through well-defined methods.
Pillar 2: Inheritance
Inheritance lets a new class (the child or derived class) acquire the properties and methods of an existing class
(the parent or base class). This models 'is-a' relationships: a Dog is an Animal, a Car is a Vehicle. Inheritance
promotes code reuse and establishes class hierarchies.
class Animal { public: void eat() { cout << "eating"; } }; class Dog :
public Animal { // Dog inherits from Animal public: void bark() { cout <<
"woof"; } }; Dog d; [Link](); // inherited [Link](); // own method
Types of Inheritance
• Single inheritance: One parent, one child.
• Multilevel inheritance: A → B → C (C inherits from B which inherits from A).
• Hierarchical inheritance: Multiple children from one parent.
• Multiple inheritance (C++ only): A class inherits from multiple parents. Java replaces this with interfaces
because multiple inheritance of implementation causes the 'diamond problem.'
Pillar 3: Polymorphism
Polymorphism means 'many forms' — the ability of the same interface to behave differently based on the
underlying object type. There are two main kinds:
Compile-time Polymorphism (Static)
The function to call is determined at compile time. Examples: function overloading, operator overloading.
Runtime Polymorphism (Dynamic)
The function to call is determined at runtime based on the actual object type. This is achieved through virtual
functions in C++. The compiler sets up a virtual table (vtable) for each class with virtual functions, and the
correct function is looked up dynamically.
class Animal { public: virtual void speak() { cout << "generic sound"; } };
class Dog : public Animal { public: void speak() override { cout << "woof"; }
}; Animal* a = new Dog(); a->speak(); // prints "woof" — runtime polymorphism
Pillar 4: Abstraction
Abstraction means exposing only the essential features of an object while hiding the complex internal details.
When you drive a car, you use the steering wheel, pedals, and gear stick — you don't need to know how the
engine, transmission, and fuel injection work. In C++, abstraction is achieved through abstract classes (classes
with at least one pure virtual function) and interfaces.
class Shape { public: virtual double area() = 0; // pure virtual — makes
Shape abstract }; class Circle : public Shape { double radius; public:
Circle(double r) : radius(r) {} double area() override { return 3.14159 *
radius * radius; } }; // Shape cannot be instantiated directly; only concrete
subclasses can.
Region Contents
The heap is managed manually (in C/C++) or by a garbage collector (in Java/Python). Memory allocated on the
heap persists until explicitly freed. Heap allocation is slower than stack but allows much larger objects and
flexible lifetimes. In C, you use malloc/free; in C++, you use new/delete.
Dynamic Memory in C
// Allocate space for 10 integers on the heap int *arr = (int *) malloc(10 *
sizeof(int)); if (arr == NULL) { /* handle allocation failure */ } // Use the
memory... arr[0] = 42; // ALWAYS free when done free(arr); arr = NULL; // good
practice — avoid dangling pointer
Pointer Syntax
int x = 10; int *p; // p is a pointer to int p = &x; // & is the
address-of operator int y = *p; // * is the dereference operator — y now equals
10 // Pointer arithmetic p++; // moves to the next int (advances by
sizeof(int), typically 4 bytes)
Special Pointers
• NULL pointer: A pointer that points to nothing. Always check for NULL before dereferencing.
• **Void pointer (void \*)**: A generic pointer that can point to any type. Must be cast before
dereferencing.
• Function pointer: Stores the address of a function. Can be called through the pointer. Useful for
callbacks.
• **Pointer to pointer (int \*\*p)**: A pointer whose value is the address of another pointer.
SDLC Models
Waterfall Model
Each phase is completed before the next begins — like water flowing down a waterfall. It is simple and easy to
manage but inflexible. Once you are in the testing phase, going back to change requirements is expensive.
Waterfall suits projects with well-defined, stable requirements (e.g., safety-critical systems).
Iterative Model
Development happens in repeated cycles. Each iteration produces a more refined version of the system. Easier
to incorporate changes than Waterfall.
Spiral Model
Combines iterative development with risk analysis. Each 'spiral' goes through planning, risk analysis,
development, and evaluation. Suitable for large, high-risk projects.
Agile Model (Scrum, Kanban, XP)
Agile emphasizes short iterations (called sprints, typically 2 weeks), close collaboration with stakeholders, and
continuous delivery of working software. Scrum is the most popular Agile framework, with roles like Product
Owner, Scrum Master, and Development Team. Agile dominates modern software development because it
adapts quickly to changing requirements.
V-Model
An extension of Waterfall where each development phase has a corresponding testing phase. Requirements →
Acceptance Testing; Design → System Testing; Coding → Unit Testing. Shows that testing is not an afterthought
but a parallel activity.
DevOps
DevOps is not strictly an SDLC model but a culture that bridges Development and Operations. It emphasizes
Continuous Integration (CI) — automatic testing on every commit — and Continuous Deployment (CD) —
automatic release to production. Core tools: Git, Jenkins, Docker, Kubernetes.
Topic 3 — Data Structures
A data structure is a way of organizing data in memory so that it can be accessed and modified efficiently.
Choosing the right data structure is often the difference between a program that runs in seconds and one that
runs in hours. This topic is foundational to computer science and always appears in technical assessments.
O(n) Linear — touches each element once. Linear search, array traversal.
O(n log n) Linearithmic — divide and conquer. Merge sort, quick sort
(average), heap sort.
O(n²) Quadratic — nested loops over the data. Bubble sort, selection sort,
insertion sort.
Advantages
• O(1) random access by index — the fastest possible.
• Cache-friendly — contiguous layout maximizes CPU cache hits.
• Simple and memory-efficient — no per-element overhead.
Disadvantages
• Fixed size in static arrays — cannot grow after declaration.
• O(n) insertion/deletion in the middle — requires shifting elements.
• Wasted memory if the array is larger than needed.
Dynamic Arrays (Vectors)
C++'s std::vector, Java's ArrayList, and Python's list are dynamic arrays that automatically resize. When
the array fills up, it allocates a new block (usually twice as large) and copies elements over. This gives amortized
O(1) append performance.
Linked List
A linked list is a sequence of nodes where each node contains data and a pointer (or reference) to the next
node. Unlike arrays, linked list nodes are scattered throughout memory, connected only by pointers.
Types of Linked Lists
• Singly Linked List: Each node has a pointer to the next. Traversal is one-directional.
• Doubly Linked List: Each node has pointers to both next and previous. Allows backward traversal but
uses more memory per node.
• Circular Linked List: The last node points back to the first. No NULL termination.
Complexity
Operation Complexity
Insert/delete at known node O(1) in doubly linked; O(n) in singly if prev not known
Stack (LIFO)
A stack is a data structure that follows the Last In, First Out principle. Think of a stack of plates: you add (push)
and remove (pop) plates from the top only. The last plate you put on is the first one you take off.
Stack Operations
• push(x): Add x to the top. O(1).
• pop(): Remove and return the top element. O(1).
• peek() / top(): Return the top element without removing it. O(1).
• isEmpty(): Check if the stack has no elements. O(1).
Applications of Stacks
• Function call management: Every function call pushes a stack frame; returning pops it.
• Expression evaluation: Converting infix to postfix, evaluating postfix expressions.
• Backtracking algorithms: Depth-first search, solving mazes.
• Undo functionality: Each edit pushed onto a stack; undo pops the last edit.
• Parenthesis matching: Check if '(((a+b)*c))' has balanced brackets.
Queue (FIFO)
A queue follows the First In, First Out principle. Think of a line at a bank: the first person in line is the first
person served. Insertions happen at the back (enqueue), removals at the front (dequeue).
Queue Operations
• enqueue(x): Add x to the back. O(1).
• dequeue(): Remove and return the front element. O(1).
• front() / peek(): Return the front element without removing it. O(1).
Types of Queues
• Simple Queue: Basic FIFO.
• Circular Queue: The tail wraps around to the front — more memory-efficient than using a linear array.
• Priority Queue: Each element has a priority; dequeue returns the highest-priority element, not the
oldest. Typically implemented with a heap.
• Deque (Double-Ended Queue): Insertion and removal from both ends. Supports both stack and queue
operations.
Applications of Queues
• Breadth-First Search (BFS) in graphs and trees.
• CPU scheduling: Round-robin, FIFO scheduling.
• Printer queues: Jobs are printed in the order submitted.
• Buffering in I/O operations, network packet handling.
3.3 Trees
A tree is a hierarchical data structure consisting of nodes connected by edges. Unlike linear structures, trees
branch. Trees naturally model hierarchies — file systems, organization charts, HTML/XML documents, decision
trees.
Tree Terminology
Term Meaning
Ancestor / Descendant Any node above / below on the path to root / leaves
Binary Tree
A binary tree is a tree where each node has at most two children, called left child and right child. Binary trees
are the foundation for many important structures like BSTs and heaps.
Special Types of Binary Trees
• Full binary tree: Every node has either 0 or 2 children (never 1).
• Complete binary tree: All levels are full except possibly the last, which is filled left-to-right.
• Perfect binary tree: All internal nodes have 2 children AND all leaves are at the same depth.
• Balanced binary tree: The heights of the left and right subtrees of every node differ by at most 1.
📌 Important: A BST is only efficient when balanced. If you insert sorted data into a simple BST, it degenerates
into a linked list with O(n) operations. Self-balancing BSTs (AVL, Red-Black) guarantee O(log n).
AVL Tree
An AVL tree is a self-balancing BST where the heights of the two child subtrees of any node differ by at most 1.
When an insertion or deletion causes imbalance, the tree is rebalanced using rotations (single left, single right,
left-right, right-left). AVL trees guarantee O(log n) for all operations but have more overhead due to rotations.
Red-Black Tree
A Red-Black tree is another self-balancing BST with slightly weaker balance guarantees than AVL but less
rotation overhead. Each node is colored either red or black, and a set of rules about coloring ensures the tree
stays approximately balanced. Red-Black trees are used in the C++ STL (map, set) and the Linux kernel.
Heap
A heap is a complete binary tree that satisfies the heap property. In a max-heap, every parent is greater than or
equal to its children (root is the maximum). In a min-heap, every parent is less than or equal to its children (root
is the minimum). Heaps are usually implemented with arrays, where for a node at index i, the children are at
2i+1 and 2i+2.
Heap Operations
• Insert: Add at end, then bubble up. O(log n).
• Extract max/min: Remove root, move last element to root, then bubble down. O(log n).
• Peek max/min: Look at root. O(1).
• Build heap from an unsorted array: O(n) using bottom-up heapify.
Heap Applications
• Priority queues: Natural fit.
• Heap sort: Build max-heap, repeatedly extract max. O(n log n).
• Dijkstra's algorithm: Min-heap for shortest-path frontier.
3.4 Graphs
A graph is a collection of vertices (nodes) connected by edges. Graphs generalize trees — a tree is a connected
acyclic graph. Graphs model relationships: social networks, road maps, web page links, computer networks.
Graph Types
• Directed vs Undirected: In a directed graph (digraph), edges have direction (one-way streets). In an
undirected graph, edges work both ways (two-way streets).
• Weighted vs Unweighted: Edges in a weighted graph have a numerical cost (distance, time, price).
• Cyclic vs Acyclic: Cyclic graphs have at least one cycle; acyclic graphs have none. A DAG (Directed Acyclic
Graph) is especially important — it models dependencies, tasks, precedence.
• Connected vs Disconnected: A connected graph has a path between every pair of vertices.
Graph Representations
Adjacency Matrix
A 2D array where matrix[i][j] = 1 if an edge exists from vertex i to vertex j, else 0 (or the weight for weighted
graphs). Space: O(V²). Good for dense graphs and O(1) edge lookup. Wasteful for sparse graphs.
Adjacency List
An array of lists, where list[i] contains all vertices adjacent to vertex i. Space: O(V + E). Good for sparse graphs.
Iterating neighbors is O(degree), which is much better than O(V) for adjacency matrix.
Does not work with negative edge weights — if edges can be negative, use Bellman-Ford.
Bellman-Ford Algorithm
Finds shortest paths from a source, and can handle negative edge weights. Detects negative cycles. Complexity:
O(V × E). Slower than Dijkstra but more general.
Floyd-Warshall Algorithm
Finds shortest paths between all pairs of vertices. Uses dynamic programming. Complexity: O(V³). Simple and
elegant, suitable for dense graphs.
• Prim's Algorithm: Grow the MST one vertex at a time, always adding the cheapest edge from the tree to
a new vertex. O(E log V) with a heap.
• Kruskal's Algorithm: Sort all edges by weight, add the next cheapest edge that does not create a cycle.
Uses Union-Find. O(E log E).
Collision Handling
A collision occurs when two different keys hash to the same index. There are two main strategies:
• Chaining: Each array slot holds a linked list of all entries that hash there. Simple and effective.
• Open addressing: On collision, probe for the next empty slot. Variants: linear probing (try next slot),
quadratic probing (try i² slots away), double hashing (use a second hash function).
Load Factor
Load factor = (number of entries) / (array size). When it exceeds a threshold (usually 0.7), the table is resized —
typically doubled — and all entries are rehashed. This resize is O(n) but amortizes to O(1) per operation.
3.6 Sorting Algorithms
Sorting is so common that understanding the major algorithms is expected of every software engineer. Each has
different strengths.
Bubble Sort
Repeatedly step through the list, compare adjacent elements, and swap them if they are in the wrong order.
After each pass, the largest unsorted element 'bubbles up' to its correct position. Simple but slow. Best case O(n)
(already sorted, with optimization), average and worst O(n²). Stable.
Selection Sort
Find the minimum element in the unsorted part and swap it with the first unsorted element. Repeat. Always
does O(n²) comparisons but O(n) swaps. Not stable.
Insertion Sort
Build the sorted portion one element at a time by inserting each new element into its correct position in the
sorted portion. Very fast for nearly-sorted data — O(n) best case. Worst case O(n²). Stable.
Merge Sort
A divide-and-conquer algorithm: divide the array in half, recursively sort each half, then merge the two sorted
halves. Always O(n log n), stable, but requires O(n) extra space. Merge sort is preferred when stability is
required or when sorting linked lists (where its merging is natural).
Quick Sort
Another divide-and-conquer: pick a pivot, partition the array so that elements less than the pivot come before
and greater come after, then recursively sort each partition. Average O(n log n), but worst case O(n²) if the pivot
is always the smallest or largest. In-place, not stable. Despite the worst case, quick sort is typically faster than
merge sort in practice due to better cache behavior and lower constants.
Heap Sort
Build a max-heap from the array, then repeatedly extract the maximum and place it at the end. O(n log n) in all
cases, in-place, not stable. Slower than quick sort in practice but has guaranteed performance.
Counting Sort
Non-comparison sort that counts occurrences of each value and uses these counts to produce the sorted
output. Works only when the range of values is small. O(n + k) where k is the range. Stable.
Radix Sort
Non-comparison sort that processes digits one at a time, using a stable sort (like counting sort) on each digit.
O(d × (n + k)) where d is the number of digits. Useful for sorting integers or fixed-length strings.
Merge Sort O(n log n) / O(n log n) / O(n log O(n) / Yes
n)
Binary Search
Requires sorted data. Compare the target with the middle element. If equal, done. If target is smaller, repeat on
the left half; if larger, on the right half. Each step halves the search space, giving O(log n) complexity —
dramatically faster than linear search for large arrays.
💡 Example: Searching in a sorted array of 1 million elements: Linear search takes up to 1,000,000 comparisons.
Binary search takes at most 20 (because log₂(1,000,000) ≈ 20).
int binarySearch(int arr[], int n, int target) { int low = 0, high = n - 1;
while (low <= high) { int mid = low + (high - low) / 2; // avoid overflow
if (arr[mid] == target) return mid; if (arr[mid] < target) low = mid + 1;
else high = mid - 1; } return -1; // not found }
Topic 4 — Computer Architecture
Computer Architecture is the study of how the components of a computer — CPU, memory, I/O — are organized
and interact. For an IC Design engineer, understanding architecture is essential because you are often
implementing the very hardware concepts described here.
Despite this bottleneck, Von Neumann architecture dominates general-purpose computing (PCs, servers,
smartphones) because the unified memory is flexible — programs can be treated as data (enabling compilers,
interpreters, self-modifying code) and the hardware is simpler.
Harvard Architecture
Harvard architecture uses separate memories and buses for instructions and data. The CPU can fetch an
instruction and read/write data in the same clock cycle, doubling memory bandwidth. This makes Harvard faster
but more complex.
Harvard is common in Digital Signal Processors (DSPs) and microcontrollers (ARM Cortex-M, Atmel AVR), where
predictable, high-speed memory access is crucial. Many modern CPUs use a Modified Harvard architecture —
Von Neumann at the main memory level, but Harvard at the L1 cache level (separate instruction cache and data
cache).
Comparison Table
Important Registers
Register Role
IR (Instruction Register) Holds the currently fetched instruction while it is being decoded
and executed.
MAR (Memory Address Holds the address of the memory location being read or written.
Register)
MDR / MBR (Memory Holds the data being transferred to or from memory.
Data/Buffer Register)
SP (Stack Pointer) Points to the top of the stack in memory. Used for function calls
and local variables.
Flags / Status Register Holds status bits: Zero (Z), Carry (C), Sign (S), Overflow (O), Parity
(P).
20. Fetch: The CPU reads the next instruction from memory. The address is in the PC. After the fetch, PC is
incremented to point to the next instruction.
21. Decode: The Control Unit examines the instruction and determines what operation it is (ADD, LOAD,
BRANCH, etc.), what registers/memory are involved, and what control signals to assert.
22. Execute: The operation is performed. For arithmetic, the ALU computes the result. For memory
operations, the address and data are set up.
23. Memory Access (if needed): Read data from memory or write data to memory.
24. Write Back: The result is stored in the destination register.
This 5-step breakdown (Fetch, Decode, Execute, Memory, Writeback) is used in the classic MIPS pipeline.
Simpler architectures combine some of these stages.
CISC was designed in an era when memory was expensive and assembly programming was common — complex
instructions allowed programs to be shorter and more memory-efficient. The trade-off is that the hardware
decoding logic is complex, limiting clock speeds and power efficiency.
The simpler, regular instruction set makes decoding fast, allows higher clock speeds, enables deep pipelines, and
is power-efficient. RISC dominates mobile (ARM), embedded, and supercomputing. Modern x86 CPUs actually
translate CISC instructions into RISC-like micro-operations internally.
Without pipelining, each instruction takes 5 cycles, and one instruction completes every 5 cycles. With
pipelining, after the pipeline is full, one instruction completes every cycle — a 5x speedup in instruction
throughput. The latency per instruction is still 5 cycles, but throughput is dramatically higher.
Pipeline Hazards
Pipelining seems like magic, but it doesn't always work smoothly. Three classes of hazards can stall the pipeline:
Structural Hazards
Two instructions in different stages need the same hardware resource simultaneously. Example: if the CPU has
only one memory port, an instruction in the MEM stage and another in the IF stage compete. Solution:
Duplicate the resource (e.g., separate instruction and data caches — Harvard-style L1).
Data Hazards
An instruction needs a value that an earlier, still-executing instruction will produce. There are three sub-types
based on the order of reads and writes:
• RAW (Read After Write): True dependency. Instruction 2 reads what Instruction 1 writes. Most
common.
• WAR (Write After Read): Anti-dependency. Instruction 2 writes what Instruction 1 reads.
• WAW (Write After Write): Output dependency. Both instructions write to the same register.
Solutions: Pipeline stalls (bubble), forwarding (bypass the result from an earlier stage directly to the needing
instruction), or compiler instruction reordering.
Control Hazards
Branch instructions change the PC, but the branch's outcome is not known until several stages into the pipeline.
By then, the CPU has already fetched the wrong next instruction. Solutions: Branch prediction (guess which way
the branch will go; modern CPUs are ~95% accurate), delayed branching (put a useful instruction in the pipeline
slot after the branch), or branch target buffers.
Cache Concepts
• Cache hit: The data is found in the cache — fast access.
• Cache miss: The data is not in the cache — fetched from the next level, which is much slower.
• Hit rate: Percentage of accesses that hit. Modern CPUs achieve 95%+ L1 hit rates.
• Cache line: The unit of transfer between levels, typically 64 bytes.
• LRU (Least Recently Used): Evict the line that has not been accessed for the longest time. Best hit rate
for most workloads.
• FIFO (First In First Out): Evict the oldest line. Simpler than LRU.
• Random: Evict a random line. Surprisingly effective and cheap.
Paging
Virtual memory is divided into fixed-size pages (typically 4 KB). Physical memory is divided into page frames of
the same size. A page table for each process maps virtual page numbers to physical frame numbers. When the
CPU generates a virtual address, the MMU looks up the page table to find the physical address.
Page Fault
If a required page is not in RAM (maybe it was never loaded, or was swapped to disk), a page fault occurs. The
OS handles it by reading the page from disk into a free frame, updating the page table, and restarting the
faulting instruction. Page faults are very expensive (milliseconds) compared to RAM access (nanoseconds).
Thrashing
If physical RAM is too small for the active working set of processes, the system spends more time swapping
pages in and out than doing useful work. Performance drops catastrophically. The solution is to reduce the
multiprogramming level or add more RAM.
4.9 Buses
A bus is a shared communication pathway connecting CPU, memory, and peripherals. Classic system buses are
split into three logical parts:
• Data bus: Carries the actual data. Width (e.g., 64 bits) determines how much data can be transferred per
clock.
• Address bus: Carries memory addresses. Width determines the maximum addressable memory (32-bit
address bus = 4 GB).
• Control bus: Carries control signals — read/write, interrupt requests, clock.
• Arithmetic progression: Constant difference between consecutive terms (2, 5, 8, 11, → 14).
• Geometric progression: Constant ratio (3, 6, 12, 24, → 48).
• Differences of differences: Second-order patterns (2, 5, 10, 17, 26 — differences are 3, 5, 7, 9 — next is
11, so 37).
• Powers: Squares (1, 4, 9, 16, 25...), cubes (1, 8, 27, 64, 125...).
• Fibonacci-like: Each term is the sum of previous two (1, 1, 2, 3, 5, 8, 13...).
• Alternating patterns: Two interleaved sequences (2, 10, 4, 20, 6, 30 → next pair: 8, 40).
• Primes: 2, 3, 5, 7, 11, 13, 17, 19, 23...
💡 Example: Find the next term: 3, 6, 11, 18, 27, ?
Solution: Differences are 3, 5, 7, 9 — each is 2 more than the previous. Next difference is 11. So next term = 27 +
11 = 38.
💡 Example: Find the next term: 1, 4, 27, 256, ?
Solution: Each term is n^n — 1¹, 2², 3³, 4⁴, so next is 5⁵ = 3125.
5.2 Coding-Decoding
Letters are encoded using a rule (shift, reversal, substitution). Decode the rule and apply it.
Shift Ciphers
💡 Example: If BOOK is coded as CPPL, then READ is coded as?
Solution: Each letter shifted by +1. R→S, E→F, A→B, D→E, so SFBE.
Reverse Coding
💡 Example: If CAT is coded as XZG, decode the rule.
Solution: Each letter is replaced by its 'opposite' (A↔Z, B↔Y, C↔X, ...). C→X, A→Z, T→G.
5.3 Logical Reasoning
Syllogisms
Given two or more statements, determine which conclusions necessarily follow. The best method is to draw
Venn diagrams.
💡 Example: Statement 1: All engineers are hardworking. Statement 2: Some hardworking people are rich. Does it
follow that 'Some engineers are rich'?
Solution: Draw three circles. 'Engineers' is entirely inside 'hardworking'. 'Rich' overlaps with 'hardworking' but
we don't know WHERE the overlap is — it might or might not include the 'engineers' region. Therefore the
conclusion does NOT necessarily follow. Answer: Cannot be determined.
Blood Relations
Draw a family tree with +/− symbols for male/female. Trace each relationship carefully.
💡 Example: Pointing to a photograph, A says: 'He is the son of the only son of my father.' Who is in the
photograph?
Direction Sense
Draw a compass and trace the path step by step. Remember the four cardinal directions: North (up), South
(down), East (right), West (left). Also the diagonals: NE, NW, SE, SW.
💡 Example: A man walks 5 km North, then 3 km East, then 5 km South. How far is he from the starting point and
in which direction?
Solution: The North and South cancel out. He ends 3 km East of start. Answer: 3 km East.
Seating Arrangements
Given clues about who sits where, deduce the final arrangement. Draw seats and fill in names systematically
based on clues. Start with the most specific clue.
Averages
Average = Sum / Count Sum = Average × Count Weighted average = (w1·x1 + w2·x2
+ ...) / (w1 + w2 + ...) Example: Class A has 30 students with avg 70. Class B has
20 students with avg 80. Combined avg = (30×70 + 20×80) / 50 = (2100 + 1600) / 50 =
74
Combination: Selection where order does not matter. How many ways to choose r items out of n? Formula: nCr
= n! / (r! × (n−r)!)
Key Insight
Permutations count arrangements (ABC, ACB, BAC, etc.). Combinations count sets ({A, B, C}). For the same n and
r, permutations are always r! times larger than combinations.
5.6 Probability
Probability is the likelihood of an event, between 0 (impossible) and 1 (certain).
P(Event) = Favorable outcomes / Total outcomes P(not A) = 1 − P(A) For independent
events A and B: P(A and B) = P(A) × P(B) For any two events: P(A or B) = P(A) +
P(B) − P(A and B) Conditional probability: P(A | B) = P(A and B) / P(B)
Solution: P(first ace) = 4/52. P(second ace | first ace) = 3/51. Total = (4/52) × (3/51) = 12/2652 = 1/221.
Solution: A 4-bit counter cycles through 16 values (0000 to 1111). After 37 pulses, the counter is at 37 mod 16 =
5. Binary of 5 = 0101.
Solution: 64K = 2¹⁶ words, so 16 address lines. Each word is 16 bits = 2 bytes. Total = 64K × 2 = 128 KB = 131,072
bytes.
Solution: n grew 10x, so time grows 10² = 100x. Answer: 400 seconds.
Solution: XOR using NAND only requires 4 NAND gates in the standard construction.
Solution: Without pipelining: 100 × 10 = 1000 ns. With pipelining: (5 + 99) × 2 = 208 ns. Speedup = 1000 / 208 ≈
4.8×.
Solution: AMAT = Hit × Hit_time + Miss × (Hit_time + Miss_penalty). Approximation: AMAT = 0.95 × 2 + 0.05 ×
100 = 1.9 + 5 = 6.9 ns.
Problem 7: Two's Complement
Represent −45 in 8-bit two's complement.