单链表概述

单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始。

从概念上讲,可以把链表想象成一系列连续的元素,然而,由于这些元素是动态分配的(C语言中使用malloc),切记这些元素通常实际上都是分散在内存空间的

欢迎关注我的个人博客:www.wuyudong.com, 更多精彩文章与您分享

单链表的接口定义:

1、list_init

void list_init(List *list, void (*destroy)(void *data));

返回值   void

描述    初始化由参数list指定的链表,该函数必须在链表做其他操作之前调用,当调用list_destroy时,destroy参数提供了一种释放动态分配数据的方法。如果链表采用malloc动态分配的数据,destroy应该设置为free来释放这些数据

复杂度   O(1)


2、list_destroy

void list_destroy(List *list);

返回值   void

描述    销毁由参数list指定的链表,调用该函数以后任何函数都不能再执行,除非重新执行list_init函数。list_destroy将list中的所有元素都移除,每移除一个元素都会调用此函数

复杂度   O(n)  n为链表元素的个数


3、list_ins_next

int list_ins_next(List *list, ListElmt *element, const void *data);

返回值   如果插入元素成功返回0,否则返回-1

描述    在指定的list的element元素后面插入一个元素,如果element为NULL,则在链表的头部插入新的元素,该元素包含一个指向data的指针

复杂度   O(1)


4、list_rem_next

int list_rem_next(List *list, ListElmt *element, void **data);

返回值    如果移除元素成功返回0,否则返回-1

描述    移除在指定的list的element后面的那个元素,如果element为NULL,则移除链表的头元素,调用返回后,data指向已经移除元素的数据

复杂度   O(1)


5、list_size

int list_size(const List *list);

返回值    如果list中元素的个数

描述    这是一个宏,用来计算指定list中元素的个数

复杂度   O(1)


6、list_head

ListElmt *list_head(const List *list);

返回值    指向链表头元素的指针

描述    这是一个宏,返回由参数list指定的链表头元素的指针

复杂度   O(1)


7、list_tail

ListElmt *list_tail(const List *list) ((list)->tail);

返回值    指向链表尾元素的指针

描述    这是一个宏,返回由参数list指定的链表尾元素的指针

复杂度   O(1)


8、list_is_head

int list_is_head(const ListElmt *element);

返回值    如果element元素是链表头元素返回1,否则返回-1

描述    这是一个宏,用来判断element元素是否是list的头元素

复杂度   O(1)


9、list_is_tail

int list_is_tail(const ListElmt *element);

返回值    如果element元素是链表尾元素返回1,否则返回-1

描述    这是一个宏,用来判断element元素是否是list的尾元素

复杂度   O(1)


10、list_data

void *list_data(const ListElmt *element);

返回值    结点中保存的数据

描述    这是一个宏,返回由element元素中保存的数据

复杂度   O(1)


11、list_next

ListElmt *list_next(const ListElmt *element) ;

返回值    返回element所指定结点的下一个结点

描述    这是一个宏,返回链表中element所指定结点的下一个结点

复杂度   O(1)

单链表的实现和分析

抽象数据类型的头文件(list.h):

#ifndef LIST_H
#define LIST_H
#include <stdlib.h>
//为单链表的结点定义一个结构体.
typedef struct ListElmt_ {
void *data; //数据域
struct ListElmt_ *next; //指针域 } ListElmt; //为单链表定义一个结构体.
typedef struct List_ {
int size; //容量
int (*match)(const void *key1, const void *key2); //匹配函数
void (*destroy)(void *data); //撤销操作
ListElmt *head; //头指针
ListElmt *tail; //尾指针 } List; //公共接口
void list_init(List *list, void (*destroy)(void *data)); void list_destroy(List *list); int list_ins_next(List *list, ListElmt *element, const void *data); int list_rem_next(List *list, ListElmt *element, void **data); #define list_size(list) ((list)->size) #define list_head(list) ((list)->head) #define list_tail(list) ((list)->tail) #define list_is_head(list, element) ((element) == (list)->head ? 1 : 0) #define list_is_tail(element) ((element)->next == NULL ? 1 : 0) #define list_data(element) ((element)->data) #define list_next(element) ((element)->next) #endif

初始化单链表:

void list_init(List *list, void (*destroy)(void *data)) {  //初始化list
list->size = ;
list->destroy = destroy; //设置为定义的析构函数
list->head = NULL;
list->tail = NULL;
return;
}

回收单链表:

void list_destroy(List *list) {
//移除每一个元素
while (list_size(list) > ) {
if (list_rem_next(list, NULL, (void **)&data) == && list->destroy != NULL) { //不断地移除链表的头结点
list->destroy(data); //调用一个用户定义的函数来释放动态分配的数据.
}
}
//现在没有操作了,释放结构体作为预防措施
memset(list, , sizeof(List));
return;
}

插入新节点作为指定结点的直接后继结点:

int list_ins_next(List *list, ListElmt *element, const void *data) {
ListElmt *new_element; //为结点动态分配存储空间
if ((new_element = (ListElmt *)malloc(sizeof(ListElmt))) == NULL) //假如分配失败
return -;
// 将元素插入链表
new_element->data = (void *)data;
if (element == NULL) {
//插入到链表的头部
if (list_size(list) == )
list->tail = new_element;
new_element->next = list->head;
list->head = new_element;
} else {
//插入到除了链表头部以外指定的其他地方
if (element->next == NULL)
list->tail = new_element;
new_element->next = element->next;
element->next = new_element;
}
list->size++; //表长增加
return ;
}

删除指定结点的直接后继结点:

int list_rem_next(List *list, ListElmt *element, void **data) {
ListElmt *old_element;
//不允许从一个空的list中移除元素.
if (list_size(list) == )
return -;
// 从list中移除元素.
if (element == NULL) {
// 移除表头的结点.
*data = list->head->data;
old_element = list->head;
list->head = list->head->next;
if (list_size(list) == ) //如果list只有一个元素,则直接删除尾结点
list->tail = NULL;
} else {
// 移除非头结点.
if (element->next == NULL)
return -;
*data = element->next->data;
old_element = element->next;
element->next = element->next->next;
if (element->next == NULL) //移除指定结点后,后继为NULL,则用尾结点指向
list->tail = element;
}
//释放分配的抽象数据类型.
free(old_element);
//调整list的长度. *
list->size--;
return ;
}

注意:list_size、list_head、list_tail、list_is_head、list_is_tail、list_data、list_next 这些宏实现了链表中的一些简单操作,它们提供了快速访问和检测结构体成员的能力。这些操作的时间复杂度都是O(1)

完整的测试代码如下:

// Completed on 2014.10.22 21:00
// Language: C99
//
// 版权所有(C)codingwu (mail: oskernel@126.com)
// 博客地址:http://www.cnblogs.com/archimedes/
#include <stdio.h>
#include <stdlib.h>
#include "list.h" static void print_list(const List *list) {
ListElmt *element;
int *data, i;
fprintf(stdout, "List size is %d\n", list_size(list));
i = ;
element = list_head(list);
while () {
data = list_data(element);
fprintf(stdout, "list[%03d]=%03d\n", i, *data);
i++;
if (list_is_tail(element))
break;
else
element = list_next(element);
}
return;
} int main(int argc, char **argv) { List list;
ListElmt *element;
int *data, i; //初始化list
list_init(&list, free);
element = list_head(&list);
for (i = ; i > ; i--) {
if ((data = (int *)malloc(sizeof(int))) == NULL)
return ;
*data = i;
if (list_ins_next(&list, NULL, data) != ) //逐个插入元素
return ; }
print_list(&list); //打印初始list
element = list_head(&list); //指向头结点
for (i = ; i < ; i++)
element = list_next(element); data = list_data(element);
fprintf(stdout, "Removing an element after the one containing %03d\n", *data); if (list_rem_next(&list, element, (void **)&data) != ) //删除指定结点
return ;
print_list(&list);
fprintf(stdout, "Inserting 011 at the tail of the list\n");
*data = ;
if (list_ins_next(&list, list_tail(&list), data) != ) //插入指定结点
return ;
print_list(&list);
fprintf(stdout, "Removing an element after the first element\n"); element = list_head(&list);
if (list_rem_next(&list, element, (void **)&data) != )
return ; print_list(&list); fprintf(stdout, "Inserting 012 at the head of the list\n"); *data = ;
if (list_ins_next(&list, NULL, data) != )
return ; print_list(&list); fprintf(stdout, "Iterating and removing the fourth element\n"); element = list_head(&list);
element = list_next(element);
element = list_next(element); if (list_rem_next(&list, element, (void **)&data) != )
return ; print_list(&list); fprintf(stdout, "Inserting 013 after the first element\n"); *data = ;
if (list_ins_next(&list, list_head(&list), data) != )
return ; print_list(&list); i = list_is_head(&list, list_head(&list));
fprintf(stdout, "Testing list_is_head...Value=%d (1=OK)\n", i);
i = list_is_head(&list, list_tail(&list));
fprintf(stdout, "Testing list_is_head...Value=%d (0=OK)\n", i);
i = list_is_tail(list_tail(&list));
fprintf(stdout, "Testing list_is_tail...Value=%d (1=OK)\n", i);
i = list_is_tail(list_head(&list));
fprintf(stdout, "Testing list_is_tail...Value=%d (0=OK)\n", i); fprintf(stdout, "Destroying the list\n");
list_destroy(&list);
return ; }

C实现通用数据结构--单链表的更多相关文章

  1. python实现数据结构单链表

    #python实现数据结构单链表 # -*- coding: utf-8 -*- class Node(object): """节点""" ...

  2. C语言数据结构-单链表的实现-初始化、销毁、长度、查找、前驱、后继、插入、删除、显示操作

    1.数据结构-单链表的实现-C语言 typedef struct LNode { int data; struct LNode* next; } LNode,*LinkList; //这两者等价.Li ...

  3. 数据结构——单链表java简易实现

    巩固数据结构 单链表java实现 单链表除了表尾 每个几点都有一个后继 结点有数据和后继指针组成  通过构建表头和表尾(尾部追加需要)两个特殊几点 实现单链表的一些操作,代码如下 package co ...

  4. C# 数据结构--单链表

    什么是单链表 这两天看到很多有关单链表的面试题,对单链表都不知道是啥的我.经过学习和整理来分享一下啥是单链表和单链表的一些基本使用方法.最后看些网上有关单链表的面试题代码实例. 啥是单链表? 单链表是 ...

  5. 数据结构-------单链表(C++)

    相关信息: /** * @subject 数据结构 实验2 * @author 信管1142班 201411671210 赖俊杰 * @project 单链表 * @time 2015年10月29日1 ...

  6. C# 数据结构 - 单链表 双链表 环形链表

    链表特点(单链表 双链表) 优点:插入和删除非常快.因为单链表只需要修改Next指向的节点,双链表只需要指向Next和Prev的节点就可以完成插入和删除操作. 缺点:当需要查找某一个节点的时候就需要一 ...

  7. 数据结构—单链表(类C语言描写叙述)

    单链表 1.链接存储方法 链接方式存储的线性表简称为链表(Linked List). 链表的详细存储表示为: ① 用一组随意的存储单元来存放线性表的结点(这组存储单元既能够是连续的.也能够是不连续的) ...

  8. python算法与数据结构-单链表(38)

    一.链表 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括 ...

  9. 数据结构——单链表(singly linked list)

    /* singlyLinkedList.c */ /* 单链表 */ /* 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素. */ #include <stdio ...

随机推荐

  1. 【转】ORATOP工具使用说明

    文章转自:http://lintzyuan.blogspot.jp/2014/07/oratop.html ORATOP   前言:隨著PC Server的規格及速度愈來愈快,大多數的公司摒棄大型主機 ...

  2. vmware虚拟机工具vmware tools介绍及安装排错

    VMware Tools是VMware虚拟机中自带的一种增强工具,相当于VirtualBox中的增强功能(Sun VirtualBox Guest Additions),是VMware提供的增强虚拟显 ...

  3. Web Component--01. 简介

    Web Components 是什么? Web Components是W3C定义的新标准,它给了前端开发者扩展浏览器标签的能力,可以自由的定制组件,更好的进行模块化开发,彻底解放了前端开发者的生产力. ...

  4. sqlserver -- 学习笔记(五)查询一天、一周、一个月记录(DateDiff 函数)(备忘)

    Learn From : http://bjtdeyx.iteye.com/blog/1447300 最常见的sql日期查询的语句 --查询当天日期在一周年的数据 ) --查询当天的所有数据 ) -- ...

  5. umbraco之DocumentType

    DocumentType定义了数据字段,这就像我们在数据库中定义表一样,这个数据字段就像表中的一个字段或者一个列.但不同的是,在umbraco里数据是分等级而不是一个表格性质. 这样就可以使用一个基本 ...

  6. 高效查看MySQL帮助文档的方法

    在mysql的使用过程中, 可能经常会遇到以下问题: 某个操作语法忘记了, 如何快速查找? 如何快速知道当前版本上某个字段类型的取值范围? 当前版本都支持哪些函数?希望有例子说明.. 当前版本是否支持 ...

  7. .Net魔法堂:log4net详解

    一.作用 提供一个记录日志的框架,可以将日志信息记录到文件.控制台.Windows事件日志和数据库(MSSQL.Acess.Oracle.DB2和SQLite等). 二.先看看示例,感受一下吧   c ...

  8. 一起Polyfill系列:Function.prototype.bind的四个阶段

    昨天边参考es5-shim边自己实现Function.prototype.bind,发现有不少以前忽视了的地方,这里就作为一个小总结吧. 一.Function.prototype.bind的作用 其实 ...

  9. 30 分钟快快乐乐学 SQL Performance Tuning

    转自:http://www.cnblogs.com/WizardWu/archive/2008/10/27/1320055.html 有些程序员在撰写数据库应用程序时,常专注于 OOP 及各种 fra ...

  10. 循序渐进开发WinForm项目(3)--Winform界面层的项目设计

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...