当我打开csdn,发现我有第一个粉丝了,激动得差点大喊“Hello World”,整晚都很开心,哈哈,为了保住我唯一的粉丝,我决定,以后勤快一点。今天就先出一篇通用链表实现的博客,只包含了一个头文件(.h)和一个源文件(.c),可以很方便地添加到你们的项目中。
list.h:
#ifndef __LIST_H__
#define __LIST_H__
#define LIST_NO_MEM -2
#define LIST_ERROR -1
#define LIST_SUCCESS 0
typedef struct _node node_t;
/*node*/
struct _node{
node_t *next; //next node
void *element; //pointer of elt
};
/*list*/
typedef struct _list{
int nb_elt; //number of element of list
node_t *node; //head pointer
}list_t;
/*initialise the list,the list shouldn't be null*/
int list_init(list_t *list);
/*get the size of a list of element*/
int list_size(const list_t *list);
/*add a element to list*/
int list_add(list_t *list,void *element,int pos);
/*get an element from a list*/
void *list_get(const list_t *list,int pos);
/*remove an element from a list*/
int list_remove(list_t *list,int pos);
#endif
list.c
#include "list.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
int list_init(list_t *list)
{
if(list == NULL)
return LIST_ERROR;
memset(list,0,sizeof(list_t));
return LIST_SUCCESS;
}
int list_size(const list_t *list)
{
if(list == NULL)
return LIST_ERROR;
return list->nb_elt;
}
/*index start from 0*/
int list_add(list_t *list,void *element,int pos)
{
int i = 0;
node_t *new_node = NULL;
node_t *node = NULL;
if(list == NULL)
return LIST_ERROR;
new_node = (node_t*)malloc(sizeof(node_t));
if(new_node == NULL)
return LIST_NO_MEM;
memset(new_node,0,sizeof(node_t));
new_node->element = element;
if(list->nb_elt == 0)
{
new_node->next = NULL;
list->node = new_node;
list->nb_elt++;
return list->nb_elt;
}
if(pos < 0 || pos >= list->nb_elt)
pos = list->nb_elt;
node = list->node;
if(pos == 0)
{
new_node->next = list->node;
list->node = new_node;
list->nb_elt++;
return list->nb_elt;
}
while(i + 1 < pos)
{
i++;
node = node->next;
}
if(pos == list->nb_elt)
{
new_node->next = NULL;
node->next = new_node;
list->nb_elt++;
return list->nb_elt;
}
new_node->next = node->next;
node->next->next = new_node;
list->nb_elt++;
return list->nb_elt;
}
void *list_get(const list_t *list,int pos)
{
int i = 0;
node_t *node = NULL;
if(list == NULL)
return NULL;
if(pos < 0 || pos >= list->nb_elt)
return NULL;
node = list->node;
while(i < pos)
{
i++;
node = node->next;
}
return node->element;
}
int list_remove(list_t *list,int pos)
{
int i = 0;
node_t *node = NULL;
node_t *del_node = NULL;
if(list == NULL)
return LIST_ERROR;
if(pos < 0 || pos >= list->nb_elt)
return LIST_ERROR;
node = list->node;
/*special case*/
if(pos == 0)
{
list->node = node->next;
list->nb_elt--;
free(node);
return LIST_SUCCESS;
}
while(pos > i + 1)
{
i++;
node = node->next;
}
del_node = node->next;
node->next = node->next->next;
list->nb_elt--;
free(del_node);
return LIST_SUCCESS;
}
#include <stdlib.h>
#include <stdio.h>
#include "list.h"
int main()
{
int size = 0;
int i = 0;
list_t list;
char *elt;
list_init(&list);
char arr1[5] = "hello";
char arr2[5] = "world";
char arr3[32] = "i'm pumpkin monkey";
list_add(&list,(void*)arr1,-1);
list_add(&list,(void*)arr2,-1);
list_add(&list,(void*)arr3,-1);
printf("list size:%d\n",list_size(&list));
list_remove(&list,1);
size = list_size(&list);
printf("list size:%d\n",size);
for(i = 0;i<size;i++)
{
elt = (char*)list_get(&list,i);
if(elt == NULL)
{
printf("get null node\n");
}
printf("elment :%s\n",elt);
}
return 0;
}
以上就是最基本的通用单向链表的实现,包含了创建、添加、删除、长度、获取等最常用的基本功能。还可以包含克隆,下个元素、等其他功能,可根据需要自己添加
实际上链表并不复杂,但是很多人还是没法写好,主要还是特殊情况(边界条件)考虑得不够充分。不仅是在链表,在任何一个函数的实现里,都必须要考虑特殊场景。对于链表来说,其实需要注意的也就那几个点,目标节点(pos)是否存在、链表长度为0和pos刚好等于链表长度、输入链表是否有效、循环条件。
大家可以考虑加个头结点:
数据结构中,在单链表的开始结点之前附设一个类型相同的结点,称之为头结点。头结点的数据域可以不存储任何信息,头结点的指针域存储指向开始结点的指针(即第一个元素结点的存储位置)。
头结点作用:
1、防止单链表是空的而设的.当链表为空的时候,带头结点的头指针就指向头结点.如果当链表为空的时候,单链表没有带头结点,那么它的头指针就为NULL.
2、是为了方便单链表的特殊操作,插入在表头或者删除第一个结点.这样就保持了单链表操作的统一性!
3、单链表加上头结点之后,无论单链表是否为空,头指针始终指向头结点,因此空表和非空表的处理也统一了,方便了单链表的操作,也减少了程序的复杂性和出现bug的机会。
4、对单链表的多数操作应明确对哪个结点以及该结点的前驱。不带头结点的链表对首元结点、中间结点分别处理等;而带头结点的链表因为有头结点,首元结点、中间结点的操作相同 ,从而减少分支,使算法变得简单 ,流程清晰。对单链表进行插入、删除操作时,如果在首元结点之前插入或删除的是首元结点,不带头结点的单链表需改变头指针的值,在C 算法的函数形参表中头指针一般使用指针的指针(在C+ +中使用引用 &);而带头结点的单链表不需改变头指针的值,函数参数表中头结点使用指针变量即可。