Learn C++ STL

lesson ১ / ৯ · array আর deque: fixed size, আর দুই মাথায় বাড়া

Module ৪ · array আর deque: fixed size, আর দুই মাথায় বাড়া

array আর deque: যে size তুমি ঠিক করে দাও, আর যে লাইন দুই মাথাতেই বাড়ে

Freeপড়া

এই lesson-এ যা শিখবে

  • Code-এই size ঠিক করে দেওয়া একটা std::array declare করতে পারবে, index দিয়ে আর range-for দিয়ে পড়তে পারবে, = দিয়ে copy আর == দিয়ে তুলনা করতে পারবে।
  • একটা std::array C array থেকে কোথায় আলাদা, বুঝিয়ে বলতে পারবে: ও নিজের size জানে, copy হয়, আর pointer হয়ে যায় না।
  • একটা deque declare করে দুই মাথাতেই push আর pop করতে পারবে, index দিয়ে পড়তে পারবে, আর বলতে পারবে কিছু না সরিয়েই ও সামনের দিকে কীভাবে বাড়ে।

Kenji-র step counter প্রতিদিনের জন্য একটা করে সংখ্যা দেখায়, সোমবার থেকে রবিবার। দিন ঠিক সাতটা, আর program লেখার সময়েই ও সেটা জানে। Alice-এর সমস্যা আবার অন্যরকম। ওর দোকানের লাইনে customer পেছনে এসে দাঁড়ায়, সামনে থেকে সার্ভিস পায়, আর কোনো VIP এলে সোজা সামনে চলে যায়। এই lesson দুজনকেই মাপমতো একটা করে container দেবে: সপ্তাহের জন্য std::array, আর লাইনের জন্য std::deque।

দুইটা সমস্যা, যেখানে vector ঠিক খাপ খায় না

Kenji প্রথমে সপ্তাহটা রাখে একটা C array-তে, C track যেমন শিখিয়েছিল। তারপর ওটা একটা function-এ পাঠায়, আর array-টা নিজের size-ই ভুলে যায়।

#include <iostream>
using namespace std;

void show(int steps[]) {
    cout << "inside show: " << sizeof(steps) << " bytes\n";
}

int main() {
    int steps[7] = {3000, 4500, 12000, 8000, 12000, 2000, 6000};
    cout << "in main: " << sizeof(steps) << " bytes\n";
    show(steps);
    return 0;
}
in main: 28 bytes
inside show: 8 bytes

main-এ 4 byte করে সাতটা int মানে 28 byte। কিন্তু show-এর ভিতরে parameter-টা শুধু প্রথম বাক্সের একটা pointer, আর Playground-এ একটা pointer 8 byte। -Wall ছাড়াও GCC 12 এটা নিয়ে warning দেয়: warning: 'sizeof' on array function parameter 'steps' will return size of 'int*' [-Wsizeof-array-argument]। তাই function-এ পাঠালে C array নিজের size ফেলে রেখে যায়, আর C track-এ এর সমাধান ছিল গুনতির জন্য আলাদা একটা parameter।

Alice-এর লাইন একটা vector-এ রাখা যেত। পেছনে দাঁড়ানো মানে push_back, ওটা সস্তা। কিন্তু সামনে থেকে সার্ভিস দেওয়া মানে v.begin() erase করা, আর VIP মানে ঠিক ওখানেই insert। দুই ক্ষেত্রেই বাকি সব customer এক বাক্স করে সরে। Module 2-এর lesson 02-এ vector-এর সামনে 100,000টা insert মেপে পাওয়া গিয়েছিল প্রায় এক সেকেন্ডের তিন ভাগের এক ভাগ। তাই সপ্তাহের দরকার এমন একটা fixed size, যেটা type নিজেই মনে রাখে, আর লাইনের দরকার দুইটা সস্তা মাথা।

ছবিটা: deque মানে কয়েকটা block আর একটা map

Deque (উচ্চারণ "ডেক", double-ended queue-এর ছোট রূপ) এমন একটা sequence, যেটা দুই মাথাতেই বাড়ে। Vector-এর মতো ও একটা লম্বা block রাখে না। ও রাখে একই মাপের কয়েকটা ছোট block। আর pointer-এর একটা ছোট তালিকা, যার নাম map, বলে দেয় কোন block প্রথমে, কোনটা দ্বিতীয়, কোনটা তৃতীয়।

শেষের block ভরে গেলে push_back ওটার পরে নতুন একটা block জুড়ে দেয়। প্রথম block-এর সামনে জায়গা না থাকলে push_front ওটার আগে নতুন একটা block জুড়ে দেয়। দুই ক্ষেত্রেই কোনো element নড়ে না, map-এ শুধু একটা pointer বাড়ে। আর কোনো pop একটা block খালি করে দিলে deque ওই block-টা free করে দেয়।

একটা deque: block pointer-এর একটা map, আর চারটা করে element-এর তিনটা block push_back(10) থেকে push_back(50), তারপর push_front(5) আর push_front(1)-এর পরে deque<int> d map 8টা pointer slot 1 5 10 20 30 40 50 সামনে পেছনে push_front ভরে 1-এর আগের ড্যাশ দেওয়া slot-গুলো; push_back ভরে 50-এর পরেরগুলো। শেষের কোনো block ভরে গেলে নতুন block আসে, আর map-এর আরেকটা slot ওটাকে দেখায়। এখানে প্রতি block-এ 4টা করে আঁকা। GCC 12-এ একটা block 512 byte, তাই ওতে ধরে 128টা int।

এবার নড়াচড়াটা দেখো। নিচের প্রতিটা ধাপ একই deque-এর উপর একটা করে call। খেয়াল করো কখন পেছনে একটা block এসে জোড়ে, কখন সামনে একটা block খোলে, আর কোন দুইটা pop একটা করে block free করে দেয়।

একটা deque<int>-এর উপর এগারোটা ধাপ, সহজ model-এ প্রতি block-এ 4টা element ধরে আঁকা। GCC 12-এ আসলে প্রতি block-এ 128টা int ধরে, আর ঠিক কখন কী হয়, সেটা দেখাবে lesson 05। বিন্দু মানে খালি slot।

Callএরপর element-গুলো, সামনে থেকে পেছনেMap-এর ক্রমে block-গুলোBlock এল, না গেল
deque<int> d(খালি)(একটাও না)কিছুই না
push_back(10)10[10 . . .]প্রথম block এল
push_back(20)10 20[10 20 . .]কিছুই না
push_back(30)10 20 30[10 20 30 .]কিছুই না
push_back(40)10 20 30 40[10 20 30 40]কিছুই না
push_back(50)10 20 30 40 50[10 20 30 40] [50 . . .]পেছনে নতুন block এল
push_front(5)5 10 20 30 40 50[. . . 5] [10 20 30 40] [50 . . .]সামনে নতুন block এল
push_front(1)1 5 10 20 30 40 50[. . 1 5] [10 20 30 40] [50 . . .]কিছুই না
pop_back()1 5 10 20 30 40[. . 1 5] [10 20 30 40]পেছনের খালি block free হলো
pop_front()5 10 20 30 40[. . . 5] [10 20 30 40]কিছুই না
pop_front()10 20 30 40[10 20 30 40]সামনের খালি block free হলো

এই ছবিতে প্রতি block-এ 4টা element, যাতে এক block থেকে আরেক block-এ যাওয়াটা চোখে পড়ে। GCC 12-এ আসল block 512 byte, তাতে ধরে 128টা int, আর ওর library প্রথম push-এর আগেই একটা block বানিয়ে রাখে। এসবই মেপে দেখাবে lesson 05। তাই deque সামনের দিকে বাড়ে সামনে একটা block জুড়ে দিয়ে, আর যা আগে থেকে রাখা আছে, তার কিছুই সরাতে হয় না।

প্রথম দিনেই যে syntax লাগবে

std::array আর std::deque, আর ওদের রোজকার call-গুলো

#include <array>
#include <deque>

array<T, N> a{...};          N elements of T; N is fixed in the code
a[i]                          the element at index i, 0 to N - 1
a.size()                      N, always
a.at(i)                       like a[i], but checks i first
a == b                        true when every element is equal

deque<T> d;                   an empty deque of T
d.push_back(x);               add x at the back
d.push_front(x);              add x at the front
d.pop_back();                 remove the back element
d.pop_front();                remove the front element
d.front()   d.back()          the first and the last element
d[i]                          the element at index i, 0 to d.size() - 1
d.size()    d.empty()         how many, and whether there are none
  • array<T, N>: angle bracket-এ দুইটা জিনিস, element-এর type আর গুনতি। Program compile হওয়ার সময়েই গুনতিটা জানা থাকতে হবে।
  • a.at(i): i সীমার বাইরে গেলে একটা exception দিয়ে program থামিয়ে দেয়, যেখানে a[i] কিছুই check করে না।
  • push_front আর pop_front: এই দুইটা call vector-এর নেই। খরচ ওদের _back জমজ ভাইদের মতোই সামান্য।
  • front(), back() আর pop-গুলো: শুধু খালি না, এমন deque-এ। আগে empty() check করো।

তাই array declare হয় একটা type আর একটা গুনতি দিয়ে, আর deque-এর দুই মাথাতেই একটা করে সস্তা push আর pop আছে।

std::array: যে C array নিজের size জানে

একটা std::array ওর element-গুলো ঠিক C array-র মতোই একই বাক্সে রাখে। তফাতটা type-এ: গুনতিটা type-এরই অংশ। তাই যে function const array<int, 7>& নেয়, সে পুরো সপ্তাহটাই হাতে পায়, আর size() তখনো বলে 7।

#include <array>
#include <iostream>
using namespace std;

int goal_days(const array<int, 7>& steps) {
    int count = 0;
    for (int s : steps) {
        if (s >= 10000) {
            count++;
        }
    }
    return count;
}

int main() {
    array<int, 7> steps{3000, 4500, 12000, 8000, 12000, 2000, 6000};
    cout << "days: " << steps.size() << '\n';
    cout << "Wednesday: " << steps[2] << '\n';
    cout << "days at 10000 or more: " << goal_days(steps) << '\n';
    cout << "sizeof: " << sizeof(steps) << " bytes\n";
    return 0;
}
days: 7
Wednesday: 12000
days at 10000 or more: 2
sizeof: 28 bytes

goal_days-এর ভিতরের range-for কাজ করে, কারণ function তখনো জানে সপ্তাহটা কোথায় শেষ। আর sizeof হলো 28, C array-র সমান: element-গুলোর পাশে লুকানো কোনো গুনতি রাখা নেই। 7 থাকে type-এ, আর type compiler জানে, তাই program চলার সময় এর জন্য কোনো memory খরচ হয় না।

একই সাতটা মানের একটা C array আর একটা std::array int steps[7] array<int, 7> steps main-এ sizeof হলো 28 byte main-এ sizeof হলো 28 byte 7 হলো type-এরই অংশ show(int steps[]) পৌঁছায় শুধু বাক্স 0-এর pointer sizeof হলো 8: গুনতি হারিয়ে গেল goal_days(const array<int, 7>& steps) পুরো array-টাই পৌঁছায় steps.size() হলো 7 দুটোতেই memory একই 28 byte। আলাদা শুধু type।

তাই একটা std::array memory-তে C array-র মতোই, শুধু গুনতিটা ওর type-এ লেখা থাকে।

Array copy হয়, তুলনাও হয়

C array = দিয়ে copy করা যায় না। b = a;-এ এসে GCC 12 থেমে যায় error: invalid array assignment দিয়ে, আর দুইটা C array-তে == তুলনা করে ওদের address, বাক্সগুলো না। এখানে std::array চলে একটা int-এর মতো: = প্রতিটা element copy করে, আর == প্রতিটা element মিলিয়ে দেখে।

#include <array>
#include <iostream>
using namespace std;

int main() {
    array<int, 3> monday{5, 7, 9};
    array<int, 3> copy = monday;
    copy[0] = 100;
    cout << monday[0] << ' ' << copy[0] << '\n';
    cout << (monday == copy) << '\n';
    copy[0] = 5;
    cout << (monday == copy) << '\n';
    return 0;
}
5 100
0
1

copy বদলানোয় monday-এ হাতই পড়েনি, তাই copy-টা সত্যিকারের। একটা element আলাদা থাকা পর্যন্ত তুলনা print করেছে 0 (মিথ্যা), আর তিনটাই মিলে যাওয়ার পরে 1 (সত্যি)। একে বলে value semantics: পুরো array-টা একটা সংখ্যার মতোই একটা মান। তাই শুধু পড়ার জন্য function-এ পাঠালে const& দিয়ে পাঠাও, নইলে আস্ত একটা copy তৈরি হবে।

Size হলো type-এর অংশ

array<int, 7> আর array<int, 8> দুইটা আলাদা type, ঠিক যেমন int আর double আলাদা। সপ্তাহের জন্য লেখা function আট দিন নেবে না। Bob উপরের goal_days দিয়ে চেষ্টা করে দেখে।

array<int, 8> eight{};
cout << goal_days(eight) << '\n';

GCC 12 রাজি হয় না: error: invalid initialization of reference of type 'const std::array<int, 7>&' from expression of type 'std::array<int, 8>'। এটা আসলে একটা উপহার। C-এর function যেকোনো int* নিয়ে নিত, আর শেষের পরেও পড়তে থাকত। তাই গুনতিটা compiler তোমার হয়ে check করে, আর সেজন্যই program compile হওয়ার সময়েই গুনতিটা জানা থাকতে হয়।

deque: যে লাইনের দুই মাথাই খোলা

Vector-এর খোলা মাথা একটা। Deque-এর দুইটাই খোলা। এই program widget-এর এগারোটা call আবার চালায়, আর কয়েকটা ধাপ পরপর লাইনটা print করে। Block না, মানগুলোর দিকে চোখ রাখো: deque ওর block তোমার কাছ থেকে লুকিয়ে রাখে।

#include <deque>
#include <iostream>
#include <string>
using namespace std;

void show(const string& call, const deque<int>& d) {
    cout << call << ":";
    for (int x : d) {
        cout << ' ' << x;
    }
    cout << '\n';
}

int main() {
    deque<int> d;
    for (int x = 10; x <= 50; x += 10) {
        d.push_back(x);
    }
    show("five push_back", d);
    d.push_front(5);
    d.push_front(1);
    show("two push_front", d);
    d.pop_back();
    show("pop_back", d);
    d.pop_front();
    d.pop_front();
    show("two pop_front", d);
    return 0;
}
five push_back: 10 20 30 40 50
two push_front: 1 5 10 20 30 40 50
pop_back: 1 5 10 20 30 40
two pop_front: 10 20 30 40

দ্বিতীয় push_front 1-কে বসিয়েছে 5-এর আগে, তাই সামনে সবসময় থাকে সবচেয়ে নতুন push_front। Vector-এর pop_back-এর মতোই দুই pop-ই শুধু সরায়, কিছু return করে না। তাই deque হলো এমন একটা vector, যার সামনে আরেকটা দরজা আছে।

Index দিয়ে পড়া এখনো চলে

Deque-এ d[i], d.at(i) আর d.size() সবই আছে, গোনা হয় সামনে থেকে, 0 থেকে d.size() - 1 পর্যন্ত। একটা push_front সবার নম্বর বদলে দেয়: পুরোনো d[0] হয়ে যায় d[1]।

#include <deque>
#include <iostream>
using namespace std;

int main() {
    deque<int> d{20, 30, 40};
    cout << "d[0] = " << d[0] << ", size " << d.size() << '\n';
    d.push_front(10);
    cout << "d[0] = " << d[0] << ", d[1] = " << d[1] << ", size " << d.size() << '\n';
    for (size_t i = 0; i < d.size(); i++) {
        d[i] *= 2;
    }
    cout << "last: " << d[d.size() - 1] << '\n';
    return 0;
}
d[0] = 20, size 3
d[0] = 10, d[1] = 20, size 4
last: 80

d[i]-এর পেছনে deque হিসাব করে বের করে, element i কোন block-এ আছে, আর সেই block-এর কোন slot-এ। তাই একটা read-এ দুই ধাপ লাগে, যেখানে vector-এর লাগে এক ধাপ। তবুও size যা-ই হোক, কাজটা একটা নির্দিষ্ট পরিমাণই। তাই vector-এর মতোই deque-এ index দিয়ে পড়ো, শুধু মনে রেখো সামনেটা সরে যায়।

খালি deque: Zara-র প্রথম test

Zara সবসময় খালি case আগে চালায়। ও একটা খালি লাইনকে জিজ্ঞেস করে, সামনে কে আছে।

deque<int> line;
cout << "first in line: " << line.front() << '\n';

Runner-এর flag-এ এটা কোনো বার্তা ছাড়াই compile হয়েছে। Compiler Explorer-এর GCC 12.2-এ একবার চালাতে print করেছে first in line: 0, আর স্বাভাবিকভাবেই শেষ হয়েছে। ওই 0 কোনো customer না। খালি deque-এ front() হলো undefined behaviour: কী হবে, standard কিছুই বলে না, তাই অন্য দিন অন্য সংখ্যা আসতে পারে, crash-ও হতে পারে। চুপচাপ একটা ভুল 0 সবচেয়ে খারাপ উত্তর। তাই front(), back() বা কোনো pop-এর আগে if (!line.empty()) check করো।

Example 1: সবচেয়ে ছোট std::array program

তিনটা পদক, চিরকালের জন্য ঠিক করা, index দিয়ে পড়া আর size() দিয়ে গোনা।

#include <array>
#include <iostream>
#include <string>
using namespace std;

int main() {
    array<string, 3> medals{"gold", "silver", "bronze"};
    for (size_t i = 0; i < medals.size(); i++) {
        cout << i + 1 << ": " << medals[i] << '\n';
    }
    return 0;
}
1: gold
2: silver
3: bronze

Loop নিজেই array-কে ওর size জিজ্ঞেস করে, তাই চারটা পদক করতে চাইলে বদলাতে হবে শুধু 3 আর braces-এর ভিতরটা।

Run in Compiler
Example 2: সবচেয়ে ছোট deque program

একটা তালিকা থেকে বানানো deque, দুই মাথায় একটা করে push, তারপর ওর দুই মাথা আর size।

#include <deque>
#include <iostream>
using namespace std;

int main() {
    deque<int> d{20, 30};
    d.push_front(10);
    d.push_back(40);
    cout << "front " << d.front() << ", back " << d.back() << ", size " << d.size() << '\n';
    return 0;
}
front 10, back 40, size 4

এখানে braces দেয় তালিকা, Module 2-এ vector-এর বেলায় যেমন দিয়েছিল।

Run in Compiler
Example 3: Alice-এর দোকানের লাইন, সাথে একজন VIP

Ticket 101 আর 102 পেছনে এসে দাঁড়ায়। Ticket 7 একজন VIP, তাই চলে যায় সামনে। Alice চারবার সার্ভিস দেয়, আর চতুর্থবার মুখ খোলে Zara-র খালি check।

#include <deque>
#include <iostream>
using namespace std;

int main() {
    deque<int> line;
    line.push_back(101);
    line.push_back(102);
    line.push_front(7);

    for (int turn = 1; turn <= 4; turn++) {
        if (line.empty()) {
            cout << "turn " << turn << ": nobody waiting\n";
        } else {
            cout << "turn " << turn << ": serving ticket " << line.front() << '\n';
            line.pop_front();
        }
    }
    return 0;
}
turn 1: serving ticket 7
turn 2: serving ticket 101
turn 3: serving ticket 102
turn 4: nobody waiting

এখানে প্রতিটা call শুধু একটা মাথা ছোঁয়, তাই কোনো customer-কে কখনো সরতে হয় না। Vector হলে প্রতিবার সার্ভিসের সময় পুরো লাইনটা এক বাক্স বাঁয়ে সরত।

Run in Compiler

এটা কোথায় কাজে লাগে

  • C++ Core Guidelines. নিয়ম SL.con.1 বলে, C array-র বদলে std::array বা std::vector নাও। কারণটা এই lesson-এর প্রথম অংশটাই: C array নিজের size হারায়, copy-ও হয় না।
  • CPython-এর collections.deque. Python-এর deque C-তে লেখা, 64 slot-এর fixed-length block-এর একটা doubly linked list হিসেবে। যেকোনো মাথায় push হলে হয় একটা slot ভরে, নয়তো নতুন একটা block জোড়া লাগে, তাই অন্য element কখনো সরে না: উপরের ছবির ঠিক একই ধারণা।
  • Chromium. এই browser-এর নিজের container-গুলোর guide std::deque-এর বদলে ওদের নিজেদের base::circular_deque নিতে বলে। কেন, সেটা বলবে lesson 04।

যে ভুলগুলো সবাই করে

১. Program চলার সময় পড়া একটা size।

int n;
cin >> n;
array<int, n> a{};

প্রতিটা command line-এ error, Playground-সহ: error: the value of 'n' is not usable in a constant expression, সাথে note: 'int n' is not const। std::array-এর গুনতি ওর type-এর অংশ, তাই program compile হওয়ার সময়েই সেটা জানা থাকতে হবে। Size যখন input থেকে আসে, তখন vector নাও। তুমি এটা চেষ্টা করবে, কারণ vector তো গোল bracket-এ n দিব্যি নিয়েছিল।

২. #include <array> লিখতে ভুলে যাওয়া।

#include <iostream>
using namespace std;

int main() {
    array<int, 7> steps{};
    cout << steps.size() << '\n';
    return 0;
}

প্রতিটা command line-এ error: error: 'array' was not declared in this scope, তারপর GCC 12 নিজেই সমাধান বলে দেয়: note: 'std::array' is defined in header '<array>'; did you forget to '#include <array>'?। Deque-এর জন্যও একইভাবে <deque> লাগে। তুমি এটা ভুলবে, কারণ array শব্দটা শুনলে মনে হয় ভাষার ভিতরেই আছে।

৩. দুই size-এর array একটা আরেকটায় assign করা।

array<int, 7> week{};
array<int, 8> eight{};
week = eight;

প্রতিটা command line-এ error: error: no match for 'operator=' (operand types are 'std::array<int, 7>' and 'std::array<int, 8>'), তারপর দুইটা candidate। একই element type আর একই গুনতির array ছাড়া একটা আরেকটায় copy হয় না। যে element-গুলো লাগবে, একটা loop দিয়ে copy করো। তুমি এটা চেষ্টা করবে, কারণ দুটোকেই দেখে মনে হয় "int-এর একটা array"।

৪. খালি হতে পারে এমন লাইনের সামনেটা পড়া।

deque<int> line;
line.pop_front();
cout << line.front() << '\n';

কোনো command line-এই কোনো বার্তা নেই। খালি deque-এ দুইটা call-ই undefined, আর Zara-র test-এর অংশে দেখেছ, একবার চালাতে চুপচাপ একটা 0 print হয়েছিল। খালি pop_front size()-এর কী দশা করে, সেটা দেখাবে lesson 02। আগে if (!line.empty()) লেখো। তুমি এটা বাদ দেবে, কারণ sample input-এ লাইনে সবসময় কেউ না কেউ থাকে।

মাথা খাটাও

Maria জানতে চায়, একটা function-এ আসলে কী পৌঁছায়। ও দুইটা function লেখে, যেগুলো নিজের parameter-এর sizeof print করে।

#include <array>
#include <iostream>
using namespace std;

void c_week(int steps[7]) {
    cout << sizeof(steps) << '\n';
}

void std_week(const array<int, 7>& steps) {
    cout << sizeof(steps) << '\n';
}

int main() {
    int a[7] = {1, 2, 3, 4, 5, 6, 7};
    array<int, 7> b{1, 2, 3, 4, 5, 6, 7};
    c_week(a);
    std_week(b);
    return 0;
}

প্রথম function তো bracket-এর ভিতরে 7-ও লিখে দিয়েছে। Playground-এ দুই লাইনে কী print হয়, আর int steps[7]-এর ওই 7 কেন কোনো কাজে আসে না?

sizeof প্রশ্ন করে একটা type নিয়ে। Function-এর প্রথম লাইনটা পড়ার পরে compiler-এর চোখে প্রতিটা parameter-এর আসল type কী?

অনুশীলন ১সহজ

Kenji-র step counter ওকে সপ্তাহে সাতটা সংখ্যা দেয়, সোমবার আগে। প্রতি সপ্তাহের জন্য ও চায় মোট step আর ওর সবচেয়ে ভালো দিন।

Input. এক লাইনে w, তারপর w-টা লাইন, প্রতিটায় 7টা পূর্ণসংখ্যা: সোমবার থেকে রবিবার পর্যন্ত step।

Output. প্রতি সপ্তাহে এক লাইন: সপ্তাহের মোট, একটা space, আর সবচেয়ে বেশি step-এর দিনটা, Mon, Tue, Wed, Thu, Fri, Sat বা Sun হিসেবে। সমান হলে সবচেয়ে আগের দিনটা print করো।

Constraints. 1 <= w <= 10000। 0 <= steps <= 100000।

Sample. Input 2, 3000 4500 12000 8000 12000 2000 6000 আর 0 0 0 0 0 0 0 দিলে 47500 Wed আর 0 Mon। বুধ আর শুক্র দুটোই 12000, আর বুধবার আগে আসে।

#include <array>
#include <iostream>
#include <string>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    const array<string, 7> days{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};

    int w = 0;
    cin >> w;
    for (int week = 0; week < w; week++) {
        array<int, 7> steps{};
        for (int d = 0; d < 7; d++) {
            cin >> steps[d];
        }

        // Print this week's total, one space, and the name of the day
        // with the most steps (the earliest such day on a tie).
    }
    return 0;
}

weekly-steps নামে গ্রেড হয়, এই module-এর problem set-এর একটা free problem। Hidden test-এ আছে একটামাত্র সপ্তাহ, 10000টা সপ্তাহ, আর হাজার হাজার সপ্তাহ, যেখানে দুই বা তার বেশি দিন সমান। সেগুলো ধরে ফেলে এমন >=, যেটা সমান দিনগুলোর শেষটা বেছে নেয়, আর ভুল জায়গায় রাখা এমন মোট, যেটা এক সপ্তাহ থেকে পরের সপ্তাহে গড়িয়ে যায়।

Run in Compiler
অনুশীলন ২মাঝারি

David parcel-গুলো এক সারিতে সাজায়। বিজোড় নম্বরের parcel যায় সারির সামনে, জোড় নম্বরেরটা যায় পেছনে, যে ক্রমে আসে সেই ক্রমেই।

Input. এক লাইনে n, তারপর n-টা পূর্ণসংখ্যা।

Output. সারিটা সামনে থেকে পেছনে, এক লাইনে, মাঝে একটা করে space।

Constraints. 1 <= n <= 100000। প্রতিটা পূর্ণসংখ্যা 1 থেকে 1000000-এর মধ্যে।

Sample. Input 6 আর 1 2 3 4 5 6 দিলে 5 3 1 2 4 6। প্রতিটা বিজোড় parcel ওর আগের বিজোড়গুলোর সামনে গিয়ে বসে।

#include <deque>
#include <iostream>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    deque<int> row;
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        // Odd x goes to the front of row, even x to the back.
    }

    // Print row from front to back, separated by single spaces.

    return 0;
}

আলাদা করে গ্রেড হয় না। Vector হলে প্রতিটা বিজোড় parcel পুরো সারিটা সরাত: 100000টা বিজোড় parcel-এ O(n2)। Deque-এ প্রতিটার খরচ একই ছোট্ট এক ধাপ।

Run in Compiler
অনুশীলন ৩কঠিন

Kenji চায় পরপর k দিনের ওর সবচেয়ে ভালো streak। ওর সপ্তাহ ঘুরে ঘুরে আসে, তাই একটা streak রবিবার পেরিয়ে সোমবারে গড়াতে পারে।

Input. এক লাইনে k, তারপর 7টা পূর্ণসংখ্যা: সোমবার থেকে রবিবার পর্যন্ত step।

Output. পরপর k দিনের সবচেয়ে বড় মোট, একটা space, আর streak যে দিনে শুরু, সেই দিনের নাম। সমান হলে সোমবার থেকে গুনে সবচেয়ে আগের শুরুর দিনটা print করো।

Constraints. 1 <= k <= 7। 0 <= steps <= 100000।

Sample. Input 3 আর 9000 1000 2000 3000 4000 8000 7000 দিলে 24000 Sat: শনিবার, রবিবার, আর পরের সোমবার।

#include <array>
#include <iostream>
#include <string>
using namespace std;

int main() {
    const array<string, 7> days{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
    int k;
    cin >> k;
    array<int, 7> steps{};
    for (int& s : steps) {
        cin >> s;
    }

    // For each start day, add k days in a row, wrapping after Sunday.
    // Keep the best total and its start day (the earliest on a tie).

    return 0;
}

আলাদা করে গ্রেড হয় না। Lesson যে কথাটা শুধু ইঙ্গিতে বলেছে: index 6-এর পরের দিন হলো index 0, আর (start + j) % 7 array ছেড়ে না বেরিয়েই পুরো সপ্তাহ ঘুরে আসে।

Run in Compiler

সচরাচর যে প্রশ্নগুলো আসে

  • std::array কি C array-র চেয়ে ধীর?

    না। একই memory-তে একই বাক্স, আর sizeof দুটোর জন্যই print করেছে 28। a[i] compile হয়ে একই read হয়। বাড়তি খরচ শুধু at()-এর সীমা check, তাও শুধু যখন at() call করো।

  • Fixed size-এর সবকিছুর জন্য vector নিলেই তো হয়, তাই না?

    নিতে পারো, চলবেও। কিন্তু std::array-এর heap memory লাগে না, বড় হওয়ারও ঝামেলা নেই, আর ওর type নিজেই গুনতিটা বলে দেয়। Size যখন সত্যিই code-এ বাঁধা, যেমন সাতটা দিন বা 3 বাই 3 একটা board, তখন array সেটা পরিষ্কার করে বলে।

  • এর নাম deque কেন?

    এটা "double-ended queue"-এর ছোট রূপ, উচ্চারণ "ডেক", তাসের ডেকের মতো। Queue এক মাথায় নেয় আর অন্য মাথায় দেয়; deque দুই মাথাতেই নেয়, দুই মাথাতেই দেয়।

  • Deque এত সুবিধার হলে vector-এর বদলে সবসময় deque নিলেই কি ভালো?

    এমনিতে না। d[i] পড়তে block খুঁজতে বাড়তি এক ধাপ লাগে, আর element-গুলো memory-র একটা block-এ থাকে না। সামনেটা যখন নড়াচড়া করে, তখনই deque নাও; সংখ্যা দিয়ে এই বাছাইটা করবে lesson 04।

মূল কথা

  • array<T, N> হলো এমন একটা C array, যার গুনতি ওর type-এ: memory একই, কিন্তু function-এর ভিতরেও ও নিজের size() জানে।
  • Array = দিয়ে copy হয়, == দিয়ে তুলনা হয়; C array-র এর কোনোটাই হয় না, আর GCC 12 বলে invalid array assignment।
  • Program compile হওয়ার সময়েই গুনতি জানা থাকতে হয়, আর array<int, 7> আর array<int, 8> দুইটা আলাদা type।
  • Deque দুই মাথাতেই বাড়ে push_back আর push_front দিয়ে: ও সেই মাথায় একটা block জুড়ে দেয়, তাই আগে থেকে রাখা কিছুই সরে না।
  • d[i] এখনো চলে, গোনা হয় এই মুহূর্তের সামনে থেকে; front(), back() আর pop-গুলোর জন্য deque খালি না হওয়া চাই।
  • আরও গভীরে যেতে চাইলে: Under the Hood, deque-এর block আর map, আর একটা push কী ভাঙে (Pro)।

এরপর lesson 02 দুই container-এরই প্রতিটা operation একটা একটা করে দেখাবে, প্রতিটার খরচসহ।

lesson ১ শেষ

শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।

পরেরটা: array আর deque-এর প্রতিটা operation, একটা একটা করে, খরচসহ

array আর deque: যে size তুমি ঠিক করে দাও, আর যে লাইন দুই মাথাতেই বাড়ে | Learn C++ STL | Progsity