学习笔记:数据结构(一)

学习数据结构有几天了,简单回顾总结一下。目前有学到单链表, 双链表, 栈, 队列, 哈希表。单链表和双链表的区别在于指针域,双链表的指针域有两个指针,分别为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节点。队列则是尾插。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值