본문 바로가기

별걸다하는 IT/기타IT

[자료구조 STL C++] list 사용법, 리스트 선언하고 초기화하는 법 알아보기, list 대소비교 사용가능한 연산자

안녕하세요 양햄찌블로거 입니다.

 

오늘은 C++ 자료구조 컨테이너 중 하나인 list에 대해 살펴보려고 합니다.

이전 포스팅에서 설명드린 적이 있긴 하지만, STL에 지정된 list는 양방향 연결리스트 형태(double linked list)로 구현되어 있어요. 

 

이전 포스팅에서 vector에 대해 다뤘었는데요~! vector를 먼저 알고 오면 유사한 메서드가 많기 때문에 더 쉽게 이해할 수 있답니다.

 

▼자료구조 vector : jhnyang.tistory.com/230

 

[자료구조STL vector 1탄]벡터란? 배열 vs 벡터 비교/장단점/ 특징, 다양한 백터 선언 및 초기화 방법,

안녕하세요 오늘은 C++ 자료구조 컨테이너 중 하나인 vector 라이브러리에 대해 살펴봅시다. [1탄 목차] 1. 벡터란 무엇인가? 2. 벡터의 구조와 특징 (장단점) 3. 언제 벡터를 사용하는가 4. 벡터를 사

jhnyang.tistory.com

리스트 LIST란 무엇인가? 간단요약

이전 포스팅에서 살펴봤던 vector 처럼 데이터를 저장하기 위한 일종의 자료구조예요.

vector, list외에도 stack, queue, heap, graph 등등.. 다양한 자료구조가 존재하는대요.

 

다수의 데이터를 저장할 수 있는 vector가 있음 그걸 쓰면 되지, 왜 리스트와 같은 또 다른 자료구조가 필요하냐고요?

자료를 저장하는 방식에 따라 장단점이 달라지기 때문입니다. A방식은 데이터를 추가하는데 성능이 좋은데, B방식은 검색하는데 성능이 좋고~ 모두 다 좋으면 좋겠지만 그럴 수 없기에 내가 구현하고자 하는 프로그램의 목적에 맞춰서 효율적으로 데이터를 사용하기 위함이예요.

 

LIST 구현 방법에도 여러가지가 있는데, STL 라이브러리에 있는 LIST는 양방향링크 방식으로 구현되어있어요.

 

이미지 출처: https://www.educative.io/blog/data-structures-linked-list-java-tutorial

LIST 자료구조의 특징은 다른 포스팅에서 살펴보고,

오늘은 이론적인 설명보다는 STL에서 제공하는 list 클래스의 멤버함수 사용법을 살펴보려고 합니다.

리스트를 사용하기 위한 헤더 

리스트(LIST)는 표준템플릿 라이브러리(STL)인데요, STL에서 제공되는 컨테이너는 일반적으로 사용하려고 하는 컨테이너 이름의 헤더파일명을 갖고 있습니다. 

#include <list>

고래서 이렇게 써주면 됌!

 

리스트 선언하고 초기화하는 법

▶기본 선언문

리스트 컨테이너의 기본적인 선언 문법은 아래와 같습니다.

 

list<자료형> [변수이름];

 

//빈 리스트를 생성한다.
list<int> list1;
list<char> list2;
list<string> list3; 

배열도 int array[5]이런식으로 int 타입이 될 수도 있고

char array[5];와 같이 char타입으로 만들 수 있듯이

list도 꺽새 안에 만드려는 리스트의 자료형을 넣어 선언해주면 됩니다. (템플릿 문법~)

 

▶생성자를 이용한 디폴트 선언 초기화

 

list<자료형> [변수이름](개수);

 

list<char> list1(5);
list<int> list2(7);
list<double> list3(3);

이렇게 선언을 하시면 개수의 공간만큼 디폴트 값으로 채워진채 list가 생성됩니다.

 

#include <iostream>
#include <list>
using namespace std;
int main()
{
	int n = 4;
	list<double> list_double(n);
	list<char> list_char(3);

	for (double val : list_double)
		cout << val << ",";
	cout << endl;

	for (char val : list_char)
		cout << val << ",";
	cout << endl;
	return 0;
}

double타입과 char 타입의 list를 각각 선언 후 디폴트값이 어떻게 들어가있는지 확인해봤습니다.

double의 디폴트값은 0

char의 디폴트값은 ' '이네요.

 

▶생성자를 이용해 특정 공간의 개수, 특정 값 지정해 초기화하기

 

list<자료형> [변수이름](개수, 값);

 

#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> list_int(4, 3);
	
	for (int val : list_int)
		cout << val << ",";
	cout << endl;
	return 0;
}

결과는 3,3,3,3, 이 나오겠죠??

list의 특징 중 하나는, 저렇게 향상된 for문이나 이터레이터를 이용해서 처음부터 끝까지 값을 출력하는건 되는데

for(int i=0; i<list_int.size(); i++) -> 이와 같은 전형적인 for문으로 데이터를 뽑을수는 없습니다.

왜냐면 각 원소 index에 직접 접근할 수 있는 vector와는 달리, list는 앞뒤 원소끼리 연결되어 있는 형태로 특정 지점의 요소에 적집 접근이 불가능하기 때문이예요~

 

▶생성자를 이용해 특정 공간의 개수, 특정 값 지정해 초기화하기

list<타입> [변수이름]{...채울 값...};

 

#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> list_int{ 2,3,4,5 }; // C++11 이상 버전부터
	for (int val : list_int) cout << val << " ";
	cout << endl;
	return 0;
}

C++11 부터는 '{...}' 문법을 이용해서 리스트를 초기화할 수 있습니다.

짠~

▶멤버함수 assign을 이용해서 특정 공간의 개수에 특정 값 할당하기

[리스트변수명].assign(개수, 값);

 

생성자를 이용해서 선언과 초기화를 한 번에 할수도 있지만, 일단 선언을 한 뒤에, assign 함수를 이용해서 초기화할 수 있습니다.

#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> list1;
	list1.assign(3, 4);
	for (int val : list1)
		cout << val << " ";
	cout << endl;
	return 0;
}

list1을 선언한뒤 3개의 공간에 4를 채우도록 했습니다. 

▶선언하고 push_back 함수 이용해 값 집어넣기 

push_back은 맨 뒤에 이어서 값을 넣어주는 멤버함수입니다.

#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> list_int{ 2,3,4,5 }; // C++11 이상 버전부터
	list_int.push_back(10);
	list_int.push_back(11);
    
	for (int val : list_int) 
    		cout << val << " ";
	cout << endl;
	return 0;
}

기존에 2,3,4,5가 들어가 있었는데 여기에 push_back 멤버함수를 이용해서 10,11을 추가적으로 넣어줬어요.

뒤에 잘 들어가서 출력된 것을 확인할 수 있습니다.

 

리스트 복사하기  - LIST COPY

▶기본 복사생성자 사용해서 복사하기

list<타입> [복사한걸저장하려는리스트명](복사하려는리스트명)

 

#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> list_original{ 2,3,4,5 }; // C++11 이상 버전부터
	list<int> list_new(list_original);
	for (int val : list_new) 
		cout << val << " ";
	cout << endl;
	return 0;
}

클래스 복사생성자 특징을 이용해서 이렇게 선언시 함께 복사할 수 있습니다.

list_original 을 복사해서 list_new를 만들어 출력해봤습니다. 당근 결과는 같죠~

 

▶대입 연산자 이용해서 복사하기 

list<타입> [복사한걸저장하려는리스트명] = [기존에존재하는복사할리스트명]

 

#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> list_original{ 2,3,4,5 }; // C++11 이상 버전부터
	list<int> list_new(list_original);  //복사생성자를 이용해서 복사
	list<int> list_opercopy = list_new; // 대입연산자 이용해서 복사
	for (int val : list_opercopy)
		cout << val << " ";
	cout << endl;

	return 0;
}

대입연산자라고 하면 '='을 의미하죠~! 

대입연산자로 대입해서 출력해보면 복사생성자를 이용해서 복사했을때와 동일한 결과를 출력하는 것을 알 수 있습니다.

 

▶iterator 반복자 사용해서 복사하기 

STL은 iterator를 공통적으로 지원해주는데요, 이를 사용해서 복사할수있습니다 

#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> list_original{ 2,3,4,5 }; // C++11 이상 버전부터
	list<int> list_new(list_original);  //복사생성자를 이용해서 복사
	list<int> list_opercopy = list_new; // 대입연산자 이용해서 복사
	list<int> list_itercopy(list_opercopy.begin(), list_opercopy.end()); // 이터레이터 이용해서 복사 
	for (int val : list_itercopy)
		cout << val << " ";
	cout << endl;

	return 0;
}

begin 멤버함수는 맨 앞의 원소를 가리키는 iterator를 반환하고

end는 마지막 원소 다음을 가리키는 iterator를 반환합니다. (그니까 끝을 가리키는) 

 

벡터의 경우, 이터레이터를 사용해서 복사할 경우 어디부터 어디까지를 복사해 넣을건지 지정할 수 있었어요.

vector<int> vector1{ 1,2,3,4,5,6,7,8,9,10 };
vector<int> vector2(vector1.begin()+1, vector1.begin()+4);

이렇게 하면 vector1의 2번째 원소부터 4번째 원소까지 복사된 vector2가 생성되었죠!

하지만!! list의 경우 처음 끝이 아닌 중간 요소에 직접 접근이 불가능하기 때문에 이와같이 중간잘라 복사하기가 안됩니다.

가능한 연산자 대소비교

list1 == list2 list1과 list2의 원소가 같은가
list1 != list2 list1과 list2의 모든 원소 중 하나라도 다른 원소가 있는가
list1 = list2 list1list2를 복사 
list[i] 리스트에서는 사용 불가능!! list의 인덱스 i에 있는 원소를 참조한다.  (불가능)
list1 > list2 list1의 첫번째 값이 list2의 첫번째 값보다 크다 
list1 >= list2 list1의 첫번째 값이 list2의 첫번째 값보다 크거나 같다
list1 < list2 list1의 첫번째 값list2의 첫번째 값보다 작다
list1 <= list2  list1의 첫번째 값이 list2의 첫번째 값보다 작거나 같다

 

#include <iostream>
#include <list>
using namespace std;
int main()
{
	list<int> list1{ 2,3,4 };
	list<int> list2{ 4,1 };
	cout << (list1 < list2) << endl; // true
	return 0;
}

첫 번째 원소가 list2가 크므로 당연히 출력값은 true가 됩니다~

 

오늘 포스팅은 여기까지~ 선언 초기화에 해당하는 문법과 멤버함수만 일단 먼저 간단하게 알아봤어요.

이 외에 남은 멤버함수들은 다음 포스팅에서 이어 진행하도록 해요.

공감은 큰 힘이 됩니다!!