Vector์ ํน์ง
Vector ๊ฐ์ฒด์ ๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ
Vector ๊ฐ์ฒด (์คํ):
โโโโโโโโโโโโโโโโโโโโโโโโ
โ ํฌ์ธํฐ (8 bytes) โ โ ์ค์ ๋ฐ์ดํฐ (ํ)
โโโโโโโโโโโโโโโโโโโโโโโโค
โ size (8 bytes) โ ํ์ฌ ์์ ๊ฐ์
โโโโโโโโโโโโโโโโโโโโโโโโค
โ capacity (8 bytes) โ ํ ๋น๋ ๊ณต๊ฐ ํฌ๊ธฐ
โโโโโโโโโโโโโโโโโโโโโโโโ
๋ฉ๋ชจ๋ฆฌ ์ฌํ ๋น ๋ฌธ์
std::vector<int> vec;
vec.push_back(1); // capacity: 1
vec.push_back(2); // capacity: 2 (๋ฉ๋ชจ๋ฆฌ ์ฌํ ๋น!)
vec.push_back(3); // capacity: 4 (๋ฉ๋ชจ๋ฆฌ ์ฌํ ๋น!)
vec.push_back(4); // capacity: 4 (์ฌํ ๋น ์์)
vec.push_back(5); // capacity: 8 (๋ฉ๋ชจ๋ฆฌ ์ฌํ ๋น!)
์ฌํ ๋น ๊ณผ์ :
1. ์๋ก์ด ๋ ํฐ ๊ณต๊ฐ ํ ๋น (๋ณดํต 2๋ฐฐ)
2. ๊ธฐ์กด ์์๋ค์ ์ ๊ณต๊ฐ์ผ๋ก ๋ณต์ฌ/์ด๋ (O(n))
3. ๊ธฐ์กด ๊ณต๊ฐ ํด์
๋ฌธ์ ์ :
reserve๋ก ํด๊ฒฐ
std::vector<int> vec;
vec.reserve(100); // ๋ฏธ๋ฆฌ 100๊ฐ ๊ณต๊ฐ ํ๋ณด
for (int i = 0; i < 100; i++)
{
vec.push_back(i); // ์ฌํ ๋น ์์!
}
reserve์ ํจ๊ณผ:
๋ฌธ์ ์ํฉ
class Cat
{
public:
explicit Cat(std::string name) : mName{std::move(name)}
{
std::cout << mName << " Cat constructor" << std::endl;
}
~Cat()
{
std::cout << mName << " ~Cat()" << std::endl;
}
Cat(const Cat& other) : mName{other.mName}
{
std::cout << mName << " copy constructor" << std::endl;
}
Cat(Cat&& other) : mName{std::move(other.mName)}
{
std::cout << mName << " move constructor" << std::endl;
}
private:
std::string mName;
};
int main()
{
std::vector<Cat> cats;
cats.emplace_back("kitty");
cats.emplace_back("nabi");
return 0;
}
์ถ๋ ฅ:
kitty Cat constructor
nabi Cat constructor
kitty copy constructor โ โ Move๊ฐ ์๋ Copy!
kitty ~Cat()
kitty ~Cat()
nabi ~Cat()
์ Copy๊ฐ ๋ฐ์ํ์๊น?
๋ฉ๋ชจ๋ฆฌ ์ฌํ ๋น ๊ณผ์ :
1. kitty ์ถ๊ฐ (capacity: 1)
2. nabi ์ถ๊ฐ ์๋
3. capacity ๋ถ์กฑ โ ์ ๊ณต๊ฐ ํ ๋น (capacity: 2)
4. kitty๋ฅผ ์ ๊ณต๊ฐ์ผ๋ก ์ด๋
โ ํ์ง๋ง Copy Constructor ํธ์ถ๋จ!
์ปดํ์ผ๋ฌ๊ฐ Copy๋ฅผ ์ ํํ๋ ์ด์ :
ํด๊ฒฐ ๋ฐฉ๋ฒ 1: noexcept ์ถ๊ฐ
class Cat
{
public:
explicit Cat(std::string name) : mName{std::move(name)}
{
std::cout << mName << " Cat constructor" << std::endl;
}
~Cat() noexcept
{
std::cout << mName << " ~Cat()" << std::endl;
}
Cat(const Cat& other) : mName{other.mName}
{
std::cout << mName << " copy constructor" << std::endl;
}
Cat(Cat&& other) noexcept // โ
noexcept ์ถ๊ฐ
: mName{std::move(other.mName)}
{
std::cout << mName << " move constructor" << std::endl;
}
Cat& operator=(const Cat& other)
{
mName = other.mName;
return *this;
}
Cat& operator=(Cat&& other) noexcept // โ
noexcept ์ถ๊ฐ
{
mName = std::move(other.mName);
return *this;
}
private:
std::string mName;
};
int main()
{
std::vector<Cat> cats;
cats.emplace_back("kitty");
cats.emplace_back("nabi");
return 0;
}
์ถ๋ ฅ (noexcept ์ถ๊ฐ ํ):
kitty Cat constructor
nabi Cat constructor
kitty move constructor โ โ
Move๋ก ๋ณ๊ฒฝ!
~Cat() โ ์ด๋๋ ์๋ณธ์ ์๋ฉธ์ (mName์ด ๋น์ด์์)
kitty ~Cat() โ ์ ์์น์ kitty
nabi ~Cat()
ํด๊ฒฐ ๋ฐฉ๋ฒ 2: reserve ์ฌ์ฉ
int main()
{
std::vector<Cat> cats;
cats.reserve(2); // โ
๋ฏธ๋ฆฌ ๊ณต๊ฐ ํ๋ณด
cats.emplace_back("kitty");
cats.emplace_back("nabi");
// ์ฌํ ๋น ์์ โ Copy/Move ๋ชจ๋ ๋ฐ์ํ์ง ์์!
return 0;
}
noexcept๋ฅผ ๋ถ์ฌ์ผ ํ๋ ํจ์
1. Destructor (์๋ฉธ์)
2. Move Constructor (์ด๋ ์์ฑ์)
3. Move Assignment (์ด๋ ๋์
์ฐ์ฐ์)
์ด์ :
noexcept๋ฅผ ๋ช
์ํ๋ฉด ์ปดํ์ผ๋ฌ๊ฐ ์ต์ ํ ๊ฐ๋ฅ์ธ ๊ฐ์ง ๋ฐ๋ณต๋ฌธ ๋ฐฉ์
std::vector<int> nums{0, 1, 0, 1};
// 1. Index-based
for (std::size_t idx = 0; idx < nums.size(); idx++)
{
if (nums[idx] == 0)
{
nums.emplace_back(2);
}
}
// 2. Iterator-based
for (auto itr = nums.begin(); itr != nums.end(); itr++)
{
if (*itr == 0)
{
nums.emplace_back(2);
}
}
// 3. Range-based
for (auto& num : nums)
{
if (num == 0)
{
nums.emplace_back(2);
}
}
๊ฒฐ๊ณผ:
Iterator-based์ ๋ฌธ์
std::vector<int> nums{0, 1, 0, 1}; // capacity: 4
for (auto itr = nums.begin(); itr != nums.end(); itr++)
{
if (*itr == 0) // ์ฒซ ๋ฒ์งธ 0 ๋ฐ๊ฒฌ
{
nums.emplace_back(2); // capacity ๋ถ์กฑ โ ์ฌํ ๋น!
// โ ๋ฌธ์ : ์ฌํ ๋น์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ ๋ณ๊ฒฝ
// itr์ ์ฌ์ ํ ์๋ ์ฃผ์๋ฅผ ๊ฐ๋ฆฌํด (dangling iterator)
// nums.end()๋ ์ ์ฃผ์๋ฅผ ๊ฐ๋ฆฌํด
}
}
Range-based์ ๋ฌธ์
Range-based for๋ ๋ด๋ถ์ ์ผ๋ก iterator๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ๊ฐ์ ๋ฌธ์ ๋ฐ์
// C++17: Range-based๋ ์ด๋ ๊ฒ ๋ณํ๋จ
{
auto&& __range = nums;
auto __begin = __range.begin();
auto __end = __range.end();
for (; __begin != __end; ++__begin)
{
auto& num = *__begin;
// capacity ๋ณ๊ฒฝ ์ __begin๊ณผ __end๊ฐ invalidate๋จ
}
}
Index-based๋ ์ ์์ ํ๊ฐ?
for (std::size_t idx = 0; idx < nums.size(); idx++)
{
if (nums[idx] == 0)
{
nums.emplace_back(2);
// nums.size()๋ ๋งค๋ฒ ์๋ก ๊ณ์ฐ๋จ
// nums[idx]๋ ๋งค๋ฒ ์ ์ฃผ์์์ ์ ๊ทผ
// โ
์ฌํ ๋น๋์ด๋ ๋ฌธ์ ์์
}
}
ํต์ฌ ๊ท์น
โ For loop ๋ด๋ถ์์ vector์ ํฌ๊ธฐ๊ฐ ๋ณ๊ฒฝ(์ฆ๊ฐ)๋๋ฉด ๋ฐ๋์ index-based๋ฅผ ์ฌ์ฉ!
// โ
์์ : Index-based
for (std::size_t i = 0; i < vec.size(); i++)
{
vec.push_back(x); // OK
}
// โ ์ํ: Iterator-based
for (auto it = vec.begin(); it != vec.end(); it++)
{
vec.push_back(x); // Undefined Behavior ๊ฐ๋ฅ!
}
// โ ์ํ: Range-based
for (auto& elem : vec)
{
vec.push_back(x); // Undefined Behavior ๊ฐ๋ฅ!
}
์์ธ: reserve๋ก ๋ฏธ๋ฆฌ ๊ณต๊ฐ ํ๋ณด
std::vector<int> nums{0, 1, 0, 1};
nums.reserve(100); // ์ถฉ๋ถํ ๊ณต๊ฐ ํ๋ณด
// โ
capacity ๋ณ๊ฒฝ์ด ์์ผ๋ฏ๋ก iterator๋ ์์
for (auto it = nums.begin(); it != nums.end(); it++)
{
if (*it == 0)
{
nums.emplace_back(2); // ์ฌํ ๋น ์์ โ ์์
}
}
erase์ ๋นํจ์จ์ฑ
std::vector<int> nums{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// ์ง์ ์ ๊ฑฐ (๋นํจ์จ์ ์ธ ๋ฐฉ๋ฒ)
for (auto it = nums.begin(); it != nums.end();)
{
if (*it % 2 == 0)
{
it = nums.erase(it); // โ ๋งค๋ฒ ๋ค์ ์์๋ค์ ์ด๋
}
else
{
++it;
}
}
erase์ ๋์:
[1, 2, 3, 4, 5]์์ 2 ์ ๊ฑฐ:
1. 2 ์ ๊ฑฐ
2. ๋ค์ ๋ชจ๋ ์์๋ฅผ ์์ผ๋ก ์ด๋
[1, _, 3, 4, 5]
[1, 3, _, 4, 5]
[1, 3, 4, _, 5]
[1, 3, 4, 5, _]
3. size ๊ฐ์
โ ํ ๋ฒ ์ ๊ฑฐํ ๋๋ง๋ค O(n)
โ k๊ฐ ์ ๊ฑฐ ์ O(n*k)
remove์ ํจ์จ์ ์ธ ๋์
std::vector<int> nums{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// std::remove๋ ์ ๊ฑฐํ ์์๋ฅผ ๋ค๋ก ๋ชจ์
auto new_end = std::remove_if(nums.begin(), nums.end(),
[](int n) { return n % 2 == 0; }
);
// ์ค์ ์ ๊ฑฐ๋ erase๋ก
nums.erase(new_end, nums.end());
remove์ two-pointer ์๊ณ ๋ฆฌ์ฆ:
์ด๊ธฐ: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
โ
write (๋ณต์ฌํ ์์น)
โ
read (ํ์ธํ ์์น)
1. read=1 (ํ์) โ write ์์น์ ๋ณต์ฌ, ๋ ๋ค ์ ์ง
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
โ
write
โ
read
2. read=2 (์ง์) โ ๋ณต์ฌ ์ ํจ, read๋ง ์ ์ง
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
โ
write
โ
read
3. read=3 (ํ์) โ write์ ๋ณต์ฌ, ๋ ๋ค ์ ์ง
[1, 3, 3, 4, 5, 6, 7, 8, 9, 10]
โ
write
โ
read
4. ๋ฐ๋ณต...
์ต์ข
: [1, 3, 5, 7, 9, 6, 7, 8, 9, 10]
โ
new_end (๋ฐํ๊ฐ)
โ new_end๋ถํฐ end()๊น์ง erase
โ ์ด O(n) ํ ๋ฒ๋ง!
erase-remove idiom (ํ์ค ํจํด)
// โ
ํจ์จ์ ์ธ ๋ฐฉ๋ฒ
nums.erase(
std::remove_if(nums.begin(), nums.end(),
[](int n) { return n % 2 == 0; }
),
nums.end()
);
// ํ ์ค๋ก ํํ ๊ฐ๋ฅ
์ฑ๋ฅ ๋น๊ต:
C++20: std::erase_if
// C++20๋ถํฐ๋ ๋ ๊ฐ๋จ
std::erase_if(nums, [](int n) { return n % 2 == 0; });
์ ๋ ฌ ์๊ณ ๋ฆฌ์ฆ
std::sort (์ผ๋ฐ ์ ๋ ฌ)
#include <algorithm>
std::vector<int> nums{5, 2, 8, 1, 9, 3};
std::sort(nums.begin(), nums.end());
// nums: {1, 2, 3, 5, 8, 9}
// ๋ด๋ฆผ์ฐจ์
std::sort(nums.begin(), nums.end(), std::greater<int>{});
// nums: {9, 8, 5, 3, 2, 1}
// ์ปค์คํ
๋น๊ต
std::sort(nums.begin(), nums.end(), [](int a, int b) {
return std::abs(a) < std::abs(b); // ์ ๋๊ฐ ๊ธฐ์ค
});
์๊ฐ ๋ณต์ก๋: O(n log n)
std::stable_sort (์์ ์ ๋ ฌ)
struct Person
{
std::string name;
int age;
};
std::vector<Person> people{
{"Alice", 25},
{"Bob", 30},
{"Charlie", 25},
{"David", 30}
};
// ๋์ด ๊ธฐ์ค ์ ๋ ฌ
std::stable_sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
}
);
// stable_sort: ๋์ด๊ฐ ๊ฐ์ผ๋ฉด ์๋ ์์ ์ ์ง
// Alice(25), Charlie(25), Bob(30), David(30)
// sort: ๋์ด๊ฐ ๊ฐ์ผ๋ฉด ์์ ๋ณด์ฅ ์ ๋จ
// Charlie(25), Alice(25), David(30), Bob(30) ๊ฐ๋ฅ
stable_sort์ ํน์ง:
std::partial_sort (๋ถ๋ถ ์ ๋ ฌ)
std::vector<int> nums{5, 2, 8, 1, 9, 3, 7, 4, 6};
// ์ฒ์ 3๊ฐ๋ง ์ ๋ ฌ
std::partial_sort(nums.begin(), nums.begin() + 3, nums.end());
// nums: {1, 2, 3, ?, ?, ?, ?, ?, ?}
// โ์ ๋ ฌ๋จ โ์ ๋ ฌ ์ ๋จ (์์ ๋ถํ์ )
// ์ค์ : {1, 2, 3, 8, 9, 5, 7, 4, 6}
์ฌ์ฉ ์ฌ๋ก:
std::nth_element
std::vector<int> nums{5, 2, 8, 1, 9, 3, 7, 4, 6};
// ์ค์๊ฐ ์ฐพ๊ธฐ (5๋ฒ์งธ๋ก ์์ ์์)
std::nth_element(nums.begin(), nums.begin() + 4, nums.end());
// nums[4]๋ ์ ํํ 5๋ฒ์งธ๋ก ์์ ์์
// nums[4] ์ผ์ชฝ์ ๋ชจ๋ ์๊ฑฐ๋ ๊ฐ์
// nums[4] ์ค๋ฅธ์ชฝ์ ๋ชจ๋ ํฌ๊ฑฐ๋ ๊ฐ์
// ํ์ง๋ง ์์ชฝ์ด ์ ๋ ฌ๋ ๊ฒ์ ์๋
std::cout << nums[4] << std::endl; // 5 (์ค์๊ฐ)
ํน์ง:
์ต์๊ฐ/์ต๋๊ฐ ์ฐพ๊ธฐ
std::vector<int> nums{5, 2, 8, 1, 9, 3};
// ์ต์๊ฐ
auto min_it = std::min_element(nums.begin(), nums.end());
std::cout << *min_it << std::endl; // 1
// ์ต๋๊ฐ
auto max_it = std::max_element(nums.begin(), nums.end());
std::cout << *max_it << std::endl; // 9
// ์ต์๊ฐ๊ณผ ์ต๋๊ฐ ๋์์
auto [min_it2, max_it2] = std::minmax_element(nums.begin(), nums.end());
std::cout << *min_it2 << ", " << *max_it2 << std::endl; // 1, 9
// ์ธ๋ฑ์ค ์ฐพ๊ธฐ
auto min_idx = std::distance(nums.begin(), min_it);
std::cout << "Min index: " << min_idx << std::endl; // 3
std::distance
auto it = std::find(nums.begin(), nums.end(), 8);
if (it != nums.end())
{
std::size_t index = std::distance(nums.begin(), it);
std::cout << "Found at index: " << index << std::endl;
}
ํฉ๊ณ ๋ฐ ๋์ ์ฐ์ฐ
std::accumulate
#include <numeric>
std::vector<int> nums{1, 2, 3, 4, 5};
// ํฉ๊ณ
int sum = std::accumulate(nums.begin(), nums.end(), 0);
std::cout << sum << std::endl; // 15
// ๊ณฑ์
int product = std::accumulate(nums.begin(), nums.end(), 1,
[](int acc, int n) { return acc * n; }
);
std::cout << product << std::endl; // 120
// ๋ฌธ์์ด ์ฐ๊ฒฐ
std::vector<std::string> words{"Hello", " ", "World"};
std::string result = std::accumulate(words.begin(), words.end(),
std::string{},
[](std::string acc, const std::string& s) { return acc + s; }
);
std::cout << result << std::endl; // "Hello World"
std::reduce (C++17)
// std::accumulate์ ๋น์ทํ์ง๋ง ๋ณ๋ ฌ ์คํ ๊ฐ๋ฅ
int sum = std::reduce(std::execution::par, // ๋ณ๋ ฌ ์คํ
nums.begin(), nums.end(), 0
);
์ฐจ์ด์ :
accumulate: ์์ฐจ ์คํ, ์์ ๋ณด์ฅreduce: ๋ณ๋ ฌ ์คํ ๊ฐ๋ฅ, ์์ ๋ณด์ฅ ์ ๋จ (๊ฒฐํฉ ๋ฒ์น ํ์)Vector ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
reserve()๋ก ์ฌํ ๋น ๋ฐฉ์งnoexcept๋ก move ์ต์ ํ๋ฐ๋ณต๋ฌธ ์ฃผ์์ฌํญ
ํจ์จ์ ์ธ ์ญ์
erase ๋ฐ๋ณต: O(n*k)erase-remove idiom: O(n)std::erase_if์๊ณ ๋ฆฌ์ฆ ์ ํ
std::sort O(n log n)std::stable_sort O(n log n)std::partial_sort O(n log k)std::nth_element O(n)std::min_element / std::max_element O(n)์ฝ๋์๋ ํ๋ก๊ทธ๋๋ฐ
C++ std::vector
C++ Algorithms Library