unique함수는

https://velog.io/@whipbaek/c-unique-%ED%95%A8%EC%88%98%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC

 

c++ unique() 함수에 관하여

공부하게 된 배경https://programmers.co.kr/learn/courses/30/lessons/12906위 문제를 푸는데 unique와 erase의 조합 한 줄 만으로 해결하는 코드가 있었다. 문제를 풀 때는 생각이 안났으나 두 함수 모두 대강 알

velog.io

의 블로그를 참고하여 작성하였습니다.


unique(구간의 시작값, 구간의 마지막값) ;

-범위내에 인접하는(연속적인) 중복 요소들을 제거해준다.

-정렬 후에 배열의 사이즈가 바뀌지 않음

-배열의 사이즈가 바뀌지 않고 중복요소인 원소들은 뒤로 밀려난다.

-반환값은 중복없이 나열된 마지막 원소 다음의 반복자를 반환한다.

- <algorithm> 헤더를 사용한다.

 

unique함수 하나로는 vector 중복 원소 제거를 하는것은 어렵다.sort()함수를 사용하여 정렬 한 뒤 erase()함수를 활용하여 쓰레기값을 제거해줘야한다. 

 

 

#include<iostream>
#include<vector>
#include<algorithm>

void main()
{
    std::vector<int> v1 = { 10,10,20,30,20,10 };

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

    for (int index : v1)
    {
        std::cout << index << " ";
    }
    std::cout << " 반환값 : " << *iter << " " << &iter << std::endl;

    //======================================================

    std::vector<int> v2 = { 10,10,20,30,20,10 };

    sort(v2.begin(), v2.end()); // 10,10,10,20,20,30
    iter = unique(v2.begin(), v2.end());
    for (int index : v2)
    {
        std::cout << index << " ";
    }
    std::cout << " 반환값 : " << *iter << " " << &iter << std::endl;
	
    v2.erase(iter, v2.end());
    // v2.erase(unique(v2.begin(), v2.end()), v2.end());
    //unique와 erase함수를 사용하여 정렬한 vector의 중복된 원소를 제거할 수 있다.  

    for (int index : v2)
    {
        std::cout << index << " ";
    }


    return;
}

 

<출력 결과>

첫번째 결과 : 정렬없이 unique함수를 동작했을때 반환되는 값은 v1[4]의 반복자가 반환된다.

두번째 결과 : 오름차순 정렬 뒤에 unique함수를 동작했을때 10의 원소가 한개씩 20,30으로 변경 되었다. 하지만 의미 중복을 제거하는 것이 목적이기에 의미 없는값들이다. (작동원리는 밑에서 서술)

세번쨰 결과 : v2.erase함수를 범위 : iter(v2[3]) ~ v2.end() 만큼 제거한뒤 출력한 값이다. 

 

<작동 원리>

더보기

위 링크는 첫번째 결과값을 작동원리를 설명했으니 나는 정렬한 두번째 값(10 10 10 20 20 30)으로 설명을 해보겠다.

//unique함수 원문
template <class ForwardIterator>
  ForwardIterator unique (ForwardIterator first, ForwardIterator last)
  {
  if (first==last) return last;

  ForwardIterator result = first;
  while (++first != last)
  {
    if (!(*result == *first))  // or: if (!pred(*result,*first)) for version (2)
      *(++result)=*first;
  }
  return ++result;
}//출처 : https://www.cplusplus.com/reference/algorithm/unique/

  if (first==last) return last;

first iter와 last iter가 같은경우는 원소가 1개 이하라는 뜻이므로 end() 값을 리턴

 

 

first, last , result iter(반복자) 초기화 완료
while 반복문의 함수 동작후 확인, // while문이 참이므로 반복분 시작

while 반복문에 들어와서 

 if (!(*result == *first)) , if조건문이 있는데  result와 first가 같으므로 true에 !를 만나 false를 반환

 

다시 while 반복문의 조건을 확인해서 계속 roop 시키면 ......

result와 first의 값이 달라지는 경우가 발생

 

 if (!(*result == *first)의 true임으로 if문이 동작한다.

*(++result)=*first; 동작 완료시

*first 가 가르키고 있는 20의 data를 result의 다음 반복자(++result)에 data를 정의하는것이다. 

그럼 vector의 [1] index의 data는 10-> 20으로 변경된다. 그리고 이 과정을 while이 false가 나올때까지 반복하게 되면  

중복 되지 않은 원소는 전부 앞으로 정렬하게 되고, 중복된 원소들은 뒤로 밀리게 되며 반환되는 return 값은 현재 가르키고있는 result의 다음 iter 값이 반환된다. 즉, unique함수의 return값은 쓰게기값(중복값)들이 모여있는 첫번째 인덱스를 iter를 반환하게 된다. 

 

이 점을 이용하여 unique한뒤 erase함수로 현재 반환 받은 값에서부터 end() 까지 지워버리면 

10 20 30 만 남게 되는것이다. 

 

reverse_iterator(역방향 반복자) : 반복자(iterator)와는 정반대로 동작하는 반복자

++,-- 연산자를 이용하여 iter를 변경하고 그랬는데 반대로 동작하게된다.

 

std::vector에서 자주 사용해온 begin(), end() 명령어는 반환값으로 iter(반복자)를 반환하는데

rbegin(), rend()는 riter(역방향반복자)를 반환한다.

즉,begin() = rend() , end() = rbegin() 인것이다.

 

예제코드)

#include <iostream>
#include <vector>
#include <algorithm>

int main(void)
{
    std::vector<int> v = { 10,20,30,40,50 };
	
    //벡터의 첫 원소부터 차례대로 출력
    for (std::vector<int>::iterator iter = v.begin(); iter != v.end(); iter++)
    {
        int num = *iter;
        cout << num;
    }

    cout <<endl;
    
    //벡터의 뒷 원소부터 역으로 출력 ( riter가 ++ 로 증가하지만 역으로 감소한다)
    for (std::vector<int>::reverse_iterator riter = v.rbegin(); riter != v.rend(); riter++)
    {
        int num = *riter;
        cout << num;
    }

}

//출력 결과
1020304050
5040302010

이 역방향 반복자를 이용하여 vector 자료형의 내림차순 정렬을 쉽게 만들 수 있다.

내림차순 정렬을 하는 방법은 많지만 주로 코딩테스트를 풀 때 이 방법들을 사용해 왔다.

(sort(구간의 시작값, 구간의 끝값) , sort 함수는 algorithm 헤더에 포함되어있다,)

 

1.sort함수에서 조건자에 grearter<자료형>() 을 쓴다.
2.조건자를 재량것 compare 함수를 만들어 쓴다.

3.sort함수로 오름차순 정렬한것을 reverse()로 뒤집어 내림차순 정렬로 만들어 쓴다.

4.sort함수의 인자값을 역방향반복자를 매개변수로 넣는다. (이 코드 만들수 있다.)

 

예제 코드)

#include <iostream>
#include <vector>
#include <algorithm>

void main()
{
	std::vector<int> v = { 1,5,2,8,4,9 };

	//오름차순 정렬
	sort(v.begin(), v.end());
	
	for (int index : v)
	{
		std::cout << index << " ";
	}
	std::cout << std::endl;

	//내림차순 정렬 
	sort(v.rbegin(), v.rend()); // 이부분이 v의 원소들을 내림차순 정렬을 한다. 
	for (int index : v)
	{
		std::cout << index << " ";
	}
	return;
}

//출력 결과
//1 2 4 5 8 9 (오름차순 정렬)
//9 8 5 4 2 1 (내림차순 정렬)

 

'C++' 카테고리의 다른 글

unique() (vector의 중복 값 제거)  (0) 2023.06.12
STL 범용 수치 알고리즘 (accumulate , inner_product)  (0) 2023.06.09
문자집합(Character Set)  (0) 2023.06.04

 

//accumulate 함수의 원문
#include <numeric>

template <class InputIterator, class T>							
T accumulate (InputIterator first, InputIterator last, T init);

template <class InputIterator, class T, class BinaryOperation>	
T accumulate (InputIterator first, InputIterator last, T init, BinaryOperation binary_op);

accumulate(구간의 시작 값, 구간의 마지막 값 , 초기 값)

-지정한 구간에 속한 값들을 모든 더한 값을 계산한다.

-기본적으로 더하기 연산, 조건자 이용시 이외의 연산 가능 (binary_op) 

-필요 헤더 : <numeric>

 

예제 코드)

#include <iostream>
#include <vector>
#include <numeric>

void main()
{
	std::vector<int> v1 = { 1,2,3,4 };

	int result = std::accumulate(v1.begin(), v1.end(), 0);

	std::cout << result;

	return;
}

//출력결과
//10
//1 + 2 + 3 + 4 = 10

//inner_product 함수 원문
#include <numeric>

template <class _InIt1, class _InIt2, class _Ty, class _BinOp1, class _BinOp2>
_Ty inner_product(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _Ty _Val, _BinOp1 _Reduce_op, _BinOp2 _Transform_op)

template <class _InIt1, class _InIt2, class _Ty>
_Ty inner_product(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Ty _Val)

inner_product는 내적이란 뜻이며 벡터(방향,크기를 가지고있는 값)를 마치 수처럼 곱하는 개념)을 뜻한다.

a라는 std::vecotr<int> b라는  std::vecotr<int> 있다고 가장할때

inner_product(시작값a,마지막값a,시작값b,초기값);

-두 입력값의 내적을 계산하는 알고리즘으로 기본적으로 +,*을 사용한다.

-반환값은 a,b의 값을 곱한뒤 서로 더한값이다.

-두번째(b)의 크기는 첫번째(a)의 크기보다 크거나 같아야한다.

 

p.s 벡터의 내적은 벡터의 곱(dot product) 라고도 부르며 벡터 사이의 크기(스칼라)를 나타날때 사용된다. 

(수학 - 벡터 관련 수학 작성후  링크 필요)

 

예제코드)

#include <iostream>
#include <vector>
#include <numeric>

void main()
{
	std::vector<int> v1 = { 1,2,3 };
	std::vector<int> v2 = { 4,5,6 };

	int result = std::inner_product(v1.begin(),v1.end(),v2.begin(),0);

	std::cout << result;

	return;
}

//출력결과
//32
//0(초기값) + (1 * 4) + (2 * 5) + (3 * 6) = 32

 

더보기 주의 , 코드에 관한 설명이 간략히 있지만 코드 내용 원문이 있으니 주의 할 것

 

<나의 풀이>

더보기

이 문제는 규칙에 의한 이진 변환시 roop 돌은 횟수roop를 돌면서 지운 0의 갯수를 구하는 문제이다.

 

<플로우 차트>

1.0 제거하기

1-1.문자열 s안에 있는 0과 1을 내림차순 정렬 하기

1-2. 문자열의 0의 첫번째 index값을 찾음

1-3. index값 반환에 성공한경우 if문 동작해서 0을 제거

2. 0을 제거한 문자열 s의 길이 체크후 이진변환

2-1. 문자열을 2로 계속 나눠서 이진 변환시키기

 

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> solution(string s) {
    vector<int> answer;
    int roop = 0; //roop를 돈 횟수 
    int zero_del = 0; //roop를 돌면서 지워진 0의 갯수
	
    //s이 "1"이되면 roop 종료 
    while (s != "1")
    {
        int zero_index = 0;

        //1. 0제거하기
        sort(s.rbegin(), s.rend());//내림차수 정렬 ex)111100000
        zero_index = s.find("0");
		
        //0을 찾을경우 if문 동작
        if (zero_index != -1)
        {
            zero_del += s.length() - zero_index; // 현재 roop에서 지운 0의 갯수 카운트
            s.erase(s.begin() + zero_index, s.end());
            //문자열의 0 첫번째 index(s.begin() + zero_index) , 문자열 끝index를 지워주면 1만 남게된다.
        }
       

        //2. 0을 제거한 s의 길이 체크후 이진변환
        int zero_del_str = s.length();
        s = ""; //s문자열 초기화 

        while (zero_del_str != 0)
        {
        //10진수를 2진수로 변환하는 방법
            s += to_string(zero_del_str % 2);
            zero_del_str /= 2;
        }
        reverse(s.begin(), s.end());// s문자열 원소를 뒤집어 원하는값으로 바꾸기 
        roop++; //1회 roop
    }


    return { roop,zero_del};
}

<다른사람의 풀이>

더보기

이진수를 vector<bool>값으로 나타냈으며 , 반복문 while에 들어가기전 for_each문을 사용하여 문자열s의 값을 vector<bool> 이진수 로 변경을 하였고 반복문에서 반복작업을 하였다. 

#include <string>
#include <vector>
#include <algorithm>
using namespace std;

vector<int> solution(string s)
{
    int zeros{ 0 }, num_transform{ 0 };
    vector<bool> bin;

    for_each(s.cbegin(), s.cend(), [&bin](const char c) {bin.emplace_back(c == '1'); });  //s를 이진수로 변환

    while (true)
    {
        if (bin == vector<bool>{true})
            break;
        
        int ones = count(bin.cbegin(), bin.cend(), true);    //1갯수를 셈
        zeros += bin.size() - ones;                          //0갯수를 셈
        bin.clear();
        
        while (ones > 0)
        { 
            bin.emplace_back(ones % 2);
            ones /= 2; 
        }//1갯수를 2진수로 바꿈. 순서는 거꾸로지만 계산에는 영향없음
        
        ++num_transform;                                   //이진변환 횟수 기록
    }

    return { num_transform,zeros };
}

https://school.programmers.co.kr/learn/courses/30/lessons/12906

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

더보기 주의 , 코드에 관한 설명이 간략히 있지만 코드 내용 원문이 있으니 주의 할 것

 

<나의 풀이>

더보기

현재 index와 다음 index 값을 비교해서 다를경우,

answer에 현재 index값을 저장 후 roop를 진행한다.

index+1 했을때 arr의 size보다 크거나 같을경우 마지막 숫자를 answer에 추가하면서

반복문을 탈출한다.

#include <vector>
#include <iostream>

using namespace std;

vector<int> solution(vector<int> arr) 
{
    vector<int> answer;
	
    //arr의 갯수만큼 roop
    for(int i = 0 ; i< arr.size() ; i++)
    {
    	//배열이 범위를 벗어나지 않게 하기위한 조건문
        if( i+1 >= arr.size())
        {
            answer.push_back(arr[i]);
            break;
        }
        
        //현재 index와 다음 index가 다를경우 값을 저장 
        if(arr[i] != arr[i+1])
        {
            answer.push_back(arr[i]);
        }
    }
    
    return answer;
}

<다른사람의 풀이>

더보기

algorithm헤더의 unique함수와 std::vector의 erase 함수 두개를 활용하면 해당 배열의 원소 중 중복값을 없앨수 있다.

 

unique 함수 : 배열에서 원소를  앞에서부터 채워나가며 채운 함수중에 중복이 있으면 뒤로 보내는 함수이다.

unique함수는 중복되지 않은 값들을 나열 한뒤 남은 중복 원소의 첫번째 iterator 값을 반환, 

unique함수를 사용하기전에 sort 함수를 하여 정렬할 필요가 있다.

 

(unique함수로부터 반환된 iterator , 배열의 끝  iterator) 범위를 eraser함수하면
함수 고유의 값들만 남아있다. 

 

하지만 이 문제는 함수의 순서를 바꾸지 말라고 하였기에 sort함수를 사용하지 않고 바로 unique함수를 활요한 것이다.

 

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

vector<int> solution(vector<int> arr) 
{
	//vector 에서 중복되는 함수들을 한곳에 모아 삭제할때 쓰는 구조다.
    arr.erase(unique(arr.begin(), arr.end()),arr.end());

    vector<int> answer = arr;
    return answer;
}

 

'코딩테스트 > 프로그래머스' 카테고리의 다른 글

LV2.숫자 변환하기  (0) 2023.10.26
LV2.2개 이하로 다른 비트  (0) 2023.10.26
LV.2 최솟값 만들기  (0) 2023.06.09
LV.1 내적  (0) 2023.06.09
LV.1 없는 숫자 더하기  (0) 2023.06.09

https://school.programmers.co.kr/learn/courses/30/lessons/12941

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

더보기 주의 , 코드에 관한 설명이 간략히 있지만 코드 내용 원문이 있으니 주의 할 것

 

<나의 풀이>

더보기

2개의 벡터가 주어지는데 각 벡터의 원소들을 곱하고서 그 합의 최솟값을 구하는 문제이다.
즉, (첫번째 작은 값 * 첫번째 큰 값) + (두번째 작은 값 * 두번째 큰 값) + .... = 최솟값 이 나온다 

 

2개의 벡터에 하나는 오름차순으로 정렬 하나는 내림차순으로 정렬한뒤

반복문을 이용하여 작은값 * 큰값을 roop할 수 있게 해주면된다.

#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;

int solution(vector<int> A, vector<int> B)
{
    int answer = 0;
    
    sort(A.begin(),A.end());
    sort(B.begin(),B.end());
    reverse(B.begin(),B.end());

    
    //양쪽 벡터에서 가장 작은 숫자 하나와 가장큰숫자를 골라서 곱한다.
    
    for(int i=0; i< A.size(); i++)
    {
        answer += A[i] * B[i];
    }

    return answer;
}

<다른사람의 풀이>

더보기
#include <vector>
#include <numeric>
#include <algorithm>
using namespace std;
int solution(vector<int> A, vector<int> B){
    sort(A.begin(),A.end());  sort(B.rbegin(),B.rend());
    return inner_product(A.begin(),A.end(),B.begin(),0);
}

 익숙하지 않은 명령어들이 있어서 정리하려고 가져왔다.


*inner_product();

numeric 헤더에 포함된 명령어이며, 내적을 구할때 사용하는 명령어이다.

inner_product(시작값A,마지막값A,시작값B,초기값) 으로 사용된다. 

예제 코드 및 추가적인 내용은 밑에 링크 확인

https://01149.tistory.com/14

 

STL 범용 수치 알고리즘 (accumulate , inner_product)

//accumulate 함수의 원문 #include template T accumulate (InputIterator first, InputIterator last, T init); template T accumulate (InputIterator first, InputIterator last, T init, BinaryOperation binary_op); accumulate(구간의 시작 값, 구간의

01149.tistory.com


*rbegin(),rend()

std::vector 자료형의 내림차순 정렬이 필요할때 , rbegin, rend 함수를 활용하면 가독성 좋은 코드를 짤 수 있다. 

예제 코드 및 추가적인 내용은 밑에 링크 확인

https://01149.tistory.com/15

 

std::vector (reverse_iterator,rbegin(),rend(),역방향반복자,내림차순정렬)

reverse_iterator(역방향 반복자) : 반복자(iterator)와는 정반대로 동작하는 반복자 ++,-- 연산자를 이용하여 iter를 변경하고 그랬는데 반대로 동작하게된다. std::vector에서 자주 사용해온 begin(), end() 명

01149.tistory.com

 

 

'코딩테스트 > 프로그래머스' 카테고리의 다른 글

LV2.2개 이하로 다른 비트  (0) 2023.10.26
LV.1 같은 숫자는 싫어  (0) 2023.06.09
LV.1 내적  (0) 2023.06.09
LV.1 없는 숫자 더하기  (0) 2023.06.09
LV.1 공원산책  (0) 2023.06.08

https://school.programmers.co.kr/learn/courses/30/lessons/70128

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

더보기 주의 , 코드에 관한 설명이 간략히 있지만 코드 내용 원문이 있으니 주의 할 것

 

<나의 풀이>

더보기
문제가 각 좌표의 내적을 구하는 것이기 때문에 그냥 내적 구하는 식에 반복문만 추가하면 쉽게 풀 수 있다. 
#include <string>
#include <vector>

using namespace std;

int solution(vector<int> a, vector<int> b) {
    int answer = 0;
    
    for(int i=0; i < a.size() ; i++)
    {
        answer += a[i] * b[i];
    }
    
    return answer;
}

'코딩테스트 > 프로그래머스' 카테고리의 다른 글

LV.1 같은 숫자는 싫어  (0) 2023.06.09
LV.2 최솟값 만들기  (0) 2023.06.09
LV.1 없는 숫자 더하기  (0) 2023.06.09
LV.1 공원산책  (0) 2023.06.08
[LV2] 귤 고르기  (0) 2023.05.31

https://school.programmers.co.kr/learn/courses/30/lessons/86051

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

더보기 주의 , 코드에 관한 설명이 간략히 있지만 코드 내용 원문이 있으니 주의 할 것

 

<나의 풀이 (비추천)>

더보기

0~9 까지의 숫자중 nuebers vector에 없는 원소를 다 더하는 문제다.

 

0.사전준비

0-1. 0~9의 list 만들기

0-2.numbers를 오름차순으로 정렬하기

 

1.list와 nubers의 원소를 비교하여 없는 원소 합 구하기

1-1.list와 number의 원소값이 다른경우 list의 현재 index값을 answer에 더한다.

1-2.list의 index만 증가시키고 다시 비교 후 원소값이 같지 않으면 1-1 반복, 같은경우 반복문을 진행

1-3.numbers의 index가 사이즈와 같거나 큰 경우 list와 비교하지 않은 원소들을 전부 answer에 더한다.

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int solution(vector<int> numbers) {
    int answer = 0;
    vector<int> list = {0,1,2,3,4,5,6,7,8,9};
    
    sort(numbers.begin(),numbers.end());
    
    for(int i=0 , j=0; i< list.size() ; i++,j++)
    {
        if(j >= numbers.size())
        {
           answer += list[i];
            continue;
        }
        
        while(list[i] != numbers[j])
        {
            answer += list[i];
            i++;
        }
    }
    
    return answer;
}

<다른사람의 풀이>

더보기

숫자의 범위는 0~9까지 변동이 없기 때문에 총합은 45로 고정되어있다.
numbers에서 총합45를 하나씩 빼면 남는 숫자는 numbers의 제외된 숫자의 총합이기 때문에 이렇게 간단히 쓸수있다.

나의 풀이는 각 어떤 원소가 남이있는지에 중점을 두다 보니 이렇게 코드를 짯다. 하지만 원하는건 제외된 숫자의 총합이기 떄문이다. 다음에 문제를 풀때는 원하는 값이 무엇인지 더욱 생각해야될듯하다. 

#include <string>
#include <vector>

using namespace std;

int solution(vector<int> numbers) {

    int answer = 45;

    for (int i = 0 ; i < numbers.size() ; i++)
        answer -= numbers[i];

    return answer;
}

 

 

'코딩테스트 > 프로그래머스' 카테고리의 다른 글

LV.2 최솟값 만들기  (0) 2023.06.09
LV.1 내적  (0) 2023.06.09
LV.1 공원산책  (0) 2023.06.08
[LV2] 귤 고르기  (0) 2023.05.31
[LV1] 대충 만든 자판  (0) 2023.05.28

+ Recent posts