컴퓨터기본/자료구조

4. General Linear Lists

차가운오미자 2021. 6. 14. 21:30

- operations can be done anywhere in the list

- Basic operations

  • insertion
  • deletion
  • retrieval
  • traversal

 

어디에서나 삽입, 삭제, 추출 등이 가능하기 위해서

- insertion: pPre

- deletion: pPre. pLoc

- retrieval: key

등 포인터를 추가한다.

1. int 데이터 저장하는 리스트

gll.h

#include <stdio.h>
#include <stdlib.h>


#define TRUE 1
#define FALSE 0

typedef int LData;

typedef struct node {
	struct NODE* next;
	LData data;
}NODE;

typedef struct gllist {
	NODE* head;
	int count;
	NODE* pos;
}GLLIST;


GLLIST* createList();

void _insert(GLLIST* pList, NODE* pPre, LData d);

int _search(GLLIST* pList, NODE** ppPre, NODE** ppLoc, LData d);

void addNodeList(GLLIST* pList, LData d);

void _delete(GLLIST* pList, NODE* pPre, NODE* pLoc);

void deleteNodeList(GLLIST* pList, LData d);

int traverseList(GLLIST* pList, int fromWhere, LData* pDataOut);

void t_printList(GLLIST* pList);

void destroyList(GLLIST* pList);

void printList(GLLIST* pList);

 

- pos: traverse()시에 현재 위치 저장하는 포인터

- _insert, _search, _delete는 pPre, pLoc 등의 포인터를 받아서 실질적으로 삭제하는 역할을 한다.

- addNodeList, deletNodeList 는 내부에서 search 함수를 이용해 위치를 찾고 insert, delete등을 불러 기능한다.

- traverseList 는 리스트에 있는 데이터를 추출해낼 수 있는 함수

- printList는 처음에 내가 그냥 만든 리스트 모든 데이터 프린트하는 함수이고, t_printList는 traverseList()를 이용해서 전체 리스트를 프린트 할 수 있는 함수이다.

 

gll.c

#include <stdio.h>
#include <stdlib.h>

#include "gll.h"

#define TRUE 1
#define FALSE 0

GLLIST* createList() {
	GLLIST* pList = (GLLIST*)malloc(sizeof(GLLIST));
	if (pList != NULL) {
		pList->count = 0;
		pList->head = NULL;
		pList->pos = NULL;
		printf("list created! \n");
		return pList;
	}
	return;

}

void _insert(GLLIST* pList, NODE* pPre, LData d) {

	NODE* newNode = (NODE*)malloc(sizeof(NODE));
	if (newNode != NULL) {
		newNode->data = d;

		if (pPre == NULL) {
			//insert before head node
			newNode->next = pList->head; //여기가 문제였음
			pList->head = newNode;
		}
		else {
			newNode->next = pPre->next;
			pPre->next = newNode;
		}
		pList->count++;
		printf("item inserted! \n");
	}
}

int _search(GLLIST* pList, NODE** ppPre, NODE** ppLoc, LData d) {
	
	for (*ppPre = NULL, *ppLoc = pList->head; *ppLoc != NULL; *ppPre = *ppLoc, *ppLoc = (*ppLoc)->next) {
		if ((*ppLoc)->data == d)
			return TRUE;
		else if ((*ppLoc)->data > d)
			break;
	}
	return FALSE;
}

void addNodeList(GLLIST* pList, LData d) {
	NODE* pPre = NULL;
	NODE* pLoc = NULL;

	int found = _search(pList, &pPre, &pLoc, d);

	if (!found) {
		_insert(pList, pPre, d);
	}
	printf("add node list success! \n");
}

void _delete(GLLIST* pList, NODE* pPre, NODE* pLoc) {
	if (pPre == NULL) {
		pList->head = pLoc->next;
	}
	else {
		pPre->next = pLoc->next;
	}

	free(pLoc);
	pList->count--;
}

void deleteNodeList(GLLIST* pList, LData d) {
	NODE* pPre = NULL;
	NODE* pLoc = NULL;

	int found = _search(pList, &pPre, &pLoc, d);

	if (found) {
		_delete(pList, pPre, pLoc);
	}
}

int traverseList(GLLIST * pList, int fromWhere, LData* pDataOut) {
	if (fromWhere == 0 || pList->pos == NULL) {
		pList->pos = pList->head;
	}
	else {
		pList->pos = pList->pos->next;
	}

	if (pList->pos != NULL) {
		*pDataOut = pList->pos->data;
		return TRUE;
	}
	else {
		*pDataOut = 0;
		return FALSE;
	}
}

void t_printList(GLLIST* pList) {
	LData data = NULL; 
	for (int i = 0; i < pList->count; i++) {
		traverseList(pList, i, &data);
		if (data != NULL) {
			printf("data: %d \n", data);
		}
	}
}

void destroyList(GLLIST* pList) {
	NODE* pDel = NULL;
	NODE* pNext = NULL;

	for (pDel = pList->head; pDel != NULL; pDel = pNext) {
		pNext = pDel->next;
		free(pDel);
	}
	free(pList);
	printf("list successfully destroyed! \n");
}

void printList(GLLIST* pList) {
	NODE* temp = pList->head;
	for (int i = 0; i < pList->count && temp!=NULL; i++) {
		printf("data: %d \n", temp->data);
		temp = temp->next;
	}
}

main.c

#include <stdio.h>
#include "gll.h"

int main(void) {

	GLLIST* pList = createList();
	_insert(pList, NULL, 1);
	addNodeList(pList, 3);
	addNodeList(pList, 2);
	printList(pList);
	t_printList(pList);
	destroyList(pList);
	return 0;
}

 

2. 좀더 Generic 한 code

data.h

#include <stdio.h>

typedef struct myData {
	int key;
	int field;
}MYDATA;

MYDATA* createData(int k, int f);

int compareData(MYDATA* arg1, MYDATA* arg2);

int compareKey(MYDATA* arg1, int key);

 

사실 compareKey나 compareData나 비슷한 역할을 한다. 헤더 파일에 이 구조체 간 key를 비교할 수 있는 비교 함수를 정의한 것이다. gll.c의 여러 함수들을 짜면서 누더기처럼 붙이다보니 일단 저렇게 두 개나 만들어졌으나, 적절하게 compareKey(int key1, int key2) 로 정리해서 compare 기능을 하는 함수를 하나로 만들어도 될 것이다.

 

data.h

#include <stdio.h>
#include "data.h"

MYDATA* createData(int k, int fi) {
	MYDATA* myData = (MYDATA*)malloc(sizeof(MYDATA));
	myData->key = k;
	myData->field = fi;
	printf("data created; [k: %d, f: %d] \n", myData->key, myData->field);
	return myData;
}

int compareData(MYDATA* arg1, MYDATA* arg2) {
	int result = arg1->key - arg2->key;
	return result;
}

int compareKey(MYDATA* arg1, int key) {
	int result = arg1->key - key;
	return result;
}

mylist.h

#include <stdio.h>
#include "data.h"

typedef struct node {
	struct NODE* next;
	MYDATA* dataptr;
}NODE;

typedef struct myList {
	NODE* head;
	int count;
	NODE* pos; // for traverse
}LIST;

//ADT
LIST* createList();
int traverse_print(LIST* myList);
NODE* retrieveNode(LIST* myList, int key);
void destroyList(LIST* myList);
void addNode(LIST* myList, MYDATA* data);
void removeList(LIST* myList, int k);

//private functions
int _insert(LIST* myList, NODE* pPre, NODE* newNode);
int _search(LIST* myList, NODE** ppPre, NODE** ppLoc, MYDATA* myDataPtr);
int _searchByKey(LIST* myList, NODE** ppPre, NODE** ppLoc, int key);
void _delete(LIST* myList, NODE** ppPre, NODE** ppDel);

 

user friendly 할 수 있는 ADT 와 ADT 함수 내에서 private하게 작동하는 내부 함수들의 관계로 만들어보았다.

 

위에 data.c에 compare 함수가 두 개가 생긴 것처럼 add/delete할 위치를 찾는 search()함수가 두 개가 되었다. 처음에 수업 ppt를 참고하면서 _search()함수의 마지막 매개변수를 MYDATA* 형식으로 하였는데, removeList, retrieveNode 등을 key를 기준으로 함수를 만들다보니까 MYDATA*를 넘겨주는 게 아니라 key를 넘겨주는 함수가 더 유용했다. _search()를 아예 없애고 addNode함수를 고치려니 귀찮아서(...) 그냥 남기고 _searchByKey()를 그냥 하나 더 선언해서 removeList 와 retrieveNode에 사용했다.

searchByKey 하나만 정의해서 addNode 에도 충분히 사용할 수 있을 것이다.

 

mylist.c

#include <stdio.h>
#include <string.h>
#include "mylist.h"

//ADT
LIST* createList() {
	LIST* myList = (LIST*)malloc(sizeof(LIST));
	myList->count = 0;
	myList->head = NULL;
	myList->pos = NULL;
	printf("list created! \n \n");
	return myList;
}

int traverse_print(LIST* myList) {
	NODE* temp = myList->head;
	printf("------------ list elements: ----------\n");
	for (; temp != NULL; temp = temp->next) {
		printf(" key: %d, field: %d \n", temp->dataptr->key, temp->dataptr->field);
	}
}

NODE* retrieveNode(LIST* myList, int key) {
	NODE* temp = myList->head;
	for (; temp != NULL; temp = temp->next) {
		if (compareKey(temp->dataptr, key) == 0) {
			//found the node;
			printf("found the right node! \n");
			return temp;
		}
	}
	//unable to find the right node
	printf("unable to find the right element \n");
	return;
}

void destroyList(LIST* myList) {
	NODE* pLoc = myList->head;
	NODE* temp = NULL;
	while (pLoc != NULL) {
		temp = pLoc;
		pLoc = pLoc->next;
		free(temp->dataptr);
		free(temp);
	}
	free(myList);
	printf("list destroyed! \n");
}


void addNode(LIST* myList, MYDATA* myData) {
	
	printf("[addNode] data inside: [k: %d, f: %d] \n", myData->key, myData->field);

	NODE* newNode = (NODE*)malloc(sizeof(NODE));
	newNode->dataptr = myData;
	newNode->next = NULL;

	printf("[addNode] created node data: [k: %d, f: %d] \n", newNode->dataptr->key, newNode->dataptr->field);

	NODE* pPre = NULL;
	NODE* pLoc = NULL;
	int result = _search(myList, &pPre, &pLoc, myData);
	if (result) {
		printf("[addNode] _search successful! \n");
		_insert(myList, pPre, newNode);
		printf("[addNode] _insert successful! \n");
	}

}

void removeList(LIST* myList, int k) {
	NODE* pPre = NULL;
	NODE* pDel = NULL;
	if(_searchByKey(myList, &pPre, &pDel, k)){
		_delete(myList, &pPre, &pDel);
		printf("[removeList] node successfully removed from the list \n");
	}
	else {
		printf("[removeList] unable to remove node \n");
	}
}


//private functions
int _insert(LIST* myList, NODE* pPre, NODE* newNode) {

	printf("[_insert] node data: [k: %d, f: %d] \n", newNode->dataptr->key, newNode->dataptr->field);
	if (pPre == NULL) {
		printf("[_insert] put in head \n");
		// put int head
		if (myList->head == NULL) {
			// list is empty
			myList->head = newNode;
		}
		else { // list isn't empty but has to go after head node
			newNode->next = myList->head; // myList->head->next가 아니지!
			myList->head = newNode;
		}
	}
	else {
		// pPre has location
		printf("[_insert] put after pPre \n");
		newNode->next = pPre->next;
		pPre->next = newNode;
	}
	myList->count++;
}

int _search(LIST* myList, NODE** ppPre, NODE** ppLoc, MYDATA* myDataPtr) {

	printf("[_search] data inside; [k: %d, f: %d] \n", myDataPtr->key, myDataPtr->field);

	/* returns 1: call _insert / returns 0: do not call _insert */
	*ppPre = NULL;
	*ppLoc = myList->head;
	if (*ppLoc == NULL) {
		//the list is empty
		return 1; // put in head
	}
	else {
		while (*ppLoc != NULL) {
			int result = compareData((*ppLoc)->dataptr, myDataPtr);
			if (result == 0) {
				// data are the same; cannot put it
				printf("[_search] unable to put in data \n");
				return 0;
			}
			else if (result < 0) {
				// pLoc->data->key > myDataPtr->key => needs to go before current node
				// go to next step
				*ppPre = *ppLoc;
				*ppLoc = (*ppLoc)->next;
			}
			else {
				// pLoc->data->key < myDataPtr->key => put after pPre and before pLoc
				// calls _insert(myList, pPre, myDataPtr)
				break;
			}
		}
		return 1;
	}
}

int _searchByKey(LIST* myList, NODE** ppPre, NODE** ppLoc, int key) {

	/* returns 0: unable to find the node that matched with the key
	   returns 1: found the right node with the key */
	*ppPre = NULL;
	*ppLoc = myList->head;
	if (*ppLoc == NULL) {
		//the list is empty
		return 0; // put in head
	}
	else {
		while (*ppLoc != NULL) {
			int result = compareKey((*ppLoc)->dataptr, key);
			if (result == 0) {
				// found the right key
				printf("[_searchByKey] found the right node with the key! \n");
				return 1;
			}
			else if (result < 0) {
				// go to next node
				*ppPre = *ppLoc;
				*ppLoc = (*ppLoc)->next;
			}
			else {
				// pLoc->data->key < myDataPtr->key => unable to find the right key
				printf("[_searchByKey] unable to find the node that has the key! \n");
				return 0;
			}
		}
	}
}

void _delete(LIST* myList, NODE** ppPre, NODE** ppDel) {
	if (*ppPre == NULL) {
		// node being deleted is the head node
		myList->head = (*ppDel)->next;
	}
	else {
		(*ppPre)->next = (*ppDel)->next;
	}
	free((*ppDel)->dataptr);
	free(*ppDel);
}

유난히 프린트 문이 많은 것은 포인터 때문에 헷갈려서 매개변수를 통해 값들을 잘 받아오고 있는지 확인하기 위해서 많이 찍어본 것이다. 대부분은 필요없는 출력들이다.

 

main.c

#include <stdio.h>
#include "mylist.h"

int main(void) {

	LIST* myList = createList();

	printf(">>>>>>>>>>>>>>>>> DATA INSERTION <<<<<<<<<<<<<<<<< \n");
	MYDATA* myData = createData(4, 7);
	addNode(myList, myData);

	printf("\n");
	myData = createData(2, 9);
	addNode(myList, myData);

	printf("\n");
	myData = createData(3, 19);
	addNode(myList, myData);

	printf("\n");
	myData = createData(3, 29);
	addNode(myList, myData);
	printf("\n");


	printf(">>>>>>>>>>>>>> SHOW INSERTION RESULT <<<<<<<<<<<<<<< \n");
	traverse_print(myList);
	printf("\n");

	printf(">>>>>>>>>>>>>>>> TRY NODE RETRIEVAL <<<<<<<<<<<<<<<<< \n");
	NODE* r_node = retrieveNode(myList, 2);
	printf("retrieved data: [k: %d, f: %d]\n", r_node->dataptr->key, r_node->dataptr->field);
	printf("\n");

	printf(">>>>>>>>>>>>>>>> TRY NODE REMOVAL <<<<<<<<<<<<<<<<< \n");
	removeList(myList, 2);
	printf("\n");
	traverse_print(myList);
	
	return 0;
}

 

결과: 

 

 

너무 누더기라 올리기도 부끄럽고 고칠 부분이 수두룩한 코드이지만... 일단 빠르게 넘기고 circular list, doubly linked list, tree 등을 시작해보려 한다. 포인터 때문에 단순 리스트 만드는 데 너무 오래 걸렸다... ㅜㅜ

 

'컴퓨터기본 > 자료구조' 카테고리의 다른 글

6. Binary Search Tree  (0) 2021.06.14
5. Doubly Linked List  (0) 2021.06.14
3. Queue  (0) 2021.06.14
2. Stack (Linked list)  (0) 2021.06.14
1. 재귀, ADT  (0) 2021.06.14