9.7 KiB
- Ufds: Union-Find Disjoint Set
- Ufds find() with path compression
- Concise Ufds find()
- Ufds constructor: initializer list vs body
- What does std::vector::resize do?
- Ufds destructor: when to omit
- Correct Ufds constructor: is this correct?
- Incorrect Ufds constructor: correct or wrong?
- What does new return?
- What does delete do?
- Is this correct Ufds with C-style array?
- Equivalent std::array version: correct or wrong?
- std::array vs vector vs C-style array
- Vector vs Array: is vector backed by array?
- Vector size vs capacity
- Comparison: which Ufds storage to use?
- C-style array key properties
- std::iota for Ufds initialization
- Path compression in find()
- Original Ufds mistakes: is this correct?
- Heap allocation: what and why?
Ufds: Union-Find Disjoint Set cpp datastructure
Front
Write a minimal Ufds class with vector parent, constructor initializing parents to self
Back
#include <vector>
#include <numeric>
class Ufds {
private:
std::vector<int> parent;
public:
Ufds(int n) : parent(n) {
std::iota(parent.begin(), parent.end(), 0);
}
};
Ufds find() with path compression cpp datastructure
Front
Write the find() method for Ufds with path compression
Back
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
Concise Ufds find() cpp datastructure
Front
Is this correct? What does it do?
int find(int x) {
if (parent[x] == x) return x;
return parent[x] = find(parent[x]);
}
Back
Yes, correct and equivalent to the longer version.
- Base case: if
xis its own parent, returnx - Recursive case: find root of parent, then assign and return
The assignment parent[x] = find(parent[x]) returns the result while compressing the path.
Ufds constructor: initializer list vs body cpp cpp
Front
What is the difference between these two Ufds constructors?
// Initializer list
Ufds(int n) : parent(n) {
std::iota(parent.begin(), parent.end(), 0);
}
// Body style
Ufds(int n) {
parent.resize(n);
std::iota(parent.begin(), parent.end(), 0);
}
Back
Initializer list: direct constructs parent with size n (one allocation)
Body style: default constructs parent, then resize (potential double allocation)
Both produce same result, initializer list is more efficient.
What does std::vector::resize do? cpp cpp
Front
What does resize(n) do on a std::vector<int>?
Back
Resizes the vector to have n elements:
- If
n> current size: adds elements (value-initialized, usually 0) - If
n< current size: truncates the vector - Reallocates if capacity < n
std::vector<int> v;
v.resize(5); // v = {0, 0, 0, 0, 0}
Ufds destructor: when to omit cpp cpp
Front
Should Ufds have a destructor? Ufds() {} vs Ufds() = default
Back
Omit entirely if only using std::vector (auto-cleanup)
If you must write one, prefer = default to explicitly show intent
Empty {} works but = default is clearer intent for future maintenance
Correct Ufds constructor: is this correct? cpp cpp
Front
Is this correct?
Ufds(int n) {
p.resize(n);
std::iota(p.begin(), p.end(), 0);
}
Back
Yes, correct. This is the body-style initialization.
One potential issue: p is default-constructed first, then resize may reallocate.
For efficiency, prefer initializer list: Ufds(int n) : p(n)
Incorrect Ufds constructor: correct or wrong? cpp cpp
Front
Is this correct?
std::vector<int> p;
Ufds(int n) {
p = new std::vector<int>(n);
}
Back
Wrong.
new std::vector<int>(n) returns a vector<int>* (pointer), but p is a vector<int> (not a pointer).
Can't assign a pointer to a vector.
What does new return? cpp cpp
Front
What does new Type return? And new Type[n]?
Back
new Type returns a pointer to that type: Type*
new Type[n] returns a pointer to an array: Type*
int* p1 = new int; // single int
int* p2 = new int[10]; // array of 10 ints
What does delete do? cpp cpp
Front
What does delete do? delete[]?
Back
delete frees a single object allocated with new
delete[] frees an array allocated with new[]
int* p1 = new int;
int* p2 = new int[10];
delete p1; // free single object
delete[] p2; // free array
Mismatching delete with new[] causes undefined behavior.
Is this correct Ufds with C-style array? cpp cpp
Front
Is this correct?
class Ufds {
private:
int* parent;
public:
Ufds(int n) : parent(new int[n]) {}
~Ufds() { delete[] parent; }
};
Back
Correct. This manually manages the heap-allocated array.
new int[n]allocates array ofnints on heapdelete[] parentin destructor frees it
This works but requires manual cleanup — error prone compared to std::vector.
Equivalent std::array version: correct or wrong? cpp cpp
Front
Is this correct?
#include <array>
#include <numeric>
class Ufds {
private:
std::array<int, 10> parent;
public:
Ufds() {
std::iota(parent.begin(), parent.end(), 0);
}
};
Back
Correct for compile-time fixed size.
But size 10 is hardcoded — not parameterized.
std::array<T, N> requires N be a compile-time constant.
For runtime size, must use std::vector.
std::array vs vector vs C-style array cpp cpp
Front
Compare std::array<T, N>, std::vector<T>, and T* for Ufds parent array
Back
| Type | Size | Cleanup | Use when |
|---|---|---|---|
std::array<T, N> |
compile-time | automatic | size known at compile time |
std::vector<T> |
runtime | automatic | size known at runtime |
T* p = new T[n] |
runtime | manual (delete[]) |
legacy code only |
Vector vs Array: is vector backed by array? cpp cpp
Front
Is std::vector backed by an array? How does it support variable length?
Back
Yes, std::vector allocates a contiguous memory block (like a heap array).
Typical implementation: three pointers
start— pointer to datafinish— pointer to last elementend_of_storage— pointer to end of allocated capacity
vector<int> v(5);
v.push_back(1); // may trigger reallocation if capacity exceeded
When capacity is exceeded, vector: allocates larger block, copies elements, deallocates old block.
Vector size vs capacity cpp cpp
Front
What is the difference between size() and capacity() in std::vector?
Back
size()— number of actual elements storedcapacity()— allocated storage space
std::vector<int> v(3); // size=3, capacity=3
v.push_back(1); // size=4, capacity may be >=4
v.push_back(1); // size=5, capacity may be >=5
reserve(n) pre-allocates capacity without resizing.
Comparison: which Ufds storage to use? cpp cpp
Front
When would you choose std::array, std::vector, or T* new for Ufds parent array?
Back
std::array<T, N> — only if N is known at compile time
std::vector<T> — if size is determined at runtime (typical Ufds case)
T* new T[n] — legacy code only; vector is safer and equivalent performance
For Ufds: std::vector is the idiomatic choice because size is runtime-determined.
C-style array key properties cpp cpp
Front
What are key properties of C-style arrays (raw arrays)?
Back
int arr[5]; // fixed size, stack-allocated
int* p = new int[n]; // dynamic size, heap-allocated
- Decay to pointer on function call (lose size info)
- No bounds checking
sizeof(arr)gives bytes, not element count- Must manage lifetime manually for heap arrays
C-style arrays in C++ are generally avoided in favor of std::array~/~std::vector.
std::iota for Ufds initialization cpp cpp
Front
How to use std::iota to initialize Ufds parent array to [0, 1, 2, ..., n-1]
Back
#include <numeric>
std::vector<int> parent(n);
std::iota(parent.begin(), parent.end(), 0);
iota fills the range with consecutive values starting from given start value
Path compression in find() cpp datastructure
Front
What does "path compression" mean in find()?
Back
After finding the root, point each visited node directly to the root:
parent[x] = find(parent[x]); // compresses path
This flattens the tree structure, making future find() calls O(α(n)) ≈ O(1)
Original Ufds mistakes: is this correct? cpp cpp
Front
Is this correct?
class Udfs {
int parent[];
vector<int> p;
public:
Ufds(int n) {
p = new vector<int>;
}
~Ufds() {
p ? free p;
}
};
Back
Wrong. Multiple errors:
int parent[]— illegal incomplete array typeUfds(int n)constructor but class isUdfs(typo)p = new vector<int>— can't assign pointer to vectorfree p— can't free a vector, andpis not a pointerUfdsdestructor but class isUdfs(typo)
Heap allocation: what and why? cpp cpp
Front
What does "heap allocated" mean? Why use it?
Back
Heap allocation with new lives until delete is called or program ends.
Stack allocation (local variables) is自动 freed when out of scope.
int arr[10]; // stack — freed when function returns
int* p = new int[10]; // heap — lives until delete[]
Heap needed when: size unknown at compile time, or object must outlive scope.