널널한 개발자 TV의 자료구조 강의 시리즈를 정리한 것이다.
prev
, next
,포인터는 관리체계이며 연결 리스트 사용자가 임의로 접근하거나 변경하지 않아야 한다.#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* 노드 구조체 정의 */
typedef struct NODE
{
char szData[64];
struct NODE *prev;
struct NODE *next;
} NODE;
/* 전역 dummy head, tail 포인터 정의 */
NODE *g_pHead;
NODE *g_pTail;
/* 전역 2중 연결리스트 사이즈 변수 정의 */
int g_nSize;
void InsertBefore(NODE *pDstNode, const char *pszData);
/* 전역 변수 초기화 함수 */
void InitList(void)
{
g_pHead = (NODE *)malloc(sizeof(NODE));
g_pTail = (NODE *)malloc(sizeof(NODE));
g_nSize = 0;
memset(g_pHead, 0, sizeof(NODE));
memset(g_pTail, 0, sizeof(NODE));
strcpy_s(g_pHead->szData, sizeof(g_pHead->szData), "DUMMY HEAD");
strcpy_s(g_pTail->szData, sizeof(g_pTail->szData), "DUMMY TAIL");
g_pHead->next = g_pTail;
g_pTail->prev = g_pHead;
}
/* 2중 연결 리스트 메모리 해제 함수 */
void ReleaseList(void)
{
NODE *pTmp = g_pHead;
while (pTmp != NULL)
{
NODE *pDelete = pTmp;
pTmp = pTmp->next;
printf("free(%p)\n", pDelete);
free(pDelete);
}
g_pHead = NULL;
g_pTail = NULL;
g_nSize = 0;
puts("ReleaseList()");
}
/* 2중 연결 리스트 전체 출력 함수 */
void PrintList(void)
{
printf("PrintList(): g_nSize %d, g_pHead [%p], g_pTail [%p]\n", g_nSize, g_pHead, g_pTail);
NODE *pTmp = g_pHead;
while (pTmp != NULL)
{
printf("[%p] %p, %s [%p]\n", pTmp->prev, pTmp, pTmp->szData, pTmp->next);
pTmp = pTmp->next;
}
}
/* 맨 앞부분에 데이터 추가 함수 */
int InsertAtHead(const char *pszData)
{
NODE *pNewNode = (NODE *)malloc(sizeof(NODE));
memset(pNewNode, 0, sizeof(NODE));
strcpy_s(pNewNode->szData, sizeof(pNewNode->szData), pszData);
pNewNode->prev = g_pHead;
pNewNode->next = g_pHead->next;
g_pHead->next = pNewNode;
pNewNode->next->prev = pNewNode;
g_nSize++;
return g_nSize;
}
/* 맨 뒷부분에 데이터 추가 함수 */
int InsertAtTail(const char *pszData)
{
InsertBefore(g_pTail, pszData);
return g_nSize;
}
/* 해당 데이터가 존재하는 노드를 찾는 함수 */
NODE *FindNode(const char *pszData)
{
NODE *pTmp = g_pHead->next;
while (pTmp != g_pTail)
{
if (strcmp(pTmp->szData, pszData) == 0)
return pTmp;
pTmp = pTmp->next;
}
return NULL;
}
/* 해당 데이터가 존재하는 노드를 삭제하는 함수 */
int DeleteNode(const char *pszData)
{
NODE *pNode = FindNode(pszData);
pNode->prev->next = pNode->next;
pNode->next->prev = pNode->prev;
printf("DeleteNode(): %p\n", pNode);
g_nSize--;
free(pNode);
return 0;
}
/* 2중 연결 리스트의 전체 크기를 반환하는 함수 */
int GetSize(void)
{
return g_nSize;
}
/* 2중 연결 리스트의 전체 크기를 반환하는 함수 */
int GetLength(void)
{
return GetSize();
}
/* 2중 연결 리스트가 비어 있는지 확인하는 함수*/
int IsEmpty(void)
{
return GetSize();
}
/* 해당 노드 뒤에 새로운 노드를 추가하는 함수 */
void InsertBefore(NODE *pDstNode, const char *pszData)
{
NODE *pPrev = pDstNode->prev;
NODE *pNewNode = (NODE *)malloc(sizeof(NODE));
memset(pNewNode, 0, sizeof(NODE));
strcpy_s(pNewNode->szData, sizeof(pNewNode->szData), pszData);
pNewNode->prev = pPrev;
pNewNode->next = pDstNode;
pPrev->next = pNewNode;
pDstNode->prev = pNewNode;
g_nSize++;
}
/* 해당 인덱스에 새로운 노드를 추가하는 함수 */
int InsertAt(int idx, const char *pszData)
{
int i = 0;
NODE *pTmp = g_pHead->next;
while (pTmp != g_pTail)
{
if (i == idx)
{
InsertBefore(pTmp, pszData);
return i;
}
pTmp = pTmp->next;
i++;
}
InsertAtTail(pszData);
return i;
}
/* 인덱스에 해당하는 노드를 반환하는 함수 */
NODE *GetAt(int idx)
{
int i = 0;
NODE *pTmp = g_pHead->next;
while (pTmp != g_pTail)
{
if (i == idx)
return pTmp;
pTmp = pTmp->next;
i++;
}
return NULL;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* 관리 대상 데이터 */
typedef struct USERDATA
{
char szName[64];
char szPhone[64];
} USERDATA;
/* 노드 구조체 정의 */
typedef struct NODE
{
// 관리 대상 자료
USERDATA *pData;
// 자료 구조
struct NODE *prev;
struct NODE *next;
} NODE;
/* 전역 dummy head, tail 포인터 정의 */
NODE *g_pHead;
NODE *g_pTail;
/* 전역 2중 연결리스트 사이즈 변수 정의 */
int g_nSize;
void InsertBefore(NODE *pDstNode, const USERDATA *pParam);
/* 전역 변수 초기화 함수 */
void InitList(void)
{
g_pHead = (NODE *)malloc(sizeof(NODE));
g_pTail = (NODE *)malloc(sizeof(NODE));
g_nSize = 0;
memset(g_pHead, 0, sizeof(NODE));
memset(g_pTail, 0, sizeof(NODE));
g_pHead->next = g_pTail;
g_pTail->prev = g_pHead;
}
/* 2중 연결 리스트 메모리 해제 함수 */
void ReleaseList(void)
{
NODE *pTmp = g_pHead;
while (pTmp != NULL)
{
NODE *pDelete = pTmp;
pTmp = pTmp->next;
printf("free(%p)\n", pDelete);
free(pDelete->pData);
free(pDelete);
}
g_pHead = NULL;
g_pTail = NULL;
g_nSize = 0;
puts("ReleaseList()");
}
/* 2중 연결 리스트 전체 출력 함수 */
void PrintList(void)
{
int i = 0;
printf("PrintList(): g_nSize %d, g_pHead [%p], g_pTail [%p]\n", g_nSize, g_pHead, g_pTail);
NODE *pTmp = g_pHead;
while (pTmp != NULL)
{
if (pTmp == g_pHead || pTmp == g_pTail)
printf("[%p] DUMMY [%p]\n", pTmp->prev, pTmp->next);
else
{
printf("index:%d [%p] %p, %s, %s [%p]\n", \
i, pTmp->prev, pTmp, \
pTmp->pData->szName, \
pTmp->pData->szPhone, \
pTmp->next);
++i;
}
pTmp = pTmp->next;
}
putchar('\n');
}
/* 맨 앞부분에 데이터 추가 함수 */
// pParam: 호출자가 메모리를 동적 할당 및 값 설정하여 전달
int InsertAtHead(const USERDATA *pParam)
{
NODE *pNewNode = (NODE *)malloc(sizeof(NODE));
memset(pNewNode, 0, sizeof(NODE));
pNewNode->pData = pParam;
pNewNode->prev = g_pHead;
pNewNode->next = g_pHead->next;
g_pHead->next = pNewNode;
pNewNode->next->prev = pNewNode;
g_nSize++;
return g_nSize;
}
/* 맨 뒷부분에 데이터 추가 함수 */
int InsertAtTail(const USERDATA *pParam)
{
InsertBefore(g_pTail, pParam);
return g_nSize;
}
/* 해당 데이터가 존재하는 노드를 찾는 함수 */
NODE *FindNode(const char *pszName)
{
NODE *pTmp = g_pHead->next;
while (pTmp != g_pTail)
{
if (strcmp(pTmp->pData->szName, pszName) == 0)
return pTmp;
pTmp = pTmp->next;
}
return NULL;
}
/* 해당 데이터가 존재하는 노드를 삭제하는 함수 */
int DeleteNode(const char *pszName)
{
NODE *pNode = FindNode(pszName);
pNode->prev->next = pNode->next;
pNode->next->prev = pNode->prev;
printf("DeleteNode(): %p\n", pNode);
g_nSize--;
free(pNode->pData);
free(pNode);
return 0;
}
/* 2중 연결 리스트의 전체 크기를 반환하는 함수 */
int GetSize(void)
{
return g_nSize;
}
/* 2중 연결 리스트의 전체 크기를 반환하는 함수 */
int GetLength(void)
{
return GetSize();
}
/* 2중 연결 리스트가 비어 있는지 확인하는 함수*/
int IsEmpty(void)
{
return GetSize();
}
/* 해당 노드 뒤에 새로운 노드를 추가하는 함수 */
void InsertBefore(NODE *pDstNode, const USERDATA *pParam)
{
NODE *pPrev = pDstNode->prev;
NODE *pNewNode = (NODE *)malloc(sizeof(NODE));
memset(pNewNode, 0, sizeof(NODE));
pNewNode->pData = pParam;
pNewNode->prev = pPrev;
pNewNode->next = pDstNode;
pPrev->next = pNewNode;
pDstNode->prev = pNewNode;
g_nSize++;
}
/* 해당 인덱스에 새로운 노드를 추가하는 함수 */
int InsertAt(int idx, const USERDATA *pParam)
{
int i = 0;
NODE *pTmp = g_pHead->next;
while (pTmp != g_pTail)
{
if (i == idx)
{
InsertBefore(pTmp, pParam);
return i;
}
pTmp = pTmp->next;
i++;
}
InsertAtTail(pParam);
return i;
}
/* 인덱스에 해당하는 노드를 반환하는 함수 */
NODE *GetAt(int idx)
{
int i = 0;
NODE *pTmp = g_pHead->next;
while (pTmp != g_pTail)
{
if (i == idx)
return pTmp;
pTmp = pTmp->next;
i++;
}
return NULL;
}
ADT의 핵심처럼 기존 노드를 자료와 관리체계 여기에서는 데이터를 담는 구조체와 이중 연결 리스트 구조로 분리하여 함수를 수정하였다.
/* 노드 구조체 정의 */
typedef struct NODE
{
char szData[64];
struct NODE *prev;
struct NODE *next;
} NODE;
/* 관리 대상 데이터 */
typedef struct USERDATA
{
char szName[64];
char szPhone[64];
} USERDATA;
/* 노드 구조체 정의 */
typedef struct NODE
{
// 관리 대상 자료
USERDATA *pData;
// 자료 구조
struct NODE *prev;
struct NODE *next;
} NODE;
int main(void)
{
InitList();
USERDATA *pNewData = (USERDATA *)malloc(sizeof(USERDATA));
memset(pNewData, 0, sizeof(USERDATA));
strcpy_s(pNewData->szName, sizeof(pNewData->szName), "BOLEE");
strcpy_s(pNewData->szPhone, sizeof(pNewData->szPhone), "010-1234-1234");
InsertAtTail(pNewData);
PrintList();
ReleaseList();
putchar('\n');
return 0;
}
PrintList(): g_nSize 1, g_pHead [0x7f9c41405850], g_pTail [0x7f9c41405870]
[0x0] DUMMY [0x7f9c41405910]
index:0 [0x7f9c41405850] 0x7f9c41405910, BOLEE, 010-1234-1234 [0x7f9c41405870]
[0x7f9c41405910] DUMMY [0x0]
free(0x7f9c41405850)
free(0x7f9c41405910)
free(0x7f9c41405870)
ReleaseList()