Module ২ · vector: যে container-টা সবার আগে হাতে আসে
vector কখন ব্যবহার করবে, আর কখন না
এই lesson-এ যা শিখবে
- নিজের data নিয়ে চারটা প্রশ্ন করে
vectorআর ওর প্রতিবেশীদের মধ্যে বেছে নিতে পারবে। - তুলনার chart আর সিদ্ধান্তের flowchart পড়তে পারবে, আর default কেন
vector, সেটা বুঝিয়ে বলতে পারবে। - জেনে-বুঝে একটা vector function-এ পাঠাতে পারবে
const&বা&দিয়ে, আর C++20-এ একটাspanview হিসেবে।
Maria সবসময় একটা sorted leaderboard রাখে। প্রতিটা নতুন score vector-এর মাঝখানে ওর ঠিক জায়গায় গিয়ে বসে, তাই তালিকা কখনো sort করতে হয় না। এটা কাজ করে, কিন্তু 200,000টা score হলে এক সেকেন্ডের বেশি লাগে। ওর code-এ কোনো ভুল নেই। ও বেছেছে ভুল container। কোনটা বাছা উচিত ছিল, যে প্রশ্নগুলো ওকে সেটা বলে দিত, এই lesson সেগুলোই তোমাকে দেবে।
তোমার data নিয়ে চারটা প্রশ্ন
একটা container মানে কোন কোন operation সস্তা, তার একটা প্রতিশ্রুতি। তাই container বাছো এটা জিজ্ঞেস করে যে তোমার program সবচেয়ে বেশি কী করে। প্রায় সব ক্ষেত্রে চারটা প্রশ্নই সিদ্ধান্তটা দিয়ে দেয়।
| প্রশ্ন | যে উত্তর vector-এর দিকে দেখায় | যে উত্তর অন্য দিকে দেখায় |
|---|---|---|
| 1. এটা কি একটা ক্রম, নাকি key দিয়ে কিছু খোঁজো? | কোনো এক ক্রমে সাজানো: নম্বর, reading, চাল | key বা মান দিয়ে ("42 কি আছে?", "Zara-র score"): set বা map, Module 8 আর 9 |
| 2. Position ধরে পড়ো, নাকি ভিতরে খোঁজো? | position ধরে, v[i], বা ক্রমে হেঁটে | বারবার একটা মান খোঁজা: sorted container বা hash table, Module 8 থেকে 10 |
| 3. এটা কোথায় বাড়ে আর কমে? | শেষে | সামনেও: deque, Module 4; মাঝখানে, হাতে থাকা একটা জায়গায়: list, Module 5 |
| 4. এটা কত বড়, আর একটা element কত বড়? | যেকোনো size; element ছোট, বা কম সরানো হয় | program লেখার সময়েই size ঠিক: array, Module 4; বিশাল element ঘন ঘন সরানো: index-এর একটা vector |
Maria-র উত্তর: হ্যাঁ, এটা একটা ক্রম, কিন্তু ও মান দিয়ে খোঁজে আর মাঝখানে insert করে। চারটার মধ্যে দুইটা উত্তর vector থেকে সরে একটা sorted container-এর দিকে দেখায়। তাই code লেখার আগেই প্রশ্নগুলো একটা container-এর নাম বলে দেয়।
তুলনার chart
Chart-টা vector-কে বসায় সেই চারটা container-এর পাশে, যাদের সাথে ওকে সবচেয়ে বেশি গুলিয়ে ফেলা হয়। খরচগুলো এসেছে cppreference-এ প্রতিটা container-এর page থেকে। Memory-র সারিটা মাপা: GCC 12-এ দশ লাখ int, প্রতিটা container যত byte চেয়েছে সব গুনে, Compiler Explorer-এ একবার চালিয়ে।
Vector-এর কলামটা উপর থেকে নিচে পড়ো: শেষে সস্তা, index-এ সস্তা, আর memory-তে সবচেয়ে কম খরচ। ওর দুর্বল সারি হলো সামনে, মাঝখানে আর খোঁজা। বাকি প্রতিটা কলাম এর কোনো একটায় জেতে, আর অন্য কিছুতে হারে। একটা list প্রতিটা int-এর পাশে দুইটা pointer রাখে, তাই memory লাগে ছয় গুণ। একটা set মানগুলো ক্রমে রাখে আর একটা মান খুঁজে পায় O(log n)-এ, কিন্তু memory লাগে দশ গুণ, আর কোনো index-ই নেই।
শেষ সারিটা খরচের মতোই জরুরি। Vector-এর কোনো element-এর দিকে একটা iterator, pointer বা reference ঠিক থাকে শুধু vector reallocate না হওয়া পর্যন্ত। তারপর সেটা দেখায় free হয়ে যাওয়া পুরোনো block-এর ভিতরে। Reallocation না হলেও মাঝখানে insert বা erase করলে পরের element-গুলো সরে যায়, তাই ওদের iterator-ও আর ঠিক থাকে না। List বা set একবার element রাখলে আর কখনো সেটা সরায় না। তাই vector ঠিক পছন্দ তখন, যখন vector বড় হওয়ার সময় কেউ ওর element ধরে বসে থাকে না।
সিদ্ধান্তের flowchart
একই চারটা ধারণা, এবার flowchart হিসেবে, যে ক্রমে জিজ্ঞেস করবে। উপর থেকে শুরু করো, আর নিজের উত্তর ধরে এগোও। প্রতিটা শেষ ঘর একটা container-এর নাম বলে, সাথে যে module সেটা শেখায়।
Maria-র পথ: প্রশ্ন 1, score-গুলোর জায়গা খুঁজতে ও মান দিয়ে খোঁজে, তাই হ্যাঁ। তীর বলে set, বা দুজন player-এর score সমান হতে পারলে multiset, Module 8। তাই flowchart ওকে একদম প্রথম ঘরেই vector থেকে সরিয়ে দেয়।
বদলটার দাম কত, এবার মেপে দেখো। এই program একই 200,000টা pseudo-random সংখ্যা insert করে একটা sorted vector-এ, প্রতিটা ওর জায়গায়, আর একটা multiset-এ। lower_bound একটা sorted range-এ জায়গাটা খুঁজে দেয়; Module 13 এটা শেখাবে, এখানে এটা শুধু বের করে কোথায় insert করতে হবে।
#include <algorithm>
#include <chrono>
#include <iostream>
#include <set>
#include <vector>
using namespace std;
int main() {
const int n = 200000;
vector<int> input(n);
unsigned x = 12345;
for (int& v : input) {
x = x * 1103515245u + 12345u;
v = (int)(x >> 1);
}
auto t0 = chrono::steady_clock::now();
vector<int> sorted_v;
for (int v : input) {
sorted_v.insert(lower_bound(sorted_v.begin(), sorted_v.end(), v), v);
}
auto t1 = chrono::steady_clock::now();
multiset<int> s;
for (int v : input) {
s.insert(v);
}
auto t2 = chrono::steady_clock::now();
chrono::duration<double, milli> a = t1 - t0, b = t2 - t1;
cout << "sorted vector: " << a.count() << " ms\n";
cout << "multiset: " << b.count() << " ms\n";
cout << (equal(sorted_v.begin(), sorted_v.end(), s.begin()) ? "same order\n" : "different\n");
return 0;
}
sorted vector: 1217.13 ms
multiset: 44.6139 ms
same order
Compiler Explorer-এ একবার চালানো, GCC 12, -O2 -std=c++17। দুটোতেই একই সংখ্যা একই ক্রমে আছে। জায়গা খোঁজা দুটোতেই দ্রুত; vector-এর খরচ হলো প্রতিটা insert-এ গড়ে অর্ধেক element সরানো। তাই এখানে ঠিক container ছিল প্রায় 27 গুণ দ্রুত, code-ও আরও সহজ।
ভরসার default: vector, তারপর মেপে দেখো
Chart দেখে list-কে লোভনীয় মনে হতে পারে: যেকোনো জায়গায় O(1) insert। কিন্তু list প্রতিটা element রাখে নিজের ছোট একটা block-এ, allocator যেখানে জায়গা পায় সেখানে। ওর উপর দিয়ে হাঁটা মানে এক block থেকে আরেক block-এ লাফানো। Vector-এর element-গুলো পাশাপাশি বসে, আর processor memory পড়ে টুকরো টুকরো করে, তাই পরের element-টা সাধারণত আগেই হাতে চলে আসে।
#include <chrono>
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main() {
const int n = 1000000;
vector<int> v;
list<int> l;
for (int i = 0; i < n; i++) {
v.push_back(i % 100);
l.push_back(i % 100);
}
auto t0 = chrono::steady_clock::now();
long long sv = 0;
for (int x : v) sv += x;
auto t1 = chrono::steady_clock::now();
long long sl = 0;
for (int x : l) sl += x;
auto t2 = chrono::steady_clock::now();
chrono::duration<double, milli> dv = t1 - t0, dl = t2 - t1;
cout << "vector: sum " << sv << " in " << dv.count() << " ms\n";
cout << "list: sum " << sl << " in " << dl.count() << " ms\n";
return 0;
}
vector: sum 49500000 in 0.736809 ms
list: sum 49500000 in 4.59888 ms
একই দশ লাখ সংখ্যা, একই loop, Compiler Explorer-এ Playground-এর flag দিয়ে একবার চালানো: list-এর উপর দিয়ে হাঁটতে প্রায় ছয় গুণ বেশি সময় লেগেছে। এই প্রভাবটা, মানে cache, ঠিকমতো মেপে দেখাবে Module 17। এই track যে নিয়ম মানে, সেটা দেয় C++ Core Guidelines। Default হিসেবে vector নাও, আর বদলাও শুধু এমন কারণে, যেটার নাম বলতে পারো বা মেপে দেখাতে পারো।
বড় element: ওদের স্থির রাখো, index সরাও
প্রশ্ন 4 মাপ নিয়ে। একটা int সরালে copy হয় 4 byte। লম্বা বর্ণনাওয়ালা একটা record সরালে copy হয় পুরোটাই। বড় record যদি ঘন ঘন সাজাতে বা বদলাতে হয়, ওদের রাখো এমন একটা vector-এ, যেটা কখনো বদলায় না, আর দ্বিতীয় একটা vector-এ সরাও ছোট ছোট int index।
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<string> books{
"The C Programming Language, a long description...",
"Structure and Interpretation of Computer Programs, a long description...",
"Introduction to Algorithms, a long description...",
};
vector<int> reading_list{2, 0};
reading_list.insert(reading_list.begin(), 1);
for (int id : reading_list) {
cout << id << ": " << books[id].substr(0, 20) << '\n';
}
return 0;
}
1: Structure and Interp
2: Introduction to Algo
0: The C Programming La
সামনে insert সরিয়েছে দুইটা int, দুইটা লম্বা string না। বইগুলো যেখানে ছিল সেখানেই থাকে, তাই books-এর ভিতরের কোনো index কখনো পুরোনো হয়ে যায় না। তাই বড় data এক জায়গায় থাকে, আর ওর ক্রম থাকে index-এর একটা vector-এ।
Function-এ vector পাঠানো
তুমি কীভাবে vector পাঠাচ্ছ, সেটাই বলে দেয় function ওটা দিয়ে কী করতে পারবে। উপায় তিনটা, আর প্রতিটা একটা প্রতিশ্রুতি।
| Parameter | Function যা করতে পারে | Call-এর খরচ |
|---|---|---|
const vector<int>& v | পড়তে পারে, বদলাতে পারে না | O(1): একটা দ্বিতীয় নাম, কোনো copy নেই |
vector<int>& v | caller-এর vector বদলাতে পারে | O(1) |
vector<int> v | নিজের copy বদলাতে পারে; caller কিছুই দেখে না | O(n): প্রতিটা element copy হয় |
#include <iostream>
#include <vector>
using namespace std;
int highest(const vector<int>& marks) {
int best = marks[0];
for (int m : marks) {
if (m > best) {
best = m;
}
}
return best;
}
void add_bonus(vector<int>& marks, int bonus) {
for (int& m : marks) {
m += bonus;
}
}
vector<int> passed(const vector<int>& marks) {
vector<int> result;
for (int m : marks) {
if (m >= 50) {
result.push_back(m);
}
}
return result;
}
int main() {
vector<int> marks{70, 45, 62, 38};
add_bonus(marks, 5);
cout << "highest " << highest(marks) << '\n';
cout << "passed:";
for (int m : passed(marks)) {
cout << ' ' << m;
}
cout << '\n';
return 0;
}
highest 75
passed: 75 50 67
passed-এর মতো value হিসেবে vector return করা সস্তা: compiler ফলাফলটা copy না করে move করে বের করে দেয়, lesson 05 এটা ব্যাখ্যা করবে। তাই পড়তে নাও const& দিয়ে, বদলাতে & দিয়ে, আর নতুন vector return করো value হিসেবে।
In C++20
<span>-এর std::span<const int> হলো একটা view: একটা pointer আর একটা দৈর্ঘ্য, কোনো copy নেই, মালিকানাও নেই। যে function এটা নেয়, সেটা একটা vector, একটা C array, বা এদের যেকোনোটার একটা অংশ নিতে পারে, তাই একটা function দিয়েই সবার কাজ চলে।
#include <iostream>
#include <span>
#include <vector>
using namespace std;
int total(span<const int> values) {
int sum = 0;
for (int x : values) {
sum += x;
}
return sum;
}
int main() {
vector<int> v{3, 1, 4, 1, 5};
int a[3] = {9, 2, 6};
cout << total(v) << ' ' << total(a) << '\n';
cout << total(span<const int>(v).subspan(1, 3)) << '\n';
return 0;
}
14 17
6
subspan(1, 3) হলো index 1 থেকে শুরু হওয়া তিনটা element: 1, 4 আর 1। Span নিজের element-গুলোর মালিক না, তাই যে vector-কে দেখছে, তার চেয়ে বেশি দিন বাঁচা চলবে না। Run button Playground খোলে C++20-এ।
Flowchart Maria-কে পাঠিয়েছিল একটা sorted container-এ। এই হলো multiset দিয়ে ওর leaderboard, Module 8-এর একটা ঝলক। প্রতিটা insert ক্রম মেনে বসে, আর print করার সময় সবচেয়ে কম score থেকে হাঁটে।
#include <iostream>
#include <set>
using namespace std;
int main() {
multiset<int> board;
int scores[] = {310, 120, 455, 120, 280};
for (int s : scores) {
board.insert(s);
}
cout << "board:";
for (int s : board) {
cout << ' ' << s;
}
cout << "\nlowest " << *board.begin() << ", highest " << *board.rbegin() << '\n';
return 0;
}
board: 120 120 280 310 455
lowest 120, highest 455
এখানে কোনো index নেই: board[2] compile-ই হবে না, কারণ set-এর কোনো position নেই। Chart যে দেওয়া-নেওয়াটা দেখায়, এটাই সেটা।
Alice-এর দোকানে customer-দের সেবা দেওয়া হয় লাইনের সামনে থেকে, আর নতুনরা দাঁড়ায় পেছনে। Flowchart-এর প্রশ্ন 3-এর উত্তর "সামনে", তাই deque, Module 4। ওর pop_front হলো O(1), যেখানে vector-এর erase(v.begin()) সবাইকে সরায়।
#include <deque>
#include <iostream>
#include <string>
using namespace std;
int main() {
deque<string> line{"Bob", "Zara"};
line.push_back("Kenji");
while (!line.empty()) {
cout << "serving " << line.front() << ", " << line.size() - 1 << " waiting\n";
line.pop_front();
}
return 0;
}
serving Bob, 2 waiting
serving Zara, 1 waiting
serving Kenji, 0 waiting
এখানে line.size() - 1 নিরাপদ, কারণ loop চলে শুধু লাইন খালি না থাকলে।
Zara প্রতি ঘণ্টায় তাপমাত্রা লিখে রাখে, তারপর জানতে চায় সবচেয়ে ঠান্ডা কত আর গড় কত। Data একটা ক্রম, বাড়ে শেষে, আর ক্রম ধরে হাঁটা হয়: চারটা উত্তরই বলে vector।
#include <iostream>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<int> temps;
int t;
while (cin >> t) {
temps.push_back(t);
}
if (temps.empty()) {
cout << "no readings\n";
return 0;
}
int coldest = temps[0];
long long total = 0;
for (int x : temps) {
if (x < coldest) {
coldest = x;
}
total += x;
}
cout << temps.size() << " readings, coldest " << coldest
<< ", average " << (double)total / temps.size() << '\n';
return 0;
}
6 readings, coldest -3, average 2.5
ওই output-টা input 4 1 -3 0 6 7-এর জন্য। এখানে কিছুই সামনে insert করে না, মান দিয়ে খোঁজেও না, তাই vector-কে কেউ হারাতে পারে না।
এটা কোথায় কাজে লাগে
- C++ Core Guidelines. Rule SL.con.2 বলে, অন্য container নেওয়ার কোনো কারণ না থাকলে default হিসেবে
vectorনাও। কারণ হিসেবে বলে উপরের কথাটাই: vector জায়গা কম নেয়, element পাশাপাশি রাখে, আর ওর উপর দিয়ে হাঁটা দ্রুত। - Bjarne Stroustrup-এর 2012 সালের GoingNative keynote. C++-এর স্রষ্টা দেখিয়েছিলেন, sorted ক্রমে insert করায় vector একটা linked list-কে হারিয়ে দেয়, যদিও list-এর insert
O(1)। জায়গা খুঁজতে list-এর উপর দিয়ে হাঁটতে হয়, আর সেই হাঁটাটাই ধীর। - LLVM-এর Programmer's Manual. Container বাছাইয়ের guide-এ এটা পাঠককে vector-এর মতো container-এর দিকে ঠেলে দেয়, আর বলে
std::listখুব কমই ঠিক পছন্দ।
যে ভুলগুলো সবাই করে
১. Vector বদলাতে চেয়ে value হিসেবে পাঠানো।
void add_bonus(vector<int> marks) {
for (int& m : marks) {
m += 5;
}
}
কোনো command line-এই বার্তা নেই। {70, 85, 62} দিয়ে call-এর পরেও caller print করেছে 70 85 62। Function বদলেছে নিজের copy, আর সেটা বানাতে খরচ করেছে O(n)। লেখো vector<int>& marks। তুমি & ভুলবে, কারণ C-এ array parameter কখনো copy ছিল না।
২. একটা element-এর pointer রেখে দিয়ে vector বড় করা।
vector<int> marks{70, 85, 62};
int* first = &marks[0];
marks.push_back(91);
cout << "first mark: " << *first << '\n';
কোনো command line-এই বার্তা নেই, Playground-এ সফল-ও, কিন্তু output ভুল, আর প্রতিবার আলাদা: দুইবার চালিয়ে print হয়েছে first mark: 1540258676 আর first mark: 1687701485। Capacity ছিল 3, তাই push_back নম্বরগুলো নতুন block-এ সরিয়ে পুরোনোটা free করে দিয়েছে। first তখনো সেই free হওয়া block-এর দিকেই দেখায়। বরং index 0 মনে রাখো, আর দরকার হলে marks[0] পড়ো। তুমি pointer-টা রেখে দেবে, কারণ নেওয়ার সময় তো ওটা ঠিকই ছিল।
৩. list বেছে নিয়ে তারপর index করা।
list<int> scores{70, 85, 62};
cout << scores[1] << '\n';
প্রতিটা command line-এ error: error: no match for 'operator[]' (operand types are 'std::__cxx11::list<int>' and 'int')। Chart-এর "index" সারি যেমন বলে, list-এর কোনো index নেই। Position লাগলে তোমার আসলে দরকার ছিল vector বা deque। তুমি চেষ্টা করবে, কারণ এর আগে যত container দেখেছ, সবগুলোতেই [] ছিল।
৪. Local vector-এর reference return করা।
const vector<int>& make_marks() {
vector<int> marks{70, 85, 62};
return marks;
}
Playground-এও, -Wall ছাড়াই, GCC 12 warning দেয়: warning: reference to local variable 'marks' returned [-Wreturn-local-addr]। তবু চালালে, যে caller m.size() পড়েছে, সেটা শেষ হয়েছে Runtime error badge নিয়ে। Function-এর closing brace-এই vector-টা মরে গেছে। vector<int> value হিসেবে return করো; সেটা move হয়, copy না। তুমি & লিখবে, কারণ parameter-এর বেলায় "copy এড়াও" ভালো পরামর্শ ছিল।
int count_above(const vector<int>& v, int limit) লেখো, যেটা return করে কয়টা element limit-এর চেয়ে বড়। প্রতিটা query-র জন্য এটা call করো।
Input. এক লাইনে n, এক লাইনে n-টা পূর্ণসংখ্যা, এক লাইনে q, তারপর q-টা limit, প্রতি লাইনে একটা।
Output. প্রতিটা limit-এর জন্য এক লাইনে গুনতিটা।
Constraints. 1 <= n, q <= 1000। প্রতিটা মান -1000000 থেকে 1000000-এর মধ্যে।
Sample. Input 5, 4 9 7 1 9, 2, 5, 9 দিলে 3 আর 0।
#include <iostream>
#include <vector>
using namespace std;
// Return how many elements of v are greater than limit.
// The const& promises not to change v, and avoids a copy per call.
int count_above(const vector<int>& v, int limit) {
return 0;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> v(n);
for (int& x : v) {
cin >> x;
}
int q;
cin >> q;
for (int i = 0; i < q; i++) {
int limit;
cin >> limit;
cout << count_above(v, limit) << '\n';
}
return 0;
}
আলাদা করে গ্রেড হয় না। Parameter-টা vector<int> v করেও চালিয়ে দেখো: উত্তর একই থাকে, কিন্তু প্রতিটা call পুরো vector copy করে।
David তিনটা শব্দে একেকটা workload বর্ণনা করে, আর তুমি উত্তর দাও flowchart-এর container দিয়ে। শব্দগুলো: কীভাবে পড়া হয় (position বা key), size (fixed বা grows), আর কোথায় বদলায় (end, front বা middle)।
Input. এক লাইনে q, তারপর q-টা লাইন, প্রতিটায় তিনটা শব্দ।
Output. প্রতিটা লাইনের জন্য একটা শব্দ: key দিয়ে পড়া হলে set, নইলে size fixed হলে array, নইলে front হলে deque, middle হলে list, end হলে vector।
Constraints. 1 <= q <= 100। শব্দগুলো ঠিক তালিকার মতোই।
Sample. Input 3, key grows middle, position grows end, position grows front দিলে set, vector আর deque।
#include <iostream>
#include <string>
using namespace std;
int main() {
int q;
cin >> q;
for (int i = 0; i < q; i++) {
string read, size, change;
cin >> read >> size >> change;
// Ask the flowchart's questions in its order, and print one word.
}
return 0;
}
আলাদা করে গ্রেড হয় না। আসল কথা প্রশ্নগুলোর ক্রম: key fixed front তবুও set।
Maria-র খরচটা নিজে টের পাও। Input শেষ না হওয়া পর্যন্ত সংখ্যা পড়ো, আর প্রতিটাকে ওর জায়গায় insert করে একটা vector sorted রাখো। প্রতিটা সংখ্যা কোন index-এ বসল print করো, তারপর শেষ তালিকাটা।
Input. Input শেষ হওয়া পর্যন্ত পূর্ণসংখ্যা।
Output. এক লাইনে প্রতিটা সংখ্যা যে index-এ বসেছে, input-এর ক্রমে, তারপর এক লাইনে sorted তালিকা। সমান সংখ্যা বসে আগে থেকে থাকা সমানগুলোর আগে।
Constraints. 1 থেকে 100000টা সংখ্যা, প্রতিটা -1000000000 থেকে 1000000000-এর মধ্যে।
Sample. Input 5 2 8 2 দিলে 0 0 2 0 আর 2 2 5 8।
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<int> sorted_v;
vector<int> landed;
int x;
while (cin >> x) {
// lower_bound(sorted_v.begin(), sorted_v.end(), x) is the first place
// whose value is not less than x. Its distance from begin() is the index.
// Insert x there, and remember the index.
}
// Print the indexes on one line, then the sorted list on the next.
return 0;
}
আলাদা করে গ্রেড হয় না। তারপর Playground-এ 100,000টা random input দিয়ে সময় মেপে দেখো: প্রতিটা insert vector-এর প্রায় অর্ধেক সরায়, এই lesson ঠিক এই খরচটাই মেপেছে।
Run in Compilerসচরাচর যে প্রশ্নগুলো আসে
List যদি
O(1)-এ insert করে, তাহলে ওটা এত কম বাছা হয় কেন?কারণ ওই
O(1)-এর জন্য জায়গাটায় আগে থেকেই একটা iterator লাগে। জায়গা খোঁজা মানে হাঁটা,O(n), আর সেটাও ধীর হাঁটা, উপরে মাপা vector-এর চেয়ে ছয় গুণ ধীর। List জেতে তখন, যখন position আগেই তোমার হাতে, Module 5 যেমন দেখাবে।arrayকি শুধু একটা দুর্বল vector?না।
std::array<int, 12>-এর size compile-এর সময়েই ঠিক, আর heap-এ কোনো block-ই নেই। বারো মাস বা ছক্কার ছয় দিকের জন্য এটাই একদম ঠিক। Module 4 এটা শেখাবে।Sorted vector কি কখনো set-কে হারাতে পারে?
পারে, যখন তুমি ওটা একবার ভরো, একবার sort করো, তারপর শুধু খোঁজো। Binary search দিয়ে sorted vector-এ খোঁজাও
O(log n), আর এটা জায়গাও কম নেয়। Maria-র সমস্যা ছিল বারবার ওটার ভিতরে insert করা।Function থেকে vector return করলে কি copy হয়?
না। C++11 থেকে ফলাফলটা move হয়, মানে
O(1)-এ block-টা হাতবদল হয়, আর অনেক ক্ষেত্রে compiler ওটা সরাসরি জায়গাতেই বানায়। দুটোর নামই বলবে lesson 05।
মূল কথা
- চারটা প্রশ্ন করো: ক্রম নাকি key, position নাকি খোঁজা, কোথায় বাড়ে, একটা element কত বড়।
- Vector জেতে শেষে, index-এ আর memory-তে; হারে সামনে, মাঝখানে, আর মান দিয়ে খোঁজায়।
- Reallocation হলে vector-এর ভিতরের প্রতিটা pointer, reference আর iterator বাতিল হয়ে যায়; list বা set কখনো element সরায় না।
- সন্দেহ হলে vector নাও আর মেপে দেখো: list-এর উপর হাঁটা ছয় গুণ ধীর ছিল, আর set ক্রম মেনে insert করেছে 27 গুণ দ্রুত।
- পড়তে
const&, বদলাতে&, return করো value হিসেবে, আর C++20-এ যেকোনো পাশাপাশি সাজানো range নিতে একটাspanনাও। - আরও গভীরে যেতে চাইলে: Under the Hood, vector কীভাবে বড় হয় আর তাতে কী কী ভাঙে (Pro)।
এরপর Pro lesson 05 vector-টা খুলে দেখাবে: আমাদের compiler-এ capacity কীভাবে বাড়ে, আর একটা reallocation কী কী ভাঙে।
lesson ৪ শেষ
শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।
পরেরটা: ভেতরের কথা: vector বাড়ে কীভাবে, আর তাতে কী ভাঙে