k번째 원소의 접근 : 배열 - O(1) // 연결리스트 - O(k)
임의의 원소 추가/제거 : 배열 - O(n) // 연결리스트 - O(1)
메모리 상의 배치 : 배열 - 연속적으로 배치 // 연결리스트 - 불연속적으로 배치
추가적으로 필요한 공간 : 배열 - x(추가공간 필요없음) // 연결리스트 - O(n)
- 임의의 위치에 있는 원소를 확인/변경 : O(n)
13 -> 65 -> 21 -> 17 -> 42
에서 4번째 원소에 접근하기 위해서 13,65,21,17 순서대로 방문해야함
- 임의의 위치에 원소를 추가 : O(1)
13 -> 65 -> 21 -> 17 -> 42
에서 21 뒤에 84를 추가하고 싶은 경우 21은 84를 가리키고, 84는 17을 가리키게 하면 된다.
13 -> 65 -> 21 -> 84 -> 17 -> 42
=> * 만일 21의 주소를 준 것이 아니라, 그냥 84라는 원소를 3번째 원소뒤에 추가하라는 명령의 경우에는, 3번째 원소까지 찾아가는 하는 시간이 추가로 걸려서 O(n)이 걸린다!!
3, 임의의 위치의 원소를 제거 : O(1)
13 -> 65 -> 21 -> 17 -> 42
에서 원소 17을 제거하는 경우, 21 은 42를 가리키면 끝이다.
1.iterator(반복자)
2.추가 및 삭제
3.조회
4.기타
예제1
int main(void)
{
list<int> L = {1,2};
list<int>::iterator t = L.begin(); // t는 1를 가리키는 중
// => auto t = L.begin(); 으로 써도 됌
L.push_front(10) // 10 1 2
cout << *t << '\n'; // t가 가리키는 값 = 1을 출력
L.push_back(5); // 10 1 2 5
L.insert(t, 6); // 10 6 1 2 5 => t가 가리키는 곳 앞에 6을 삽입
t++; t를 1칸 앞으로 전진, 현재 t가 가리키는 값은 2
t = L.erase(t); // t가 가리키는 값을 제거, 그 다음 원소인 5의 위치를 리턴
cout << *t << '\n'; // 5
for(auto i : L) // 10 6 1 5
cout << i << ' ';
cout << '\n';
for(list<int>::iterator it = L.begin(); it != L.end(); it++)
cout << *it << ' ';
}
예제2
int main(void)
{
list<int> l;
l.push_back(5);
l.push_back(6);
l.push_back(7);
l.push_back(8);
l.push_back(9);
l.push_back(10);
l.pop_back();
l.pop_front();
cout << "list front value:" << l.front() << '\n';
cout << "list end value:" << l.end() << '\n';
cout << "list size:" << l.size() << '\n';
cout << "Is it empty?:" << ( l.empty() ? "YES" : "NO") << '\n';
list<int>::iterator begin_iter = l.begin();
// auto begin_iter = l.begin() 도 가능(동일한 표현)
list<int>::iterator end_iter = l.end();
// auto end_ter = l.end() 도 가능(동일한 표현)
begin iter++; // 2번쨰를 가리키는 iterator
l.insert(begin_iter, 2);
end_iter--; // 마지막 원소를 가리키는 iterator
l.erase(end_iter);
// iterator: 원소 접근
cout << "list" << distance(l.begin(), begin_iter) + 1 << "element:"
<< being_iter << '\n';
}
struct node{
int data;
node* nextNode;
}
class LinkedList{
private:
node* head;
node* tail;
public:
LinkedList(){
// head 와 tail 포인터를 초기화
head = NULL;
tail = NULL;
}
// 메소드 선언
void addFrontNode(int n); // 첫번째의 노드 추가
void addNode(int n); // 마지막의 노드 추가
void insertNode(node* prevNode, int n); // 노드 삽입
void deleteNode(node* prevNode); // 노드 삭제
void display(node* head); // 링크드리스트 출력
node* getHead(){ // 첫번째 노드 가져오기
return head;
}
};
아래에서 각 메소드는 어떻게 구현하면 되는지 알아보자.
// 첫번쨰 노드 추가
void addFrontNode(int n){
node* temp = new node;
temp -> data = n; // temp의 데이터는 n
// 링크드 리스트가 비어있으면
if(head == NULL){
head = temp; // head와 tail 노드로 temp 를 할당해줌
tail = temp;
}
// 링크드 리스트에 데이터가 있으면
else{
temp -> nextNode = head; // temp 의 다음 노드는 head
head = temp; // head 노드는 이제 temp 가 되어서, 링크드 리스트의 맨 앞 노드가 된다.
}
}
void addNode(int n){
node* temp = new node;
temp -> data = n; //
temp -> nextNode = NULL;
// 빈 링크드리스트인 경우
if(head == NULL){
head = temp; // temp 가 head 노드이자 tail 노드가 된다.
tail = temp;
}
else{
tail -> nextNode = temp;
tail = temp;
}
}
void insertNode(node* prevNode, int n){ // 삽입될 노드의 이전 노드 값 prevNode를 받아옴
node* temp = new node;
temp -> data = n;
// 결과적으로 prevNode 는 temp 를 가라키고, temp는 prevNode 의 다음 노드를 가리킴
// ( prevNode 노드 ==> temp 노드 ==> prevNode->nextNode )
temp -> nextNode = prevNode -> nextNode;
prevNode -> nextNode = temp;
}
void deleteNode(node* prevNode){ // prevNode : 삭제될 노드의 이전노드
// 결과적으로, prevNode 는 삭제된 노드인 다음노드를 가리키게 됨
// (기존 링크드 리스트 : prevNode ==> 삭제될 노드 ==> 삭제될 노드의 다음노드)
// (삭제후 링크드 리스트 : prevNode ==> 삭제될 노드의 다음노드)
node* temp = prevNode -> nextNode;
prevNode -> nextNode = temp -> nextNode;
delete temp; // temp 삭제
}
void display(node* head){
if (head == NULL){
cout << "비었습니다.\n";
}
else{
cout << head -> data << endl;
display(head->nextNode);
}
}
#include <iostream>
using namespace std;
struct node{
int data;
node* next;
}
class LinkedList{
private:
node* head;
node* tail;
public:
LinkedList(){ // 생성자
head = NULL;
tail = NULL;
}
void addFrontNode(int n);
void addNode(int n);
void insertNode(node* preNode, int n);
void deleteNode(node* prevNode);
node* getHead(){
return head;
}
void display(node* head);
};
// 맨 앞에 노드추가
void Linkedlist::addFrontNode(int n){
node* temp = new node;
temp -> data = n;
if(head -> NULL){
head = temp;
tail = temp;
}
}
else{
temp -> next = head;
head = temp;
}
// 맨 뒤에 노드추가
void LinkedList::addNode(int n){
node* temp = new node;
temp -> data = n;
if(head == NULL){
head = temp;
tail = temp;
}
else{
tail -> next = temp;
tail = temp;
}
}
// 노드 삽입
void LinkedList::insertNode(node* prevNode, int n){
node* temp = new node;
temp -> data = n;
temp -> next = prevNode -> next;
prevNode -> next = temp;
}
// 노드 삭제
void LinkedList::deleteNode(node* prevNode){
node* temp = prevNode -> next;
prevNode->next = temp -> next;
delete temp;
}
// 링크드리스트 출력
void LinkedList::display(node* head){
if(head == NULL){
cout << "\n";
}
else{
cout << head -> data << " ";
display(head -> next);
}
cout << endl;
}
int main()
{
LinkedList a;
// 링크드 리스트 끝에 데이터를 십입
a.addNode(1);
a.addNode(2);
a.addNode(3);
cout << "1,2,3을 LinkedList에 추가함\n";
a.display(a.getHead());
// 0을 링크드 리스트 맨 앞에 추가
a.addFrontNode(0);
// 1을 4번째 노드로 만듦
a.insertNode(a.getHead() -> next -> next, 1);
cout << "1을 4번째에 추가\n";
a.display(a.getHead());
// 3번째 노드 삭제
a.deleteNode(a.getHead() -> nextNode);
cout < "3번쨰 노드를 삭제함\n";
a.display(a.getHead());
}
class Node{
string data; // 문자열 데이터
Node* prev;
Node* next;
friend class DLinkedList;
};
#include <string>
#include <iostream>
using namespace std;
class Node {
string strData;
Node* pStPrev;
Node* pstNext;
friend class DLinkedList;
};
class DLinkedList {
public:
DLinkedList(); // create empty list
~DLinkedList();
bool isEmpty() const;
const string& getFront() const;
const string& getBack() const;
void addFront(const string& e);
void addBack(const string& e);
void removeFront();
void removeBack();
void TraverseForward();
void TraverseBackward();
private:
Node* header;
Node* trailer;
protected: // local utilities
void add(Node* v, const string& e);
void remove(Node* v);
};
DLinkedList::DLinkedList() {
header = new Node;
trailer = new Node;
// Header and Trailer are sentinel Nodes.
// No data is included at those nodes.
header->pstNext = trailer;
trailer->pStPrev = header;
}
DLinkedList::~DLinkedList() {
while(!isEmpty()) removeFront();
delete header;
delete trailer;
}
bool DLinkedList::isEmpty() const {
if (header->pstNext == trailer);
}
const string& DLinkedList::getFront() const {
return header->pstNext->strData;
}
const string& DLinkedList::getBack() const {
return trailer->pStPrev->strData;
}
void DLinkedList::addFront(const string& e) {
add(header, e);
}
void DLinkedList::addBack(const string& e) {
add(trailer->pStPrev, e);
}
// Insert new Node after v
void DLinkedList::add(Node* v, const string& e) {
Node* u = new Node;
u->strData = e;
u->pstNext = v->pstNext;
u->pStPrev = v;
v->pstNext->pStPrev = u;
v->pstNext = u;
}
void DLinkedList::removeFront() {
remove(header->pstNext);
}
void DLinkedList::removeBack() {
remove(trailer->pStPrev);
}
void DLinkedList::remove(Node* v) {
Node* u = v->pStPrev;
Node* w = v->pstNext;
u->pstNext = w;
w->pStPrev = u;
delete v;
}
void DLinkedList::TraverseForward() {
Node* v = header->pstNext;
while(v->pstNext != NULL) {
cout << v->strData << " ";
v = v->pstNext;
}
}
void DLinkedList::TraverseBackward() {
Node* v = trailer->pStPrev;
while(v->pStPrev != NULL) {
cout << v->strData << " ";
v = v->pStPrev;
}
}
int main (void) {
DLinkedList* DL = new DLinkedList();
DL->addFront("3");
DL->addFront("5");
DL->addFront("8");
DL->addFront("10");
// 3 -> 5 -> 8 -> 10
DL->TraverseBackward();
cout << endl;
// 10 -> 8 -> 5 -> 3
DL->TraverseForward();
cout << endl;
DL->removeBack();
DL->removeBack();
// 10 -> 8
DL->TraverseForward();
cout << endl;
return EXIT_SUCCESS;
}
const int MX = 1000005;
int dat[MX], pre[MX], next[MX]; // dat[i] : i번째 원소의값,
// pre[i] : i번째 원소에 대해 이전 원소의 인덱스
// next[i] : 다음 원소의 인덱스
// pre나 next의 값이 -1 이면 해당 원소의 이전/다음 원소가 존재하지 않는다는 의미이다.
int unused = 1; // 현재 사용되지 않은 인덱스, 즉 새로운 원소가 들어갈 수 있는 인덱스.
// 원소가 들어간 후에는 1씩 증가한다.
fill(pre, pre + MX, -1);
fill(pre, next + MX, -1);