본문 바로가기
학교생활!

[230414] 자료구조 과제4: LinkedList클래스

by Daybreak21 2023. 4. 17.

자료구조 6주차 : 포인터(동적메모리)와 연결 리스트

교수님이 제시해주신 스켈레톤 코드를 채워넣는 과제였다. 

 

※문제설명

  • 우리에게 Node 클래스와 LinkedList 클래스가 주어졌다
  • 기본적인 이론 지식을 기반으로 Node와 LinkedList의 기본 연산들을 구현하라
  • 이와 더불어 추가 적인 연산을 하는 멤버 함수들인 reverse()와 mergeTwoLists()를 구현하라.

※구현할 코드  

Node 클래스

더보기

⦁ Node* 형 link (기본 제공)
⦁ int 형data (기본 제공)
⦁ 기본 생성자
⦁ Node* getLink(): 객체(Node)의 link 변수가 point하는 객체(메모리 주소)를 반환한다.
⦁ void setLink(Node* next): 객체(Node)의 link를 매개변수 next로 설정한다.
⦁ void display(): 객체(Node)의 data를 출력한다. (예: <10> / 맨 마지막 출력화면을 참고).
⦁ int getData(): 객체(Node)의 data를 반환한다.
⦁ void insertNext(Node* n): 매개변수 n을 객체(Node)의 다음 노드(link)로 설정한다.
⦁ Node* removeNext(): 객체(Node)의 다음 노드(link)를 삭제한다.

 

OutputPrintStack 클래스

더보기

⦁ Node형 org (기본 제공)
⦁ 기본 생성자 및 소멸자
⦁ void clear(): 객체(LinkedList)가 공백상태가 될 때까지 Node들을 삭제한다.
⦁ Node* getHead(): 제일 처음 노드를 반환한다.
⦁ bool isEmpty(): 객체(LinkedList)가 공백상태인지 확인한다. 공백상태이면 true 아니면 false를 리턴한다.
⦁ Node* getEntry(int pos): 객체(LinkedList)에서 pos번째 Node를 반환한다.
⦁ void insert(int pos, Node* n): 객체(LinkedList)에서 pos 번째에 매개변수 n을 삽입(추가)한다.
⦁ Node* remove(int pos): 객체(LinkedList)에서 pos번째 Node를 삭제한다.
⦁ int size(): 객체(LinkedList)의 사이즈(총 Node 수)를 반환한다.
⦁ void display(const char* str = “List” ): 객체(LinkedList)의 Node데이터를 출력한다. (맨 마지막 출력화면을 참고).
void reverse(): 객체(LinkedList)를 역순으로 바꾼다.
LinkedList* mergeTwoLists(LinkedList* list2): 객체(LinkedList)와 매개변수 list2의 Node들을 순회하며 노드의 data가 큰 순서대로 정렬된 별도의 LinkedList를 반환한다

 

 

※출력 화면


  • 전체 코드
더보기
//Visual Studio: 프로젝트 -> 속성 -> C/C++ -> 언어 -> 준수모드: 아니요(/permissive)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;


inline void error(char* str) {
	fprintf(stderr, "%s\n", str);
	exit(1);
};

class Node {
	Node* link; // pointer to next node
	int		data; // data field of 'this' node

public:
	Node(int val = 0) : data(val), link(NULL) { }
	Node* getLink() { return link; }
	void setLink(Node* next) { link = next; }
	void display() { cout << " <" << data << ">"; }
	int getData() { return data; }
	void setData(int n) { data = n; } //Data swap을 위해 정의한 함수

	// insert new node (n) next to current node
	void insertNext(Node* n) { 
		if (n != NULL) {
			n->link = link;
			link = n;
		}
	}

	// remove next node from current node
	Node* removeNext() { 
		Node* removed = link;
		if (removed != NULL) link = removed->link;
		return removed;
	}
};

class LinkedList {
	Node	org;	// head node (not head pointer!)

public:
	LinkedList() : org(0) { }
	~LinkedList() { clear(); }

	void clear() { while (!isEmpty()) delete remove(0); }
	Node* getHead() { return org.getLink(); }
	bool isEmpty() { return getHead() == NULL; }

	// return node in position 'pos' from the list
	Node* getEntry(int pos) { 
		Node* n = &org;
		for (int i = -1; i < pos; i++, n = n->getLink())
			if (n == NULL) break;
		return n;
	}

	// insert new node (n) in position 'pos'
	void insert(int pos, Node* n) { 
		Node* prev = getEntry(pos - 1);
		if (prev != NULL) prev->insertNext(n);
	}

	// remove a node in position 'pos'
	Node* remove(int pos) { 
		Node* prev = getEntry(pos - 1);
		return prev->removeNext();
	}

	// return size of list
	int size() { 
		int count = 0;
		for (Node* p = getHead(); p != NULL; p = p->getLink()) count++;
		return count;
	}

	// display data fields of nodes in the list
	void display(const char* str = "List") { 
		cout << str;
		cout << "[list size  " << size() << "] : ";
		for (Node* p = getHead(); p != NULL; p = p->getLink()) p->display();
		cout << endl;
	}

	// reverse list
	void reverse() { 
		Node prev(NULL), temp(NULL), curr(NULL);
		curr.setLink(getHead()); //현재노드를 head가 가르키는 곳으로 설정

		while (curr.getLink()) {
			temp.setLink(curr.getLink()->getLink()); //temp = curr->link
			curr.getLink()->setLink(prev.getLink()); //curr->link = prev;
			prev.setLink(curr.getLink()); //prev = curr
			curr.setLink(temp.getLink());
		}
		org.setLink(prev.getLink());
	}

	void DataSwap(Node* curr, Node* next) {
		int temp = curr->getData();
		curr->setData(next->getData());
		next->setData(temp);
	}

	// return new list that have data fields of two lists in descending order
	LinkedList* mergeTwoLists(LinkedList* list2) { 
		LinkedList* list3 = new LinkedList();

		for (int i = 0; i < size(); i++) {
			list3->insert(i, new Node(getEntry(i)->getData()));
		}
		for (int i = size(), j = 0; j < list2->size(); i++, j++) {
			list3->insert(i, new Node(list2->getEntry(j)->getData()));
		}

		//내림차순 버블정렬
		for (int i = 0; i < list3->size(); i++) {
			Node* next = list3->getHead()->getLink();
			for (Node* q = list3->getHead(); next != NULL; q = q->getLink(), next = next->getLink()) {
				if (q->getData() < next->getData()) {
					DataSwap(q, next);
				}
			}
		}
		return list3;
	}
};

int main()
{
	LinkedList* list1 = new LinkedList(), * list2 = new LinkedList();
	LinkedList* list3;

	list1->insert(0, new Node(80));
	list1->insert(0, new Node(50));
	list1->insert(0, new Node(40));
	list1->insert(0, new Node(10));

	list1->display("List1(   before    )");
	list1->reverse();
	list1->display("List1(after reverse)");

	list2->insert(0, new Node(70));
	list2->insert(1, new Node(60));
	list2->insert(2, new Node(30));
	list2->insert(3, new Node(20));
	list2->display("List2(     now     )");

	list3 = list1->mergeTwoLists(list2);
	list3->display("List3( List1+List2 )");

	list1->clear();
	list2->clear();
	list1->display("List1(     end     )");
	list2->display("List2(     end     )");

	return 0;
}

 


 

 

 

class Node { }

class Node {
	Node* link; // pointer to next node
	int		data; // data field of 'this' node

public:
	Node(int val = 0) : data(val), link(NULL) { }
	Node* getLink() { return link; }
	void setLink(Node* next) { link = next; }
	void display() { cout << " <" << data << ">"; }
	int getData() { return data; }
	void setData(int n) { data = n; } //Data swap을 위해 정의한 함수

	// insert new node (n) next to current node
	void insertNext(Node* n) { 
		if (n != NULL) {
			n->link = link;
			link = n;
		}
	}

	// remove next node from current node
	Node* removeNext() { 
		Node* removed = link;
		if (removed != NULL) link = removed->link;
		return removed;
	}
};

 

 

 

class LinkedList { } 

class LinkedList {
	Node	org;	// head node (not head pointer!)

public:
	LinkedList() : org(0) { }
	~LinkedList() { clear(); }

	void clear() { while (!isEmpty()) delete remove(0); }
	Node* getHead() { return org.getLink(); }
	bool isEmpty() { return getHead() == NULL; }

	// return node in position 'pos' from the list
	Node* getEntry(int pos) { 
		Node* n = &org;
		for (int i = -1; i < pos; i++, n = n->getLink())
			if (n == NULL) break;
		return n;
	}

	// insert new node (n) in position 'pos'
	void insert(int pos, Node* n) { 
		Node* prev = getEntry(pos - 1);
		if (prev != NULL) prev->insertNext(n);
	}

	// remove a node in position 'pos'
	Node* remove(int pos) { 
		Node* prev = getEntry(pos - 1);
		return prev->removeNext();
	}

	// return size of list
	int size() { 
		int count = 0;
		for (Node* p = getHead(); p != NULL; p = p->getLink()) count++;
		return count;
	}

	// display data fields of nodes in the list
	void display(const char* str = "List") { 
		cout << str;
		cout << "[list size  " << size() << "] : ";
		for (Node* p = getHead(); p != NULL; p = p->getLink()) p->display();
		cout << endl;
	}

	// reverse list
	void reverse() { 
		Node prev(NULL), temp(NULL), curr(NULL);
		curr.setLink(getHead()); //현재노드를 head가 가르키는 곳으로 설정

		while (curr.getLink()) {
			temp.setLink(curr.getLink()->getLink()); //temp = curr->link
			curr.getLink()->setLink(prev.getLink()); //curr->link = prev;
			prev.setLink(curr.getLink()); //prev = curr
			curr.setLink(temp.getLink());
		}
		org.setLink(prev.getLink());
	}

	void DataSwap(Node* curr, Node* next) {
		int temp = curr->getData();
		curr->setData(next->getData());
		next->setData(temp);
	}

	// return new list that have data fields of two lists in descending order
	LinkedList* mergeTwoLists(LinkedList* list2) { 
		LinkedList* list3 = new LinkedList();

		for (int i = 0; i < size(); i++) {
			list3->insert(i, new Node(getEntry(i)->getData()));
		}
		for (int i = size(), j = 0; j < list2->size(); i++, j++) {
			list3->insert(i, new Node(list2->getEntry(j)->getData()));
		}

		//내림차순 버블정렬
		for (int i = 0; i < list3->size(); i++) {
			Node* next = list3->getHead()->getLink();
			for (Node* q = list3->getHead(); next != NULL; q = q->getLink(), next = next->getLink()) {
				if (q->getData() < next->getData()) {
					DataSwap(q, next);
				}
			}
		}
		return list3;
	}
};



-reverse() :  

void reverse() { 
		Node prev(NULL), temp(NULL), curr(NULL);
		curr.setLink(getHead()); //현재노드를 head가 가르키는 곳으로 설정

		while (curr.getLink()) {
			temp.setLink(curr.getLink()->getLink()); //temp = curr->link
			curr.getLink()->setLink(prev.getLink()); //curr->link = prev;
			prev.setLink(curr.getLink()); //prev = curr
			curr.setLink(temp.getLink());
		}
		org.setLink(prev.getLink());
	}


  단순연결리스트를 역순으로 바꾸기 위해서는 노드를 순서대로 탐색하면서 각 노드의 다음 노드를 가르키는 포인터를 현재노드의 이전 노드를 가르키도록 바꿔주어야 한다. 
그러기 위해 이전노드를 저장하는 previous, 현재노드를 저장하는 current, 다음노드를 저장하는 temp변수를 선언해주었다. data영역은 NULL로 설정해주었다. 
 `반복문을 시작하기 전에 현재노드를 head()로 지정
 `현재노드가  끝이 아닐때 까지 while문 반복
 (1) 현재노드의 다음노드를 temp변수로 지정
 (2) 현재노드의 다음노드를 이전노드를 가르키도록 변경
 (3) prev변수를 현재노드로 업데이트한다. 
 (4) 현재노드를 temp로 업데이트한다. 
 `반복문이 끝난 뒤 업데이트 된 head를 반환한다. 
 `노드를 3~4개 정도 그려보면 이해가 쉽다. 


 -mergeTwoLists() :  

  main함수를 보면 list1객체 안의 함수를 호출하고 있다. 그리고 매개변수로 받는 list2를 list3으로 반환해야한다. 
 그래서 일단 list1과 list2를 list3으로 합쳐주고 버블정렬을 사용하였다. 

	LinkedList* mergeTwoLists(LinkedList* list2) { 
		LinkedList* list3 = new LinkedList();

		for (int i = 0; i < size(); i++) {
			list3->insert(i, new Node(getEntry(i)->getData()));
		}
		for (int i = size(), j = 0; j < list2->size(); i++, j++) {
			list3->insert(i, new Node(list2->getEntry(j)->getData()));
		}
        
		//버블정렬 알고리즘() {...}
        
		}
		return list3;
	}

 (1)반환할 list3의 포인터를 선언하고 클래스를 동적할당하였다. 
 (2)list1, list2의 Data값을 복사하여 Node를 만드는 for문을 각각 만들었다. 두번째 for문에서는 매개변수로 받은 포인터를 활용해서 list2에 접근해야 한다.  


 내림차순 버블정렬: (시간복잡도가 O(n^2)이지만 교수님께서 제시하신 노드의 수가 많지않아서 간단할 알고리즘을 선택하였다.) 

//내림차순 버블정렬
		for (int i = 0; i < list3->size(); i++) {
			Node* next = list3->getHead()->getLink();
			for (Node* q = list3->getHead(); next != NULL; q = q->getLink(), next = next->getLink()) {
				if (q->getData() < next->getData()) {
					DataSwap(q, next);
				}
			}
		}

 첫번째 for문을 수행할 때마다 맨 끝부터 정렬된다. 
 (1)두번째 반복문을 실행하기 전에 next노드를 list3의 두번째 노드를 가르키도록 초기화한다.
 (2)현재노드 q와 next노드의 데이터 값을 비교하고, Swap함수를 이용해 정렬한다. 
 (3)q와 next노드를 다음노드로 이동한다.


Swap() :

	void DataSwap(Node* curr, Node* next) {
		int temp = curr->getData();
		curr->setData(next->getData());
		next->setData(temp);
	}

노드의 순서를 바꿔주는 Swap함수가 아니라  데이터의 값만 바꿔주는 Swap함수를 구현하였다. 
변경해야하는 데이터가 많아진다면 이 방법은 바람직하지 않지만 이 문제에서는 복잡하게 Link를 바꿔줄 필요없이 원하는 결과를 얻을 수 있다. 


*replace() : 

void replace(int pos, NodeToList* n) {
		NodeToList* prev = getEntry(pos - 1);
		if(prev != NULL) {
			delete prev->removeNext();
			prev->insertNext(n);
		}
	}

리스트의 pos번째 노드를 다른 노드로 교체하는 함수
(1)현재노드르 삭제한다. (=이전노드가 다음노드를 가르킴)
(2)다음노드의 뒤에 n을 삽입한다.