연관 컨테이너 사용하기 USING ASSOCIATIVE CONTAINERS

24
연연 연연연연 연연연연 USING ASSOCIATIVE CONTAINERS Chapter 7

description

Chapter 7. 연관 컨테이너 사용하기 USING ASSOCIATIVE CONTAINERS. 7 장에서는 …. 지금까지 순차컨테이너 (sequential container): vector, list 순차컨테이너의 요소들은 우리가 선택한 순서대로 유지된다 7 장에서는 순차컨테이너 를 이용하는 경우 비효율적인 경우가 있다 . 예 : 컨테이너 요소에 정수 42 가 포함되어있는지 확인하는 문제 방법 1) 컨테이너의 시작부터 끝까지 검사 - PowerPoint PPT Presentation

Transcript of 연관 컨테이너 사용하기 USING ASSOCIATIVE CONTAINERS

Page 1: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

연관 컨테이너 사용하기

USING ASSOCIATIVE CONTAINERS

Chapter 7

Page 2: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

7 장에서는…

지금까지 순차컨테이너 (sequential container): vector, list 순차컨테이너의 요소들은 우리가 선택한 순서대로 유지된다

7 장에서는 순차컨테이너를 이용하는 경우 비효율적인 경우가 있다 . 예 : 컨테이너 요소에 정수 42 가 포함되어있는지 확인하는

문제 방법 1) 컨테이너의 시작부터 끝까지 검사 방법 2) 적당한 순서대로 저장하여 원하는 요소를

효과적으로 찾을 수 있는 알고리즘 이용

효과적인 참조방법을 제공하는 연관 컨테이너 : map

2

Page 3: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

연관 컨테이너 (Associative Containers)

연관 컨테이너의 요소들은 삽입한 순서대로 배열하지 않고 , 요소의 값에 따라 삽입

순서를 자동적으로 조정한다 . 따라서 , 특정 요소들을 더 빨리 검색할 수 있다 .

Maps: 빠른 검색을 위한 연관 컨테이너 연관 자료구조에는 키 - 값 쌍 (key-value pair) 으로 저장 연관 배열 (associative array)

값과 키를 연결시켜서 , 키 (key) 에 의해 빠르게 삽입 , 탐색

효과적인 검색을 위해 사용하는 컨테이너의 요소

3

Page 4: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

map

map 을 정의하는 형식 : map<key_type, value_type> var_name;

map 의 각 요소은 pair 타입

pair 타입 : first 와 second 를 멤버로 갖는 구조체 (struct) 타입 pair 클래스는 여러 타입의 값을 담을 수 있다 . 따라서 , first 와

second 의 데이터 멤버를 명시해야 한다 .

pair<const key_type, value_type> 키는 항상 const 이기 때문에 해당하는 값은 바꿀 수 없다 .

#include <map>

4

Austria 43

Korea 82

U.S.A 1

<key> <value>first second

pair

Page 5: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

순차 컨테이너와 연관 컨테이너 대부분의 경우 , map 은 vector 처럼 동작한다

map 과 vector 의 기본적인 차이점 : 인덱스 vector: 인덱스가 정수이다 . v[0], v[1], v[2], … map: 다른 것들과 순서를 비교할 수 있는 것이라면 어떤

타입도 가능하다 . string 일 수 있다 .

연관 컨테이너의 중요한 특징은 자체 순서 (self-ordering) 를 유지시킨다 .

입력 순서와 상관없이 키 값의 순서에 따라 저장된다 . 따라서 , 순서를 변경하는 작업은 할 수 없다 .

5

Page 6: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

각국의 국제전화 코드번호 저장할 정보의 키와 값 쌍 :

< 국가이름 , 국가코드번호 >map<string, int> m;m[“Canada”] = 1;m[“Austria”] = 43; pair<const string, int> e1(“Czech”, 42);m.insert(e1);cout << e1.first << “ ” << e1.second << endl; …

6

Austria 43

Canada 1

Czech 42

Egypt 20

Japan 81

Korea 82

Spain 34

U.S.A 1

<key> <value>first second

Page 7: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

Map 반복자

Map iterator: 순차적으로 (sequentially) Map 에 저장된 내용을 접근하도록 이용 가능

begin() end() map 반복자는 pair 를 가리킨다 : Map 에 저장된

pair 의 key 부분과 value 부분을 참조 it->first it->second

삽입된 순서와 상관없이 키 값에 따라 순서를 유지하고 있어서 정렬되어 출력된다 .

7

for(map<string, int>:: const_iterator it = m.begin(); it != m.end(); ++it)

cout << it-> first << “ ” << it->second << endl;

Page 8: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

Map 멤버함수

insert() : 요소 삽입 find() : 키를 이용한 검색 erase() : 요소 삭제

begin() end() size() clear() …

8

참조사이트 : http://msdn.microsoft.com/ko-kr/library/xdayte4c(v=VS.90).aspx

Page 9: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

연관컨테이너에 삽입 방법

연산자 [] 를 이용하는 방법 m1[1] = 10; m1[5]; // 디폴트 값 0 으로 초기화 됨 ; m1[5]=0; m1[1] = 11; // 이미 있는 요소일 때 : m[1] 의 값을 변경함

insert() 멤버함수를 이용하는 방법 pair 타입을 이용

1) pair<const int, int> e1(4, 40); m1.insert(e1);

2) m1.insert(pair<const int, int> (8, 80) );

9

map <int, int> m1;

Page 10: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

#include <map> …int main( ){ map <int, int> m1; map <int, int> :: iterator pIter; // Insert a data value of 10 with a key of 1 into a map // using the operator[] member function m1[ 1 ] = 10;

// Compare other ways to insert objects into a map m1.insert ( pair <const int, int> ( 3, 30 ) ); m1.insert ( pair <const int, int> ( 2, 20 ) );

for ( pIter = m1.begin( ) ; pIter != m1.end( ) ; pIter++ ) cout << pIter -> first << ": " << pIter -> second << endl;

// If the key already exists, operator[] // changes the value of the datum in the element m1[ 2 ] = 40;

// operator[] will also insert the value of the data // type's default constructor if the value is unspecified m1[5]; // m1[5]=0;

for ( pIter = m1.begin( ) ; pIter != m1.end( ) ; pIter++ ) cout << pIter -> first <<“: " << pIter -> second << endl;}

1: 102: 203: 30

출력결과 :

1: 102: 403: 305: 0

Page 11: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

operator[] 와 insert() 함수 비교

operator[] 를 이용해서 삽입한 경우 , 삽입 후 이미 존재하는 요소 (element) 가 변경되었는지 ,

새로운 요소를 삽입했는지를 알 수 없다 . insert() 멤버함수를 이용해서 삽입을 시도한 경우 ,

삽입하기 전에 , key 가 이미 존재하는를 알려준다 . return_type 은 map 반복자와 bool 로 된 pair 타입이다 .

pair< map<int,int>::iterator, bool > pr;

이미 존재할 때 : map 에서 해당요소의 pair 를 참조하는 반복자와 false 를 리턴한다 .

존재하지 않을 때 : map 에 pair 를 새로 추가하고 , 추가된 pair 를 참조하는 반복자와 true 를 리턴한다 .

11

pr = m1.insert( make_pair(1, 11) ); if( pr.second == true ) // 존재하지 않을 때

…else

Page 12: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

insert() 함수 사용 예

12

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

int main( ) { map <int, int> m; m.insert ( make_pair ( 1, 100 ) );

pair< map<int,int>::iterator, bool > pr; pr = m.insert ( make_pair ( 1, 10 ) );

if( pr.second == true ) { cout << "The element was inserted in m successfully." << endl; } else { cout << "Key number already exists in m\n" << "with an associated value = " << ( pr.first ) -> second // 저장된 값 << "." << endl; }}

Page 13: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

operator []

m[Key] = DataValue ; 연관컨테이너에서 Key 를 탐색한다 . 있으면 , DataValue 로

Key 와 연관된 값을 변경한다 . 없으면 , Key 와 DataValue 로 된 새로운 pair 를 삽입한다 .

m[s]; key 에 s 를 포함한 pair 를 연관컨테이너에서 탐색한다 . 컨테이너에 있으면 , pair 에 있는 s 와 연관된 값에 대한 참조

(reference) 를 리턴한다 . 연관 컨테이너에 없으면 ,

1) key 를 s 로 하고 , s 와 연관된 값을 디폴트 초기값 (int, double 은 0 으로 초기화 ) 으로 하는 pair 를 생성한다 .

2) 컨테이너의 적절한 위치에 pair 를 삽입한다 . 3) 저장된 새로운 pair 의 값에 대한 참조를 리턴한다 .

13

m[1] = 10;cout << m[1]; // 10 출력

cout << m[5]; // 0 출력

Page 14: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

find() 멤버 함수

find 함수 : Map 에 Key 값이 존재하는지를 탐색한다m.find( key );

탐색할 Key 가 map 에 저장되어 있으면 , 해당 pair 를 참조하는 반복자를 리턴한다 . 없으면 , m.end() 를 리턴한다 .

14

map <int, int> m; map <int, int> :: const_iterator m_iter; m.insert ( make_pair ( 1, 10 ) ); m.insert ( make_pair ( 2, 20 ) ); m.insert ( make_pair ( 3, 30 ) );

m_iter = m.find( 2 ); if ( m_iter != m.end( ) ) cout << "The element of map m with a key of 2 is: " << m_iter -> second << endl;

// If no match is found for the key, end( ) is returned m_iter = m.find( 4 );

if ( m_iter == m.end( ) ) // NOT FOUND cout << "The map m doesn't have an element with a key of 4." << endl; else // FOUND cout << "The element of map m with a key of 4 is: " << m_iter -> second << endl;

The element of map m with a key of 2 is: 20The map m doesn't have an element with a key of 4.

출력 결과 :

Page 15: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

erase() 멤버함수

erase() 함수 : Map 에서 요소를 삭제하는 함수 void erase(iterator); // iterator 가 참조하는 요소를 삭제 void erase(iterator first, iterator last); // first 부터 last

앞까지 삭제 size_type erase(Key); // 해당 Key 값의 요소를 삭제 ;

삭제된 개수를 리턴

15

map<int, int> m; map<int, int>::iterator pIter, Iter1, Iter2; int i; map<int, int>::size_type n; for (i = 1; i < 10; i++) m.insert(make_pair(i, i)); Iter1 = ++m.begin(); m.erase(Iter1); n = m.erase ( 3 );

for (pIter = m.begin(); pIter != m.end(); pIter++) cout << " " << pIter->second; cout << endl;

Iter1 = ++m.begin(); Iter2 = --m.end(); m.erase(Iter1, Iter2); for (pIter = m.begin(); pIter != m.end(); pIter++) cout << " " << pIter->second; cout << endl;

1 4 5 6 7 8 9

출력 결과 :

1 9

Page 16: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

7.2 단어개수 세기

입력에 각 단어가 몇 번씩 나오는지를 세는 프로그램 벡터를 이용했을 때 .. 꽤 복잡하네 .

16

struct word_frequency { string word; int freq;};

vector<word_frequency> words;

int main() { string s; while(cin>>s) { s 가 vector 속에 있는지 찾아보고 , 있다면 그것에 해당하는 freq 를 하나 증가시키고 없다면 새로 요소를 추가하고 … }

결과를 출력한다 .}

Page 17: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

단어개수 세기 – map 이용

map 을 사용하면… 신기할 정도로 간단하네

17

#include <iostream>#include <map>#include <string> using namespace std;int main() {

string s;map<string, int> counters; // store each word and an associated counter

// read the input, keeping track of each word and how often we see itwhile (cin >> s)

++counters[s];// write the words and associated countsfor (map<string, int>::const_iterator it = counters.begin(); it != counters.end(); ++it) {

cout << it->first << "\t" << it->second << endl;}return 0;

}

Page 18: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

단어 개수 세기 : 분석

map<string, int> counters; counters 를 map 으로 정의 . “string 에서 int 로의 map” 키 : string 타입 , 연관된 값 : int 타입

++counters[s]; 키로 읽어들인 단어 s 를 counters 에서 찾는다 counters[s] 는 s 에 저장된 string 에 연관된 정수를 리턴한다 . ++ 를 사용하여 , 저장된 정수 값을 증가시킨다 .

값지정 - 초기화 (value-initialzed) int 와 같은 단순한 타입의 경우에는 0 으로 초기화 된다 .

처음 나타난 단어는 0 으로 초기화 된 상태에서 , 증가시키므로 , 1

cout << it->first << "\t" << it->second << endl; it->first : 현재 요소의 키 it->second : 키와 연관된 값

18

Page 19: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

7.3 교차 - 참조 테이블 생성 단어가 입력의 어느 라인에서 나타나는지를 저장하는 교차 -

참조 테이블 (cross-reference table) 을 생성하는 프로그램 한번에 한 라인을 읽어 들여서 , 라인번호와 연관 짓는다 . 단어 대신 라인을 읽으므로 , 단어별로 분리하는 작업이 필요

5.6 절의 split() 함수 이용 교차 - 참조함수 xref() 에 대한 매개변수로 split 함수를

이용 라인 상에서 단어 찾는 방식을 다른 함수로 바꿀 수 있도록 .

string 에서 vector<int> 로의 map 을 이용 : 키 : 개별 단어 연관된 값 : 단어가 나타난 라인 번호들을 저장 map<string, vector<int> > ret;

19

Page 20: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

20

// find all the lines that refer to each word in the inputmap<string, vector<int> > xref(istream& in, vector<string> find_words(const string&) = split){

string line;int line_number = 0;map<string, vector<int> > ret;

// read the next linewhile (getline(in, line)) { ++line_number;

// break the input line into words vector<string> words = find_words(line);

// remember that each word occurs on the current line

for (vector<string>::const_iterator it = words.begin(); it != words.end(); ++it)

ret[*it].push_back(line_number);}return ret;

}

입력 예 :aaa bbb cccddd aaa ccceee aaa

저장 예 :aaa 1 2 3

bbb 1

ccc 1 2

ddd 2

eee 3

Page 21: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

xref() 함수 분석

map<string, vector<int> > ret; 리턴 타입과 지역변수 선언에서 > > 를 사용 . (>> 아님 ) 컴파일러가 이해하기 위해 공백 (space) 가 필요하다 .

>> 연산자와 구분할 수 있도록 !!

xref(istream& in, vector<string> find_words(const string&) = split)

인자 목록에서 find_word() 함수를 매개변수로 정의하고 있다 . 기본인자 (default argument) 를 지정 : = split

호출하는 곳에서 매개변수를 생략하면 , 기본인자를 이용 매개변수를 명시한다면 , 그 인자를 사용xref(cin); // 입력스트림에서 단어들을 찾기 위해 split 함수를

이용

xref(cin, find_urls); // ” find_urls함수를 이용

21

Page 22: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

xref() 함수 분석

22

find_words() 함수 split 함수일 수 도 있고 , string 인자를 받고

vector<string> 을 리턴하는 다른 함수도 가능하다 . split 함수 : line 을 각 단어별로 쪼개는 함수 ( 책 p152 이용 )

ret[*it].push_back(line_number); words 의 각 요소를 순차적으로 순회하면서 , 단어에 해당하는

라인번호를 벡터에 추가 . it: 벡터 words 의 한 요소 . *it: 단어 for (vector<string>::const_iterator it = words.begin(); it != words.end(); +

+it) ret[*it].push_back(line_number); // string s=*it;

// ret[s].push_bak(line_number);

이 처음 나오는 단어라면 , vector<int> 는 값지정 초기화 된다 . 즉 , 빈 벡터가 생성된다 .

Page 23: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

main() 함수 : 간단한 출력형식으로

23

int main(){ // call `xref' using `split' by default map<string, vector<int> > ret = xref(cin);

// write the results for (map<string, vector<int> >::const_iterator it = ret.begin(); it != ret.end(); ++it) { // write the word cout << it->first << “: ";

// followed by one or more line numbers for(vector<int>::const_iterator line_it = it->second.begin();

line_it != it->second.end(); ++line_it) { cout << *line_it << “ ”;

// write a new line to separate each word from the next cout << endl; } return 0;}

출력 예 :aaa: 1 2 3bbb: 1ccc: 1 2ddd: 2eee: 3

입력 예 :aaa bbb cccddd aaa ccceee aaa

저장 예 :aaa 1 2 3

bbb 1

ccc 1 2

ddd 2

eee 3

Page 24: 연관 컨테이너 사용하기   USING ASSOCIATIVE CONTAINERS

main() 함수 :

24

int main(){ // call `xref' using `split' by default map<string, vector<int> > ret = xref(cin);

// write the results for (map<string, vector<int> >::const_iterator it = ret.begin(); it != ret.end(); ++it) { // write the word cout << it->first << " occurs on line(s): ";

// followed by one or more line numbers vector<int>::const_iterator line_it = it->second.begin(); cout << *line_it; // write the first line number

++line_it; // write the rest of the line numbers, if any while (line_it != it->second.end()) {

cout << ", " << *line_it;++line_it;

} // write a new line to separate each word from the next cout << endl; } return 0;}

출력 예 :aaa occurs on line(s): 1, 2, 3bbb occurs on line(s): 1ccc occurs on line(s): 1, 2ddd occurs on line(s): 2eee occurs on line(s): 3