Just Like That .. (^-^)v

doubly linked list in C

Code นี้เขียนเอาไว้นานมากแล้วตั้งแต่สมัยเรียน สมัย C++ ยังไม่เริ่มโด่งดัง

ขั้นแรกต้อง คิดก่อนว่า อะไรจะเป็นข้อมูลใน list คิดแบบ abstract ก็คือ node นั่นเอง ก็ทำการ กำหนดขึ้นมาว่า node มันเป็นอย่างไร สามารถทำอะไรมันได้บ้าง โดยสรุปก็คือ node จะประกอบไปด้วยส่วนที่เป็น data และส่วนที่เป็นตัวชี้ไปยัง node อื่น ข้างหน้า และ ข้างหลัง (เพื่อง่ายในการเขียนรูปแบบอื่นต่อไป) และ เืพื่อให้ยืดหยุ่น จึงกำหนด data เป็น (void *)

ก็สรุปออกมาเป็น file node.h

/* node.h */
#ifndef _NODE_H_
#define _NODE_H_
typedef struct node
{
struct node* next;
struct node* prev;
void *data;
} node_t;

node_t* node_create(void *data, int dtsize);

inline int node_delete(node_t *n);
#endif

แล้วก็เขียนโค้ดมันใน file node.c
เมื่อ create node ขึ้นมา ตัว node จะต้องกำหนดค่าที่จะส่งเข้ามา และก็หนด prev และ next ให้เป็น NULL ไปก่อน
เวลา delete node ก็ต้องทำการ free data ก่อนแล้วค่อย ทำลายตัวเองทิ้ง

/* node.c */
#include <stdio.h>
#include <string.h>
#include "node.h"

inline node_t* node_create(void *data, int dtsize)
{
node_t *n;
n = (node_t *)malloc(sizeof(node_t));
n->next = NULL;
n->prev = NULL;
n->data = (void *)malloc(dtsize);
memcpy(n->data, data, dtsize);
return n;
}

inline int node_delete(node_t *n)
{
free(n->data);
free(n);
return 0;
}


เมื่อได้ node แล้ว ต่อไปก็คิดว่า list เราจะมีหน้าตาอย่างไร ในที่นี้จะให้ list เป็นตัว เก็บ node เริ่มต้น และ node ท้ายสุด และมีการเก็บขนาดไว้
ส่วน operation ของ list ก็มีเยอะ (ดูใน code) เช่น add หน้า add หลัง pop หน้า pop หลัง add ที่ตำแหน่งใด ๆ remove ที่ตำแหน่งใด ๆ ฯลฯ


/* list.h */
#ifndef _LIST_H_
#define _LIST_H_

#include "node.h"

typedef struct list
{
struct node* head;
struct node* tail;
int length;
} list_t;

list_t* list_init();

inline int list_add_tail(list_t *l, node_t *n);
inline int list_add_head(list_t *l, node_t *n);

inline node_t* list_pop_tail(list_t *l);
inline node_t* list_pop_head(list_t *l);

inline int list_remove_tail(list_t *l);
inline int list_remove_head(list_t *l);

inline int list_add_nth(list_t *l, int index, node_t *n);
inline node_t* list_pop_nth(list_t *l, int index);
inline int list_remove_nth(list_t *l, int index);

inline int list_deinit(list_t *l);

inline void list_print(list_t *l);
#endif


ส่วนตัวโค้ดก็ตามนี้ อธิบายไม่หมด คงต้องไปศึกษาเพิ่มเติมเอง


/* list.c */
#include <stdio.h>
#include <string.h>
#include "list.h"

inline list_t* list_init()
{
list_t *l;
l = (list_t *)malloc(sizeof(list_t));
l->head = NULL;
l->tail = NULL;
l->length = 0;
return l;
}

inline int list_add_tail(list_t *l, node_t *n)
{
if(l->tail != NULL)
{
n->prev = l->tail;
l->tail->next = n;
l->tail = n;
}
else
{
l->head = n;
l->tail = n;
}
l->length++;
return 0;
}

inline int list_add_head(list_t *l, node_t *n)
{
if(l->head != NULL)
{
n->next = l->head;
l->head->prev = n;
l->head = n;
}
else
{
l->head = n;
l->tail = n;
}
l->length++;
return 0;
}



inline int list_deinit(list_t *l)
{
node_t* buff;
while(l->head != NULL && l->tail != NULL)
{
list_remove_tail(l);
}
free(l);
return 0;
}

inline node_t* list_pop_tail(list_t *l)
{
node_t *buff;
buff = l->tail;
l->tail = buff->prev;
if(l->tail != NULL) l->tail->next = NULL;
buff->next = NULL;
buff->prev = NULL;
l->length--;
return buff;
}

inline node_t* list_pop_head(list_t *l)
{
node_t *buff;
buff = l->head;
l->head = buff->next;
if(l->head != NULL) l->head->prev = NULL;
buff->next = NULL;
buff->prev = NULL;
l->length--;
return buff;
}

inline int list_remove_tail(list_t *l)
{
node_t *buff;
buff = list_pop_tail(l);
node_delete(buff);
return 0;
}

inline int list_remove_head(list_t *l)
{
node_t *buff;
buff = list_pop_head(l);
node_delete(buff);
return 0;
}

inline void list_print(list_t *l)
{
node_t *n = l->head;
printf("current length = %d: ", l->length);
while(n != NULL)
{
printf("%s ", (char *)(n->data));
n = n->next;
}
printf("\n");
}

inline int list_add_nth(list_t *l, int index, node_t *n)
{
node_t *t;
int i;
if(index > l->length || index < 0)
{
return -1;
}
else if(index == 0)
{
list_add_head(l, n);
}
else if(index == l->length )
{
list_add_tail(l, n);
}
else
{
for(t = l->head, i = 0; i != index; i++)
{
t = t->next;
}
t->prev->next = n;
n->next = t;
n->prev = t->prev;
t->prev = n;
l->length++;
}
return 0;
}

inline node_t* list_pop_nth(list_t *l, int index)
{
node_t *t;
node_t *n;
int i;
if(index >= l->length || index < 0)
{
return NULL;
}
else if(index == 0)
{
t = list_pop_head(l);
}
else if(index == l->length - 1)
{
t = list_pop_tail(l);
}
else
{
for(t = l->head, i = 0; i != index; i++)
{
t = t->next;
}
t->prev->next = t->next;
t->next->prev = t->prev;
t->next = NULL;
t->prev = NULL;
l->length--;
}
return t;
}

inline int list_remove_nth(list_t *l, int index)
{
node_t *n = list_pop_nth(l, index);
if(n != NULL) node_delete(n);
return (n == NULL)?-1:0;
}


จากการ design ในลักษณะนี้จะเห็นได้ว่า เราสามารถเพิ่ม stack กะ queue support ได้ไม่ยากเลย เพราะ list มี operation พร้อมหมดแล้ว สำหรับ ทั้ง?stack และ queue

ดังนี้


/* stack.h */
#include "node.h"
#include "list.h"

typedef list_t stack_t;

inline stack_t* stack_init();

inline int stack_push(stack_t *s, node_t *);
inline node_t* stack_pop(stack_t *s);

inline void stack_print(stack_t *s);

inline int stack_deinit(stack_t *s);

/* stack.c */
#include <stdio.h>
#include "stack.h"

inline stack_t* stack_init()
{
list_t* l;
l = list_init();
return l;
}

inline int stack_push(stack_t *s, node_t *n)
{
list_add_head(s, n);
return 0;
}

inline node_t* stack_pop(stack_t *s)
{
node_t *n;
n = list_pop_head(s);
return n;
}

inline void stack_print(stack_t *s)
{
list_print(s);
}

inline int stack_deinit(stack_t *s)
{
list_deinit(s);
return 0;
}

/* queue.h */
#include "node.h"
#include "list.h"

typedef list_t queue_t;

inline queue_t* queue_init();
inline int queue_push(queue_t *q, node_t *n);
inline node_t* queue_pop(queue_t *q);

inline void queue_print(queue_t* q);

inline int queue_deinit(queue_t* q);

/* queue.c */
#include <stdio.h>
#include "queue.h"

inline queue_t* queue_init()
{
list_t *l;
l = list_init();
return l;
}

inline int queue_push(queue_t *q, node_t *n)
{
list_add_head(q, n);
return 0;
}

inline node_t* queue_pop(queue_t *q)
{
node_t *n;
n = list_pop_tail(q);
return n;
}

inline void queue_print(queue_t* q)
{
list_print(q);
}

inline int queue_deinit(queue_t* q)
{
list_deinit(q);
return 0;
}


จะเห็นได้ว่าแทบไม่ต้องเขียนอะไรเลย แค่ reuse list operation เท่านั้น
ส่วนการเรียกใช้ก็

#include <stdio.h>
#include "node.h"
#include "list.h"
#include "stack.h"
#include "queue.h"

int main(int argc, char *argv[])
{
list_t *l;
stack_t *s;
queue_t *q;
node_t *n;
int i;

l = list_init();
printf("\n========== Start List =========\n");
for(i = 1; i < argc; i++)
{
printf("\tPushing: %s\n", argv[i]);
n = node_create((void *)(argv[i]), strlen(argv[i]) + 1);
list_add_tail(l, n);
list_print(l);
}
printf("\n");
if(l->length >= 3)
{
n = node_create((void *)"HELLO", sizeof("HELLO") + 1);
list_print(l);
printf("\tAdding: %s at %d\n", (char *)(n->data), 3);
list_add_nth(l, 3, n);

list_print(l);
n = list_pop_nth(l, 3);
printf("\tPopping at %d: %s\n", 3, (char *)(n->data));
list_print(l);
}
printf("\n========== End List =========\n");
list_deinit(l);

q = queue_init();
printf("\n========== Start Queue =========\n");
for(i = 1; i < argc; i++)
{
printf("\tPushing: %s\n", argv[i]);
n = node_create((void *)(argv[i]), strlen(argv[i]) + 1);
queue_push(q, n);
queue_print(q);
}
printf("\n");
for(i = 1; i < argc; i++)
{
queue_print(q);
n = queue_pop(q);
printf("\tPopping: %s\n", (char *)(n->data));
}
printf("\n========== End Queue =========\n");
queue_deinit(q);

s = stack_init();
printf("\n========== Start Stack =========\n");
for(i = 1; i < argc; i++)
{
printf("\tPushing: %s\n", argv[i]);
n = node_create((void *)(argv[i]), strlen(argv[i]) + 1);
stack_push(s, n);
stack_print(s);
}
printf("\n");
for(i = 1; i < argc; i++)
{
stack_print(s);
n = stack_pop(s);
printf("\tPopping: %s\n", (char *)(n->data));
}
printf("\n========== End Stack =========\n");
stack_deinit(s);

return 0;
}


ผลลัพธ์ที่ได้ เป็นดังนี้

khunpid@PETER2003:/home/khunpid/container> mainapp 1 2 3

========== Start List =========
Pushing: 1
current length = 1: 1
Pushing: 2
current length = 2: 1 2
Pushing: 3
current length = 3: 1 2 3

current length = 3: 1 2 3
Adding: HELLO at 3
current length = 4: 1 2 3 HELLO
Popping at 3: HELLO
current length = 3: 1 2 3

========== End List =========

========== Start Queue =========
Pushing: 1
current length = 1: 1
Pushing: 2
current length = 2: 2 1
Pushing: 3
current length = 3: 3 2 1

current length = 3: 3 2 1
Popping: 1
current length = 2: 3 2
Popping: 2
current length = 1: 3
Popping: 3

========== End Queue =========

========== Start Stack =========
Pushing: 1
current length = 1: 1
Pushing: 2
current length = 2: 2 1
Pushing: 3
current length = 3: 3 2 1

current length = 3: 3 2 1
Popping: 3
current length = 2: 2 1
Popping: 2
current length = 1: 1
Popping: 1

========== End Stack =========






Create Date : 14 กุมภาพันธ์ 2549
Last Update : 14 กุมภาพันธ์ 2549 23:27:21 น. 1 comments
Counter : 722 Pageviews.  

 


มีความสุขกับวันแห่งความรักนะครับ


โดย: note-d วันที่: 14 กุมภาพันธ์ 2549 เวลา:23:20:03 น.  

ชื่อ :
Comment :
  *ใช้ code html ตกแต่งข้อความได้เฉพาะสมาชิกช้ code html ตกแต่งข้อความได้เฉพาะสมาชิก
 

Forsberg
Location :
กรุงเทพ Thailand

[Profile ทั้งหมด]

ฝากข้อความหลังไมค์
Rss Feed

ผู้ติดตามบล็อก : 1 คน [?]




[Add Forsberg's blog to your web]