Skip to content

Commit

Permalink
[LinkedList] Add Singly-Linked-Lists
Browse files Browse the repository at this point in the history
  • Loading branch information
deunlee committed May 27, 2020
1 parent 29bf084 commit 0419215
Show file tree
Hide file tree
Showing 7 changed files with 633 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Data-Structure.sln
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTest", "UnitTest\UnitTe
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Deque", "Deque\Deque.vcxproj", "{88D1A79E-BB93-46A3-A9A9-18BED3527596}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LinkedList", "LinkedList\LinkedList.vcxproj", "{57412A8E-41D0-474D-B912-9AAB3EBDA451}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -51,6 +53,14 @@ Global
{88D1A79E-BB93-46A3-A9A9-18BED3527596}.Release|x64.Build.0 = Release|x64
{88D1A79E-BB93-46A3-A9A9-18BED3527596}.Release|x86.ActiveCfg = Release|Win32
{88D1A79E-BB93-46A3-A9A9-18BED3527596}.Release|x86.Build.0 = Release|Win32
{57412A8E-41D0-474D-B912-9AAB3EBDA451}.Debug|x64.ActiveCfg = Debug|x64
{57412A8E-41D0-474D-B912-9AAB3EBDA451}.Debug|x64.Build.0 = Debug|x64
{57412A8E-41D0-474D-B912-9AAB3EBDA451}.Debug|x86.ActiveCfg = Debug|Win32
{57412A8E-41D0-474D-B912-9AAB3EBDA451}.Debug|x86.Build.0 = Debug|Win32
{57412A8E-41D0-474D-B912-9AAB3EBDA451}.Release|x64.ActiveCfg = Release|x64
{57412A8E-41D0-474D-B912-9AAB3EBDA451}.Release|x64.Build.0 = Release|x64
{57412A8E-41D0-474D-B912-9AAB3EBDA451}.Release|x86.ActiveCfg = Release|Win32
{57412A8E-41D0-474D-B912-9AAB3EBDA451}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
225 changes: 225 additions & 0 deletions LinkedList/LinkedList.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#include "LinkedList.h"

namespace Deun {
LinkedList::LinkedList() {
head = tail = NULL;
count = 0;
}

LinkedList::~LinkedList() {
Node* current = head;
Node* next;
while (current) { // 모두 삭제
next = current->link;
delete current;
current = next;
}
}

// 인덱스 유효성 검증
bool LinkedList::isValid(unsigned int index) {
// index는 0부터 시작하므로, count보다 작아야 함
return (index < count);
}

Node* LinkedList::getNode(unsigned int index) {
if (!isValid(index)) {
return NULL;
}
if (index + 1 == count) {
return tail;
}

// index가 유효하다면, current는 항상 NULL이 아님
Node* current = head;
for (unsigned int i = 0; i < index; i++) {
current = current->link;
}
return current;
}

bool LinkedList::isEmpty() {
return (count == 0);
}

unsigned int LinkedList::getCount() {
return count;
}

// 삽입
bool LinkedList::insert(unsigned int index, int data) {
if (index == 0) { // 맨 앞에 삽입
return insertFront(data);
}
else if (index == count) { // 맨 뒤에 삽입
return insertRear(data);
}
else if (!isValid(index)) {
return false;
}

// 여기부터는 index가 1 이상임이 보장됨
Node* preNode = getNode(index - 1);
Node* newNode = new (std::nothrow) Node;
if (!newNode) {
return false; // 메모리 할당 실패
}

// head와 tail 포인터는 수정할 필요가 없음
// 수정이 필요한 경우는 insertFront()와 insertRear()에서 따로 처리함
newNode->data = data;
newNode->link = preNode->link;
preNode->link = newNode;
count++;
return true;
}

bool LinkedList::insertFront(int data) {
Node* newNode = new (std::nothrow) Node;
if (!newNode) {
return false; // 메모리 할당 실패
}

newNode->data = data;
newNode->link = head;
head = newNode;
tail = (tail ? tail : newNode); // 노드가 하나인 경우 tail도 같게 함
count++;
return true;
}

bool LinkedList::insertRear(int data) {
Node* newNode = new (std::nothrow) Node;
if (!newNode) {
return false; // 메모리 할당 실패
}

newNode->data = data;
newNode->link = NULL;
tail->link = newNode;
tail = newNode;
head = (head ? head : newNode); // 노드가 하나인 경우 head도 같게 함
count++;
return true;
}

// 삭제
bool LinkedList::remove(unsigned int index) {
if (index == 0) { // 맨 앞 노드 삭제
return removeFront();
}
else if (!isValid(index)) {
return false;
}

// 여기부터는 index가 1 이상임이 보장됨
Node* preNode = getNode(index - 1);
Node* curNode = preNode->link;
preNode->link = curNode->link;
delete curNode;
count--;
if (!preNode->link) { // 삭제된 노드가 마지막 노드이면 tail 변경
tail = preNode;
}
return true;
}

bool LinkedList::removeFront() {
if (!head) {
return false;
}

Node* next = head->link;
delete head;
head = next;
tail = (head ? tail : NULL); // 노드가 하나도 없는 경우 tail도 NULL로 변경
count--;
return true;
}

bool LinkedList::removeRear() {
// tail의 앞 노드를 저장해두지 않았기 때문에 O(1)로 삭제할 수 없음
return remove(count - 1);
}

// 조회
int LinkedList::get(unsigned int index) {
Node* current = getNode(index);
if (!current) {
throw LinkedListError::NODE_NOT_FOUND;
}
return current->data;
}

int LinkedList::getFront() {
if (!head) {
throw LinkedListError::NODE_NOT_FOUND;
}
return head->data;
}

int LinkedList::getRear() {
if (!tail) {
throw LinkedListError::NODE_NOT_FOUND;
}
return tail->data;
}

// 수정
bool LinkedList::update(unsigned int index, int data) {
Node* current = getNode(index);
if (!current) {
return false;
}
current->data = data;
return true;
}

bool LinkedList::updateFront(int data) {
if (!head) {
return false;
}
head->data = data;
return true;
}

bool LinkedList::updateRear(int data) {
if (!tail) {
return false;
}
tail->data = data;
return true;
}

// 검색
unsigned int LinkedList::find(int data) {
if (!head) {
throw LinkedListError::NODE_NOT_FOUND;
}

Node* current = head;
for (unsigned int i = 0; i < count; i++) {
if (current->data == data) {
return i;
}
current = current->link;
}
throw LinkedListError::NODE_NOT_FOUND;
}

// 출력
void LinkedList::print() {
using namespace std;

cout << "LinkedList(count=" << count;
cout << ", head=0x" << head << ", tail=0x" << tail << ")" << endl;

cout << "head -> ";
Node* current = head;
for (unsigned int i = 0; i < count; i++) {
cout << i << "=" << current->data << "(0x" << current << ") -> ";
current = current->link;
}
cout << "NULL" << endl;
}
}
78 changes: 78 additions & 0 deletions LinkedList/LinkedList.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#ifndef __DEUN_LINKED_LIST__
#define __DEUN_LINKED_LIST__

#include <iostream>
#include <new>

namespace Deun {
enum class LinkedListError {
NODE_NOT_FOUND = 1000,
};

typedef struct Node {
int data;
struct Node* link;
} Node;

// 단순 연결 리스트
class LinkedList {
private:
Node* head;
Node* tail;
unsigned int count;

bool isValid(unsigned int index);
Node* getNode(unsigned int index); // O(n)

public:
LinkedList();
~LinkedList();

bool isEmpty();
unsigned int getCount();

// index: 0-based

bool insert(unsigned int index, int data); // 특정 위치에 노드 삽입 (성공 = true, 실패 = false)
bool insertFront(int data); // 맨 앞에 노드 삽입 (성공 = true, 실패 = false)
bool insertRear(int data); // 맨 뒤에 노드 삽입 (성공 = true, 실패 = false)

bool remove(unsigned int index); // 특정 위치 노드 제거 (성공 = true, 실패 = false)
bool removeFront(); // 맨 앞 노드 제거 (성공 = true, 실패 = false)
bool removeRear(); // 맨 뒤 노드 제거 (성공 = true, 실패 = false)

int get(unsigned int index); // 특정 위치 노드 반환 (실패 = throw)
int getFront(); // 맨 앞 노드 반환 (실패 = throw)
int getRear(); // 맨 뒤 노드 반환 (실패 = throw)

bool update(unsigned int index, int data); // 특정 위치 노드 수정 (성공 = true, 실패 = false)
bool updateFront(int data); // 맨 앞 노드 수정 (성공 = true, 실패 = false)
bool updateRear(int data); // 맨 뒤 노드 수정 (성공 = true, 실패 = false)

unsigned int find(int data); // 주어진 값과 일치하는 첫 번째 노드의 인덱스 반환 (실패 = throw)
void print(); // 현재 상태 출력

// 시간 복잡도

// insert() insertFront() insertRear()
// O(n) O(1) O(1)

// remove() removeFront() removeRear()
// O(n) O(1) O(n)

// get() getFront() getRear()
// O(n) O(1) O(1)

// update() updateFront() updateRear()
// O(n) O(1) O(1)

// find()
// O(n)

// removeRear()가 O(n)인 이유는 tail의 앞 노드를 알 수 없기 때문임
// tail의 link는 사용하지 않으므로, link를 tail의 앞 노드로 설정하면
// O(1)에 삭제가 가능하게 바꿀 수 있을 것 같음 (TODO?)
};
}

#endif
Loading

0 comments on commit 0419215

Please sign in to comment.