연관 컨테이너 사용하기 USING ASSOCIATIVE CONTAINERS
-
Upload
bruno-lott -
Category
Documents
-
view
50 -
download
0
description
Transcript of 연관 컨테이너 사용하기 USING ASSOCIATIVE CONTAINERS
연관 컨테이너 사용하기
USING ASSOCIATIVE CONTAINERS
Chapter 7
7 장에서는…
지금까지 순차컨테이너 (sequential container): vector, list 순차컨테이너의 요소들은 우리가 선택한 순서대로 유지된다
7 장에서는 순차컨테이너를 이용하는 경우 비효율적인 경우가 있다 . 예 : 컨테이너 요소에 정수 42 가 포함되어있는지 확인하는
문제 방법 1) 컨테이너의 시작부터 끝까지 검사 방법 2) 적당한 순서대로 저장하여 원하는 요소를
효과적으로 찾을 수 있는 알고리즘 이용
효과적인 참조방법을 제공하는 연관 컨테이너 : map
2
연관 컨테이너 (Associative Containers)
연관 컨테이너의 요소들은 삽입한 순서대로 배열하지 않고 , 요소의 값에 따라 삽입
순서를 자동적으로 조정한다 . 따라서 , 특정 요소들을 더 빨리 검색할 수 있다 .
Maps: 빠른 검색을 위한 연관 컨테이너 연관 자료구조에는 키 - 값 쌍 (key-value pair) 으로 저장 연관 배열 (associative array)
값과 키를 연결시켜서 , 키 (key) 에 의해 빠르게 삽입 , 탐색
효과적인 검색을 위해 사용하는 컨테이너의 요소
3
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
순차 컨테이너와 연관 컨테이너 대부분의 경우 , map 은 vector 처럼 동작한다
map 과 vector 의 기본적인 차이점 : 인덱스 vector: 인덱스가 정수이다 . v[0], v[1], v[2], … map: 다른 것들과 순서를 비교할 수 있는 것이라면 어떤
타입도 가능하다 . string 일 수 있다 .
연관 컨테이너의 중요한 특징은 자체 순서 (self-ordering) 를 유지시킨다 .
입력 순서와 상관없이 키 값의 순서에 따라 저장된다 . 따라서 , 순서를 변경하는 작업은 할 수 없다 .
5
예
각국의 국제전화 코드번호 저장할 정보의 키와 값 쌍 :
< 국가이름 , 국가코드번호 >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
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;
Map 멤버함수
insert() : 요소 삽입 find() : 키를 이용한 검색 erase() : 요소 삭제
begin() end() size() clear() …
8
참조사이트 : http://msdn.microsoft.com/ko-kr/library/xdayte4c(v=VS.90).aspx
연관컨테이너에 삽입 방법
연산자 [] 를 이용하는 방법 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;
#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
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
…
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; }}
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 출력
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.
출력 결과 :
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
7.2 단어개수 세기
입력에 각 단어가 몇 번씩 나오는지를 세는 프로그램 벡터를 이용했을 때 .. 꽤 복잡하네 .
16
struct word_frequency { string word; int freq;};
vector<word_frequency> words;
int main() { string s; while(cin>>s) { s 가 vector 속에 있는지 찾아보고 , 있다면 그것에 해당하는 freq 를 하나 증가시키고 없다면 새로 요소를 추가하고 … }
결과를 출력한다 .}
단어개수 세기 – 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;
}
단어 개수 세기 : 분석
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
7.3 교차 - 참조 테이블 생성 단어가 입력의 어느 라인에서 나타나는지를 저장하는 교차 -
참조 테이블 (cross-reference table) 을 생성하는 프로그램 한번에 한 라인을 읽어 들여서 , 라인번호와 연관 짓는다 . 단어 대신 라인을 읽으므로 , 단어별로 분리하는 작업이 필요
5.6 절의 split() 함수 이용 교차 - 참조함수 xref() 에 대한 매개변수로 split 함수를
이용 라인 상에서 단어 찾는 방식을 다른 함수로 바꿀 수 있도록 .
string 에서 vector<int> 로의 map 을 이용 : 키 : 개별 단어 연관된 값 : 단어가 나타난 라인 번호들을 저장 map<string, vector<int> > ret;
19
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
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
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> 는 값지정 초기화 된다 . 즉 , 빈 벡터가 생성된다 .
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
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