[STL] Algorithm - 원소 수정O

치치·2025년 7월 16일

STL

목록 보기
16/21
post-thumbnail

Algorithm 알고리즘


원소를 수정하는 알고리즘

덮어쓰기 모드로 동작하기 때문에 ➡ 목적지 순차열은 원본 이상의 원소를 가지고 있어야 한다.
➡ ✅ 기존 반복자 위치에 직접 써버리는 것이기 때문에 충분한 공간이 있어야 한다는 것!!


알고리즘 함수사용 형태 (인자)역할반환값
copycopy(first, last, out_first)[first, last) 범위를 out_first부터 복사출력 반복자 (out_first + 거리)
fillfill(first, last, value)[first, last) 구간을 모두 value로 채움void
fill_nfill_n(out_first, count, value)count개의 값을 out_first부터 value로 채움출력 반복자 (out_first + count)
for_eachfor_each(first, last, func)[first, last) 범위의 각 원소에 func(x) 적용 (출력, 수정 가능)함수 객체 (복사본)
generategenerate(first, last, gen)[first, last) 구간을 함수 gen()으로 생성void
generate_ngenerate_n(out_first, count, gen)count개의 값을 gen()으로 생성해 out_first부터 채움출력 반복자 (out_first + count)
swapswap(a, b)두 변수 a, b의 값을 교환void
iter_swapiter_swap(it1, it2)반복자 it1, it2가 가리키는 값을 교환void
swap_rangesswap_ranges(first1, last1, first2)[first1, last1)first2부터 같은 거리의 값을 교환first2 + 거리
transform (단일 입력)transform(first, last, out_first, unary_op)unary_op(x) 적용 결과를 out_first부터 저장출력 반복자 (out_first + 거리)
transform (이중 입력)transform(first1, last1, first2, out_first, binary_op)binary_op(x, y) 결과를 out_first부터 저장출력 반복자 (out_first + 거리)
mergemerge(first1, last1, first2, last2, out_first)정렬된 두 범위를 병합하여 out_first부터 저장출력 반복자 (out_first + 거리)
merge (함수 객체 사용)merge(first1, last1, first2, last2, out_first, comp)사용자 정의 비교 함수로 병합 결과 저장출력 반복자 (out_first + 거리)
replacereplace(first, last, old_val, new_val)[first, last)에서 old_valnew_val로 직접 교체void
replace_ifreplace_if(first, last, pred, new_val)조건(pred)을 만족하는 원소를 new_val로 직접 교체void
replace_copyreplace_copy(first, last, out_first, old_val, new_val)원본은 유지하고 old_val → new_val 변환 후 복사출력 반복자 (out_first + 거리)
replace_copy_ifreplace_copy_if(first, last, out_first, pred, new_val)조건 만족하는 원소만 new_val로 바꾸어 복사출력 반복자 (out_first + 거리)


📌 copy(); & copy_backward();

✅ copy(1구간 반복자 구간, 2구간 시작 반복자);

1구간 반복자를 2구간의 시작 반복자에 복사한다.
➡ 복사된 마지막 원소를 가리키는 반복자를 반환한다. (제일 뒤)

int main()
{
	vector<int> v;

	v.push_back(10);
	v.push_back(20);
	v.push_back(30);

	vector<int> v2(3); // 사이즈 3 {0, 0, 0} 상태

	vector<int>::iterator iter = copy(v.begin(), v.end(), v2.begin());

	cout << "마지막 원소 : " << * (iter - 1) << endl;

	for (int i = 0; i < v2.size(); i++)
	{
		cout << v2[i] << " "; // 복사된 후 v2의 값
	}
}


✅ copy_backward(반복자 구간, 시작 반복자);

뒤에서 부터 앞으로 값이 복사된다.
➡ 복사된 마지막 원소를 가리키는 반복자를 반환한다. (제일 앞)

int main()
{
	vector<int> v;

	v.push_back(10);
	v.push_back(20);
	v.push_back(30);

	vector<int> v2(6); // 사이즈 3 {0, 0, 0, 0, 0, 0} 상태

	vector<int>::iterator iter = copy_backward(v.begin(), v.end(), v2.end()-1);

	cout << "v2의 마지막 원소 : " << *iter << endl;

	for (int i = 0; i < v2.size(); i++)
	{
		cout << v2[i] << " ";
	}
}



📌 fill(); & fill_n();

반환값은 없다. (void)

✅ fill(반복자 구간, 채울 요소);

정의한 반복자 구간 내의 원소를 원하는 값으로 채운다. ➡ 덮어 씌우는 방식

✅ fill_n(시작 반복자, 시작 반복자 + n, 채울 요소);

➡ 시작 반복자 위치에서 +n의 위치까지 요소를 채운다. 즉, fill_n(b, b+e, n);

int main()
{
	vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	fill(v1.begin(), v1.end(), 0); // 구간에 값들을 0으로 채우기

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 0 0 0
	}

	cout << endl;

	// ----------------------------

	fill_n(v1.begin(), 2, 55);

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 0 0 0
	}
}



📌 for_each();

참조를 통해 원본의 원소 값이 직접 수정된다.

➡ 아래의 코드를 보면 v1의 각 원소들의 값이 참조를 통해 +5가 적용된다.

void Func(int & r) // 반환값이 없다.
{
	r += 5;
}

int main()
{
	vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	for_each(v1.begin(), v1.end(), Func);

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 15 25 35
	}
}

누적된 값을 사용하는 경우, 각 원소들이 total에 누적되고 r을 갱신하고 출력된다.

// 누적된 값을 사용하는 경우
class Calculate
{
	int total;

public:
	Calculate(int init = 0) : total(init) { }

	void operator() (int& r)
	{
		total += r;

		r = total; // r값에 적용되어야 v1원소에 적용된다.
	}
};

int main()
{
	vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	for_each(v1.begin(), v1.end(), Calculate(0));

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 10 30 60
	}
}


📌 generate(); & generate_n();

구간의 모든 원소를 f( ) 함수를 통해 채우는 방법

➡ 아래의 코드는 v1의 모든 원소들을 1씩 증가하면서 채운다.

class Calculate
{
	int total;

public:
	Calculate(int init = 0) : total(init) { }

	int operator() ()
	{
		return total++;
	}
};

int main()
{
	vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	generate(v1.begin(), v1.end(), Calculate(0));

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 0 1 2 
	}

	// ------------------------------------
    
    vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	
	// [v1.begin() ~ v1.begin()+1) 구간 반복자
	generate_n(v1.begin(), 1, Calculate(0));
	
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 0 20 30
	}
}


📌 swap(); & iter_swap();

  • swap : 단순 값 교환
  • iter_swap : 반복자가 가리키는 값 교환
int main()
{
	int a = 5; int b = 1;

	swap(a, b);

	cout << a << " " << b; // 1 5

	// -------------------------------
    
	vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	// 반복자끼리 스왑	
	iter_swap(v1.begin(),v1.begin()+1);

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 20 10 30
	}
}


📌 merge(); & merge(함수객체);

✅ merge(1구간 반복자, 2구간 반복자, 3시작 반복자);

➡ 1구간과 2구간을 병합하여 3구간에 저장한다.

merge()를 사용하기 위해서는 정렬되어있는 순차열만 사용 가능하다. ➡ 기본 오름차순 정렬

int main()
{
	vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	vector<int> v2;

	v2.push_back(60);
	v2.push_back(70);
	v2.push_back(80);

	vector<int> v3 (8); // 사이즈 8

	vector<int>::iterator iter = merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());

	for (int i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << " "; // 10 20 30 60 70 80 [0] 0
	}

	cout << endl << "목적지 끝 반복자 : " << *iter; // [0]
}

✅ merge(1구간 반복자, 2구간 반복자, 3시작 반복자, 함수 객체);

➡ 사용자 정의 함수 객체를 사용한 경우
아래의 코드는 사용자 정의 내림차순을 적용한 코드이다.

template <typename T>

struct Greater // 내림차순 정렬
{
	bool operator()(const T& left, const T& right) const
	{
		return left > right;
	}
};

int main()
{
	vector<int> v1;

	v1.push_back(30);
	v1.push_back(20);
	v1.push_back(10);

	vector<int> v2;

	v2.push_back(80);
	v2.push_back(70);
	v2.push_back(60);

	vector<int> v3 (8); // 사이즈 8

	vector<int>::iterator iter = merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin(), Greater<int>());

	for (int i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << " "; // 80 70 60 30 20 10 0 0
	}

	cout << endl << "목적지 끝 반복자 : " << *iter; // [0]
}


📌 replace(); & replace_if();

✅ replace(1구간 반복자, 기존 원소, 바꾼 원소 값);

  • replace() : 반복자 구간 범위 내의 원소 중 원하는 원소의 값을 교체한다.
  • replace_if() : 조건이 만족하면 원소의 값을 교체한다.
bool Calculate(int n)
{
	return 20 <= n; // 20보다 크다면 교체
}

int main()
{
	vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	replace(v1.begin(), v1.end(), 10, 5); // 10인 원소를 5로 교체

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 5 20 30
	}

	// -----------------------------------

	replace_if(v1.begin(), v1.end(), Calculate, 1); // 20보다 큰 원소를 1로 교체

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 5 1 1 
	}
}


📌 replace_copy(); & replace_copy_if();

replace + copy == 즉, 값을 교체하고 복사한 값을 저장하기

✅ replace_copy(1구간 반복자, 2구간 시작 반복자, 기존 원소, 바꾼 원소값);

➡ replace_copy_if() 는 조건에 만족하면 원소를 교체하고 복사한 값을 저장

bool Calculate(int n)
{
	return 20 <= n; // 20보다 크다면 교체
}

int main()
{
	vector<int> v1;

	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);

	vector<int> v2(6); // 사이즈 6

	// 10인 원소를 5로 교체 후 v2에 저장
	replace_copy(v1.begin(), v1.end(),v2.begin(), 10, 5);

	for (int i = 0; i < v2.size(); i++)
	{
		cout << v2[i] << " "; // 5 20 30 0 0 0
	}

	// ------------------------------

	vector<int> v3(6); // 사이즈 6

	// 20이상인 원소를 1로 교체 후 v3에 저장
	replace_copy_if(v1.begin(), v1.end(), v3.begin(), Calculate, 1);

	for (int i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << " "; // 10 1 1 0 0 0
	}
}


📌 swap_ranges();

두 순차열의 반복자 구간의 범위를 모두 바꾸는 방법
➡ 서로의 순차열의 원소 값이 바뀐다.

int main()
{
	vector<int> v1;

	v1.push_back(10); // 변경
	v1.push_back(20);
	v1.push_back(30);

	vector<int> v2;

	v2.push_back(50); // 변경
	v2.push_back(60);
	v2.push_back(70);

	swap_ranges(v1.begin(), v1.begin() + 1, v2.begin());

	for (int i = 0; i < v2.size(); i++)
	{
		cout << v2[i] << " "; // 10 60 70
	}

	cout << endl;

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 50 20 30
	}
}


📌 transform();

✅ transform(1구간 반복자, 2구간 시작 반복자, 함수);

반환타입을 가지고, for_each()와는 다르게 값이 복사되어 들어가기 때문에 다른 컨테이너 값을 저장할 수 있다. ➡ 이 경우엔, 원본 값은 변경되지 않는다.

아래의 경우는 v1 순차열에 값이 복사되어 Func()을 거치고 반환된 값들이 다시 v1 순차열로 저장되기 때문에 원본의 값이 변경된다.

// 반환 타입을 가진다.
int Func(int n)
{
	return n + 5;
}

int main()
{
	vector<int> v1;

	v1.push_back(10); 
	v1.push_back(20);
	v1.push_back(30);

	vector<int>::iterator iter = transform(v1.begin(), v1.end(), v1.begin(), Func);

	cout << "목적지의 끝 반복자 : " << *(iter - 1) << endl;

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " "; // 15 25 35
	}
}

아래의 경우는 v1의 순차열과 v2의 순차열을 서로 더한 뒤 반환된 값을 v3에 저장한다.
원본의 값은 변경되지 않는다.

// 반환 타입을 가진다.
int Plus(int left, int right)
{
	return left + right;
}

int main()
{
	vector<int> v1;

	v1.push_back(10); 
	v1.push_back(20);
	v1.push_back(30);

	vector<int> v2;

	v2.push_back(10);
	v2.push_back(20);
	v2.push_back(30);

	vector<int> v3 (10); // 사이즈 10

	vector<int>::iterator iter = transform(v1.begin(), v1.end(), v2.begin(), v3.begin(), Plus);

	cout << "목적지의 끝 반복자 : " << *(iter - 1) << endl;

	for (int i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << " "; // 15 25 35
	}
}



⭐ for_each() / transform() 차이

  • for_each()
    주로 출력, 누적에 사용된다.
    참조를 통해 실제 원본의 값에 적용될 수 있고, 출력할 때 적용된 값이 나온다.
    함수의 반환값이 없다.
    ➡ ⭐출력 매개변수를 사용하기 때문에 & 사용

  • transform
    반환값이 있고, 반환 값을 사용하여 사용자 동작(함수)을 원소에 적용한다.
    ➡ ⭐값을 복사하고, Func()을 지나 값을 반환한다.
    복사된 값을 사용하기 때문에 만약 원본값에서 다른 곳에 추가 할 경우 원본 값은 변화가 없다.
    ➡ 만약 원본값에 그대로 적용 할 경우엔 원본 값에 변화가 적용된다.

profile
뉴비 개발자

0개의 댓글