您的位置:首页 > 理论基础 > 数据结构算法

算法和数据结构总结---单链表

2020-01-15 07:35 330 查看

链表

链表可以说是一种很基本的数据结构,链表通常以一种特定的组合将元素链接在一起,以便可以对元素实现方便的管理维护。这一点和我们常常使用的数组很相似,但是链表在最多的情况下可以带来比链表更为优势的操作,链表通常是在系统需要的时候动态开辟的,换句话说链表的存储空间是在程序运行的时候在进行分配的,在许多的时候,我们无法明确的确定数据的大小直接在编译前分配内存,这种动态分配的方法也是链表的优势之一。

单链表

单链表(通常也被成为链表) 链表元素由彼此的内部的一个指针相链接。每个元素包括两个成员:数据成员和一个称为

next
的指针成员,每个元素通过
next
指针指向下一个链表元素,实现链表的链接。链表的开始处称之为链表的 “头”,链表的最后结束部分称为链表的 “尾”。单链表只允许一个一个方向遍历链表,尽管有的时候我们保存链表的指针信息。下图为一个标准的单链表结构。

从物理内存上在看,链表不是连续的内存存储,但是在逻辑上可以理解链表是线性的。我们可以类似的画出链表的内存存储方式:

单链表接口的公共接口

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

返回值:无
描述:这个是单链表初始化函数,必须在链表进行其他操作其使用,List* list是单链表的头尾信息结构体。
当调用

list_destroy
时,
destroy
参数提供一种释放动态内存的方法。当链表中的元素有动态分配的内存,在摧毁链表时必须
free
掉分配的动态内存。
destroy
作为一个用户自己可以设置的析构函数,提高了链表的稳定性,如果链表当中不存在应该释放的动态内存,
destroy
的值应该为
NULL

时间复杂度:O(1) 在链表初始化时,时间是固定的。

void list_destroy(List* list)

返回值:无
描述:销毁链表

list
,在销毁后不可以对
list
进行任何的数组操作,除非重新在对数组进行初始化操作,如果传给list_init函数的形参
destroy
不是
NULL
的话,则每次移除链表元素都要执行该函数一次。

时间复杂度:O(n) n代表链表中元素的个数。

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

返回值:如果成功插入链表返回0,出现错误返回-1
描述:将元素插入

list
指向的单向链表的
element
元素之后,如果
element
NULL
,则新元素插入链表的头部。新元素包含一个指向
data
的指针。

时间复杂度:O(1)

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

返回值:如果返回值成功则返回0,出现错误则返回-1
描述:函数的功能是移除,如果

element
等于
NULL
则移除头元素。调回返回后
data
指向已移除的那个链表元素的数据,由用户可以灵活的使用
data
的存储空间。

复杂度:O(1) 移除链表元素的时间是固定的

宏定义

#define list_size(list) ((list)->size)

#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)

复杂度:O(1) 宏定义的时间都是固定的,为了提高代码的可读性

单链表的头文件list.h

//list.h
#pragma
#ifndef LIST_H
#define LIST_H
#include <stdlib.h>

//Define a structure for list element
typedef struct ListElement_
{
void*			data;
struct		    ListElement_* next;
}ListElement;
//Define a structure for linked element
typedef struct List_
{
int				size;
int				(*match)(const void* key1, const void* key2);
void			(*destroy)(void* data);
ListElement*	head;
ListElement*    tail;

} List;

// Public Interface
void list_init(List* list, void	(*destroy)(void* data));
int  list_ins_next(List* list, ListElement* element, const void* data);
int  list_rem_next(List* list, ListElement* element, void** data);

#define list_size(list) ((list)->size)
#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));

链表初始化的操作很简单,只要创造一个空链表就可以,将存储链表头和尾信息的结果体置
为空指针,链表的长度

size
为0。链表的析构函数置为我们指定的析构函数。

void list_init(List* list, void	(*destroy)(void* data))
{
list->size = 0;
list->destroy = destroy;
list->head = NULL;
list->tail = NULL;
return;
}
链表摧毁
void list_destroy(List* list)

void list_destroy(List* list)
函数用于销毁链表,其作用是要移除链表中所有的元素,如果链表初始化时
list
中的
destroy
参数不为0,则表示在移走每个链表元素时还必须对链表元素的数据进行处理,所以思路为当链表的长度不为0和析构函数的指针不是空指针时,不停的移走头链表,并返回移除链表的数据指针通过析构函数对数据释放,最后在
list
的区域清理干净。

void list_destroy(List* list)
{
void* data;
// 移走每个链表元素
while (list_size(list) > 0)
{
if (list_rem_next(list,NULL,(void**)&data) == 0 && list->destroy != NULL)
{
list->destroy(data);
}
}
memset(list, 0, sizeof(list));
return;
}
链表后插
int list_ins_next(List* list, ListElement* element, const void* data)

链表后插的操作很简单,插入就两种大情况要分类讨论,一种是当

int list_ins_next(List* list, ListElement* element, const void* data)
中的element元素为NULL是表示插入头接单,另一种就是在一个链表元素后插。

  1. element
    为 0 插入头链表。当整个链表不存在链表元素时,这个时候链表头即是链表为链表尾,
    这个时候要更新
    list
    tail
    信息。
    当整个链表存在链表元素时候,这个时候新的链表就取代原来的链表头,
    list
    的链表头信息就更新。
  2. element
    不为 0 就要注意你插入的元素是不是链表尾,如果是链表尾,则要更新
    list
    的链表尾指针。否则就让新链表的的next指针指向
    element
    netx
    指针指向的链表,
    element
    next
    指针指向新的链表元素。

最后整个

list
的长度要加1;

int  list_ins_next(List* list, ListElement* element, const void* data)
{
ListElement* new_element;
if ((new_element = (ListElement*)malloc(sizeof(ListElement)))== NULL)
{
return -1;
}
new_element->data = (void*)data;
if (element == NULL)
{
if (list_size(list) == 0)
{
list->tail = new_element;
}
new_element->next = list->head;
list->head = new_element;
}
else
{
if (element == NULL)
{
list->tail = new_element;
}
new_element->next = element->next;
element->next = element;
}
list->size++;
return 0;
}
链表后删
int list_rem_next(List* list, ListElement* element, void** data)

这个函数的功能就是要移除

int list_rem_next(List* list, ListElement* element, void** data)
element
元素后的一个元素链表,并不是元素释放。这一点要清楚。和插入链表的操作相同这个也要分类两种情况进行分析:移除头结点和其他结点。

  1. element
    为 0 移除头链表。先把头结点的数据指针位置保持,并用
    old_element
    保持要移除的链表的空间内存地址,这个时候就用原本头结点的下一链表取代原本的头结点。如果这个时候链表的长度为1。
  2. 如果
    element
    的下一个元素就是空指针,那不能删,返回错误,否则把
    element
    的下一个链表的数据空间和链表位置保存。利用
    element->next = element->next->next;
    更新链表结构。如果删掉是最后一个链表结点,要注意更新链表信息的尾信息。

最后整个list的长度要减1;

int  list_rem_next(List* list, ListElement* element, void** data)
{
ListElement* old_element;
if (list_size(list) == 0)
{
return -1;
}
if (element == NULL)
{
*data = list->head->data;
old_element = list->head;
list->head = list->head->next;

if (list->size == 1)
{
list->tail = NULL;
}
}
else
{
if (element->next == NULL)
{
return -1;
}
*data = element->next->data;
old_element = element->next;
element->next = element->next->next;
if (element->next == NULL)
{
list->tail = element;
}
}

free(old_element);
list->size--;
return 0;
}

完整代码

//list.h
#pragma
#ifndef LIST_H
#define LIST_H
#include <stdlib.h>

//Define a structure for list element
typedef struct ListElement_
{
void*			data;
struct		    ListElement_* next;
}ListElement;
//Define a structure for linked element
typedef struct List_
{
int				size;
int				(*match)(const void* key1, const void* key2);
void			(*destroy)(void* data);
ListElement*	head;
ListElement*    tail;

} List;

// Public Interface
void list_init(List* list, void	(*destroy)(void* data));
void list_destroy(List* list);
int  list_ins_next(List* list, ListElement* element, const void* data);
int  list_rem_next(List* list, ListElement* element, void** data);

#define list_head(list) ((list)->head)
#define list_size(list) ((list)->size)
#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
// list.c
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
#include <string.h>

/*
void list_init(List* list, void	(*destroy)(void* data))
list_init 链表初始化
链表初始化只需要把链表的size成员设置为0,把函数指针成员设置为析构函数函数
*/
void list_init(List* list, void	(*destroy)(void* data))
{
list->size = 0;
list->destroy = destroy;
list->head = NULL;
list->tail = NULL;
return;
}
/*
void list_destroy(List* list);
链表摧毁函数,功能就是摧毁链表中的全部元素,如果调用list_init时
destroy的参数不为NULL,则当每个元素被移除的时候都将调用list_destroy
一次
*/
void list_destroy(List* list)
{
void* data;
// Remove each element
while (list_size(list) > 0)
{
if (list_rem_next(list,NULL,(void**)&data) == 0 && list->destroy != NULL)
{
list->destroy(data);
}
}
memset(list, 0, sizeof(list));
return;
}

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

int  list_ins_next(List* list, ListElement* element, const void* data)
{
ListElement* new_element;
if ((new_element = (ListElement*)malloc(sizeof(ListElement)))== NULL)
{
return -1;
}
new_element->data = (void*)data;
if (element == NULL)
{
if (list_size(list) == 0)
{
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 = element;
}
list->size++;
return 0;
}
/*
int  list_rem_next(List* list, ListElement* element, void* data)
*/
int  list_rem_next(List* list, ListElement* element, void** data)
{
ListElement* old_element;
if (list_size(list) == 0)
{
return -1;
}
if (element == NULL)
{
*data = list->head->data;
old_element = list->head;
list->head = list->head->next;

if (list->size == 1)
{
list->tail = NULL;
}
}
else
{
if (element->next == NULL)
{
return -1;
}
*data = element->next->data;
old_element = element->next;
element->next = element->next->next;
if (element->next == NULL)
{
list->tail = element;
}
}

free(old_element);
list->size--;
return 0;
}
  • 点赞
  • 收藏
  • 分享
  • 文章举报
Kroner 发布了29 篇原创文章 · 获赞 5 · 访问量 1662 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: