学习数据结构有几天了,简单回顾总结一下。目前有学到单链表, 双链表, 栈, 队列, 哈希表。单链表和双链表的区别在于指针域,双链表的指针域有两个指针,分别为prev, next , prev指向它的上一个节点,next指向它的下一个节点。链表分为有头(头节点),无头(只有头指针); 循环,非循环(最后一个节点的指针是否指向头,形成环),它们只是在访问和插入上很小的差异,实际上都差不多。单链表一般定义为
typedef int elem_t;
struct slist {
elem_t data;
struct slist *next;
};
双链表就是在单链表的基础上加上了一个指向前一个节点的指针,方便了同时向前向后遍历,相对单链表只能从头开始访问,提高了访问效率。双链表的定义一般为
typedef int elem_t;
struct dlist {
elem_t data;
struct dlist *prev;
struct dlist *next;
};
链表的操作一般也就增删改查。
抽象数据类型(ADT)理解的不是很深,感觉就是一种封装, 里面有对象和方法,有点c++面类的感觉。下面是对双链表的一种封装。几个基本的方法。
struct node {
struct node *prev;
struct node *next;
char data[1];
/*gcc环境下有零长数组的概念,但其他编译器一般不支持,所以data给了1,给节点申请空间的时候加上你需要的数据域大小即可*/
};
struct dlist {
struct node *head;
void (*add)(struct dlist *, void *, size_t);
void (*add_tail)(struct dlist *, void *, size_t);
void (*for_each)(struct dlist *, void (*todo)(struct node *));
/* 这里用到了回调函数,只是传了一个函数指针过来,定义则在你调用的地方即可。下面的cmp也是一样*/
void (*for_each_rev)(struct dlist *, void (*todo)(struct node *));
struct node *(*find)(struct dlist *, const void *, int (*cmp)(const struct node *, const void *));
void (*del)(struct node *);
};
void init(struct dlist *);
void destroy(struct dlist *);
方法的实现
/*头插*/
static void add(struct dlist *dlist, const void *data, size_t size)
{
assert(dlist != NULL);
assert(data != NULL);
struct node *pa = (struct node *)malloc(sizeof(*pa)+size);
assert(pa != NULL);
memcpy(pa->data, data, size); /* 将数据拷贝到节点中 */
pa->next = dlist->head->next;
pa->prev = dlist->head;
dlist->head->next = pa;
pa->next->prev = pa;
}
static int is_empty(struct dlist *dlist)
{
return (dlist->head->next == dlist->head) && (dlist->head->prev == dlist->head);
}
/* 尾插 */
static void add_tail(struct dlist *dlist, const void *data, size_t size)
{
assert(dlist != NULL);
assert(data != NULL);
struct node *pa = (struct node *)malloc(sizeof(*pa) + size);
assert(pa != NULL);
memcpy(pa->data, data, size);
pa->next = dlist->head;
pa->prev = dlist->head->prev;
dlist->head->prev = pa;
pa->prev->next = pa;
}
/* 顺向遍历 */
static void for_each(struct dlist *dlist, void (*todo)(struct node *node))
{
assert(dlist != NULL);
struct node *cur = dlist->head->next;
for(; cur != dlist->head; cur = cur->next)
todo(cur);
}
/* 反向遍历 */
static void for_each_rev(struct dlist *dlist, void (*todo)(struct node *node))
{
assert(dlist != NULL);
struct node *cur = dlist->head->prev;
for(; cur != dlist->head; cur = cur->prev)
todo(cur);
}
/* 查找 */
static struct node *find(struct dlist *dlist, const void *key, int (*cmp)(const struct node *node, const void *key))
{
static struct node *cur = NULL;
if (cur == NULL) {
cur = dlist->head;
}
for (cur = cur->next; cur != dlist->head; cur = cur->next) {
if (cmp(cur, key)) {
return cur;
}
}
return NULL;
}
/* 删除 */
static void del(struct node *node)
{
assert(node != NULL);
node->prev->next = node->next;
node->next->prev = node->prev;
node->next = node;
node->prev = node;
free(node);
}
/*初始化函数 */
void dlist_init(struct dlist *dlist)
{
assert(dlist != NULL);
dlist->head = (struct node *)malloc(sizeof(*dlist->head)); /* 申请头节点空间 */
assert(dlist->head != NULL);
dlist->head->next = dlist->head; /* 初始化头节点 */
dlist->head->prev = dlist->head;
dlist->head->data[0] = 0;
dlist->add = add; /* 挂载函数 */
dlist->add_tail = add_tail;
dlist->for_each = for_each;
dlist->for_each_rev = for_each_rev;
dlist->find = find;
dlist->is_empty = is_empty;
dlist->del = del;
}
void dlist_destroy(struct dlist *dlist)
{
while(dlist->head->next != dlist->head)
del(dlist->head->next);
free(dlist->head);
}
栈(区别与内存中的栈)的特点是先进后出,队列则刚好相反,先进先出, 同现实中的排队一样。所以栈实际实现来说就是一种双链表,只是数据插入的方式只能是头插,访问的话也只能访问头节点的next节点。队列则是尾插。