线性表之双向链表
一、双向链表简单介绍
双向链表是单链表的一种扩展,单链表中每一个节点只保存一个指针域,该指针域唯一的指向当前节点的直接后继,而双向链表在单链表节点类型的基础之上,额外增加了一个前驱指针域,该指针域指向当前节点的直接前驱,正是因为有了直接前驱指针,双向链表的插入和删除算法和单链表也略有不同
二、时间复杂度
2.1 查找数据元素
在双向链表中
查找
一个元素同样需要遍历链表中的元素,因此时间复杂度是O(N)
2.2 插入一个元素的时间复杂度
在双向链表中
插入
一个元素,时间复杂度是O(1)
,因为插入操作仅需要对指针进行常数级别的操作,调整指针指向即可
2.3 删除一个元素的时间复杂度
在双向链表中
删除
一个元素,时间复杂度是O(1)
,因为双向链表的删除操作也只需要调整指针指向即可,这也是常数级别的时间复杂度
三、双向链表的C语言实现
3.1 头文件定义
#ifndef _DOUBLYLINKEDLIST_H
#define _DOUBLYLINKEDLIST_H
// 定义双向链表元素类型
typedef int elementType;
// 声明双向链表结构类型
struct doublyLinkedlist;
// 声明双向链表迭代器类型
struct doublyLinkedlistIterator;
// 声明双向链表的迭代器指针类型
typedef struct doublyLinkedlist *doublyLinkedlistPtr;
// 声明双向链表迭代器指针类型
typedef struct doublyLinkedlistIterator *doublyLinkedlistIteratorPtr;
// 定义元素指针类型
typedef elementType *elementTypePtr;
// 定义双向链表元素比较函数
typedef int (*cmpFunc)(elementType, elementType);
// 从一个数组中创建双向链表
// arr是指向数组基地址的指针
// len是数组元素的个数
// mode表示采用的是头插法还是尾插法,如果mode为0,那么是头插法,否则是尾插法
doublyLinkedlistPtr createWithArr(elementTypePtr arr, int len, int mode);
// 清空双向链表
void makeEmpty(doublyLinkedlistPtr list);
// 判断双向链表是否为空
// 如果为空,返回true,否则返回false
int isEmpty(doublyLinkedlistPtr list);
// 统计双向链表元素的个数
int length(doublyLinkedlistPtr list);
// 判断指定的元素是否是双向链表的最后一个元素
// 如果是返回1,否则返回0
int isLast(elementType e, doublyLinkedlistPtr list, cmpFunc cmp);
// 返回指定元素在双向链表中的位置
// 如果不存在返回-1
int locate(elementType e, doublyLinkedlistPtr list, cmpFunc cmp);
// 寻找指定元素e在双向链表中的直接前驱
// 如果不存在,返回0,否则返回1
// e的直接前驱使用pre指针接收
int findPrevious(elementType e, elementTypePtr pre, doublyLinkedlistPtr list, cmpFunc cmp);
// 返回指定元素e在双向链表中的直接后继
// 如果不存在,那么返回0,否则返回1
// e的直接后继使用next指针接收
int findNext(elementType e, elementTypePtr next, doublyLinkedlistPtr list, cmpFunc cmp);
// 返回双向链表中指定位置处的数据元素
// 如果不存在,那么返回0,否则返回1
// 元素使用指针e接收
int findByIndex(int index, elementTypePtr e, doublyLinkedlistPtr list);
// 删除双向链表中第一个出现的元素
// 删除成功,返回1,否则返回0
int deleteElement(elementType e, doublyLinkedlistPtr list, cmpFunc cmp);
// 删除双向链表指定位置上的元素
// 删除成功,返回1,否则返回0
// 被删除的元素使用del指针来接收,该参数可以为NULL
int deleteElementByIndex(int index, elementTypePtr del, doublyLinkedlistPtr list);
// 将e插入在双向链表指定的index位置上,index从0算起
// 如果插入成功,返回1,否则返回0
int insertByIndex(int index, elementType e, doublyLinkedlistPtr list);
// 将元素e追加到双向链表的最后一个位置
void addLast(elementType e, doublyLinkedlistPtr list);
// 将元素追加到双向链表的最后一个位置
void addFirst(elementType e, doublyLinkedlistPtr list);
// 获取一个双向链表的迭代器对象
doublyLinkedlistIteratorPtr getDoublyLinkedlistIterator(doublyLinkedlistPtr list);
// 判断当前迭代器对象中是否还包含下一个可遍历的元素
// 如果包含,返回1,否则返回0
int iteratorHasNext(doublyLinkedlistIteratorPtr iterator);
// 返回迭代器中下一个可遍历的元素
// 确保在调用该方法之前,先调用iteratorHasNext()方法
// 否则该方法的行为将不能提供正常的保证
elementType iteratorNext(doublyLinkedlistIteratorPtr iterator);
// 清空迭代器对象,被清空的迭代器对象可以重新使用
void zeroIterator(doublyLinkedlistIteratorPtr iterator);
// 回收迭代器空间
void freeIterator(doublyLinkedlistIteratorPtr iterator);
// 回收双向链表的空间
void freeDoublyLinkedlist(doublyLinkedlistPtr list);
#endif
3.2 源文件定义
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "doublylinkedlist.h"
// 定义双向链表节点类型
struct doublyLinkedlistNode {
// 节点数据域
elementType elem;
// 节点的直接前驱,如果无,则为NULL
struct doublyLinkedlistNode *pre;
// 节点的直接后继,如果无,则为NULL
struct doublyLinkedlistNode *next;
};
// 定义双向链表节点类型的指针类型
typedef struct doublyLinkedlistNode *doublyLinkedlistNodePtr;
// 定义双向链表节点类型
struct doublyLinkedlist {
doublyLinkedlistNodePtr head;
};
// 定义双向链表迭代器类型
struct doublyLinkedlistIterator {
// 待被迭代的双向链表
doublyLinkedlistPtr list;
// 当前正在被访问的双向链表节点
doublyLinkedlistNodePtr cur;
};
// 错误打印函数
static void fatalOf(const char *msg) {
fprintf(stderr, "file: %s, func: %s, line: %d, info: %s\n",
__FILE__, __func__, __LINE__, msg);
}
// 确保参数不为NULL
// argNum是传入的检查的非NULL的参数的数目
static void ensureArgsNotNull(int argNum, ...) {
va_list valist;
va_start(valist, argNum);
for (int i = 0; i < argNum; i++) {
void *tmp = va_arg(valist, void *);
if (!tmp) {
fatalOf("args null exception!");
}
}
va_end(valist);
}
// 创建一个双向链表节点
// e用于填充当前双向链表的数据域
static doublyLinkedlistNodePtr makeNode(elementType e) {
doublyLinkedlistNodePtr node = (doublyLinkedlistNodePtr) malloc(sizeof(struct doublyLinkedlistNode));
if (!node) {
fatalOf("create node failure!");
}
node->pre = NULL;
node->next = NULL;
node->elem = e;
return node;
}
// 头插法创建双向链表
static void headInsert(elementTypePtr arr, int len, doublyLinkedlistPtr list) {
ensureArgsNotNull(2, arr, list);
if (len < 0) {
fatalOf("数组长度不合法");
}
for (int i = 0; i < len; i++) {
doublyLinkedlistNodePtr node = makeNode(arr[i]);
if (list->head) {
node->pre = NULL;
node->next = list->head;
list->head->pre = node;
} else {
node->pre = NULL;
node->next = NULL;
}
list->head = node;
}
}
// 释放以head为头的双向链表空间
static void freeNodes(doublyLinkedlistNodePtr head) {
doublyLinkedlistNodePtr next = NULL;
while (head) {
next = head->next;
free(head);
head = next;
}
}
// 尾插法创建双向链表
static void tailInsert(elementTypePtr arr, int len, doublyLinkedlistPtr list) {
ensureArgsNotNull(2, arr, list);
doublyLinkedlistNodePtr tail = list->head;
for (int i = 0; i < len; i++) {
doublyLinkedlistNodePtr node = makeNode(arr[i]);
if (!tail) {
list->head = node;
tail = node;
} else {
node->pre = tail;
node->next = NULL;
tail->next = node;
tail = node;
}
}
}
// 从一个数组中创建双向链表
// arr是指向数组基地址的指针
// len是数组元素的个数
// mode表示采用的是头插法还是尾插法,如果mode为0,那么是头插法,否则是尾插法
doublyLinkedlistPtr createWithArr(elementTypePtr arr, int len, int mode) {
doublyLinkedlistPtr list = (doublyLinkedlistPtr) malloc(sizeof(struct doublyLinkedlist));
if (!list) {
fatalOf("创建双向链表失败");
}
list->head = NULL;
switch (mode) {
case 0:
headInsert(arr, len, list);
break;
default:
tailInsert(arr, len, list);
break;
}
return list;
}
// 清空双向链表
void makeEmpty(doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
freeNodes(list->head);
list->head = NULL;
}
// 判断双向链表是否为空
// 如果为空,返回true,否则返回false
int isEmpty(doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
return NULL == list->head;
}
// 统计双向链表元素的个数
int length(doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
int len = 0;
doublyLinkedlistNodePtr cur = list->head;
while (cur) {
len++;
cur = cur->next;
}
return len;
}
// 判断指定的元素是否是双向链表的最后一个元素
// 如果是返回1,否则返回0
int isLast(elementType e, doublyLinkedlistPtr list, cmpFunc cmp) {
ensureArgsNotNull(2, list, cmp);
doublyLinkedlistNodePtr tail = list->head;
while (tail && tail->next) {
tail = tail->next;
}
if (!tail) {
return 0;
}
return !cmp(e, tail->elem);
}
// 返回指定元素在双向链表中的位置
// 如果不存在返回-1
int locate(elementType e, doublyLinkedlistPtr list, cmpFunc cmp) {
ensureArgsNotNull(2, list, cmp);
doublyLinkedlistNodePtr cur = list->head;
int index = 0;
while (cur && cmp(e, cur->elem) != 0) {
cur = cur->next;
index++;
}
if (!cur) {
return 0;
}
return index;
}
// 寻找指定元素e在双向链表中的直接前驱
// 如果不存在,返回0,否则返回1
// e的直接前驱使用pre指针接收
int findPrevious(elementType e, elementTypePtr pre, doublyLinkedlistPtr list, cmpFunc cmp) {
ensureArgsNotNull(2, list, cmp);
doublyLinkedlistNodePtr cur = list->head;
while (cur && cmp(e, cur->elem) != 0) {
cur = cur->next;
}
if (!cur) {
return 0;
}
if (!cur->pre) {
return 0;
}
*pre = cur->pre->elem;
return 1;
}
// 返回指定元素e在双向链表中的直接后继
// 如果不存在,那么返回0,否则返回1
// e的直接后继使用next指针接收
int findNext(elementType e, elementTypePtr next, doublyLinkedlistPtr list, cmpFunc cmp) {
ensureArgsNotNull(2, list, cmp);
doublyLinkedlistNodePtr cur = list->head;
while (cur && cmp(e, cur->elem) != 0) {
cur = cur->next;
}
if (!cur) {
return 0;
}
if (!cur->next) {
return 0;
}
if (next) {
*next = cur->next->elem;
}
return 1;
}
// 返回双向链表中指定位置处的数据元素
// 如果不存在,那么返回0,否则返回1
// 元素使用指针e接收
int findByIndex(int index, elementTypePtr e, doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
doublyLinkedlistNodePtr cur = list->head;
int i = 0;
while (i < index && cur) {
cur = cur->next;
i++;
}
if (i > index || !cur) {
return 0;
}
if (e) {
*e = cur->elem;
}
return 1;
}
// 删除双向链表中第一个出现的元素
// 删除成功,返回1,否则返回0
int deleteElement(elementType e, doublyLinkedlistPtr list, cmpFunc cmp) {
ensureArgsNotNull(2, list, cmp);
doublyLinkedlistNodePtr cur = list->head;
while (cur && cmp(e, cur->elem) != 0) {
cur = cur->next;
}
if (!cur) {
return 0;
}
if (cur->pre && cur->next) {
cur->pre->next = cur->next;
cur->next->pre = cur->pre;
} else if (cur->pre) {
cur->pre->next = NULL;
} else if (cur->next) {
list->head = cur->next;
cur->pre = NULL;
} else {
list->head = NULL;
}
free(cur);
return 1;
}
// 删除双向链表指定位置上的元素
// 删除成功,返回1,否则返回0
// 被删除的元素使用del指针来接收,该参数可以为NULL
int deleteElementByIndex(int index, elementTypePtr del, doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
int i = 0;
doublyLinkedlistNodePtr cur = list->head;
while (i < index && cur) {
i++;
cur = cur->next;
}
if (i > index || !cur) {
return 0;
}
if (cur->pre && cur->next) {
cur->pre->next = cur->next;
cur->next->pre = cur->pre;
} else if (cur->pre) {
cur->pre->next = NULL;
} else if (cur->next) {
list->head = cur->next;
list->head->pre = NULL;
} else {
list->head = NULL;
}
*del = cur->elem;
free(cur);
return 1;
}
// 将e插入在双向链表指定的index位置上,index从0算起
// 如果插入成功,返回1,否则返回0
int insertByIndex(int index, elementType e, doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
doublyLinkedlistNodePtr newNode = makeNode(e);
newNode->next = list->head;
doublyLinkedlistNodePtr pre = newNode;
int i = -1;
while (i < index - 1 && pre) {
pre = pre->next;
i++;
}
if (i > index - 1 || !pre) {
return 0;
}
if (pre == newNode) {
list->head->pre = newNode;
list->head = newNode;
} else if (pre->next) {
newNode->next = pre->next;
pre->next->pre = newNode;
newNode->pre = pre;
pre->next = newNode;
} else {
newNode->next = NULL;
newNode->pre = pre;
pre->next = newNode;
}
return 1;
}
// 将元素e追加到双向链表的最后一个位置
void addFirst(elementType e, doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
doublyLinkedlistNodePtr newNode = makeNode(e);
if (!list->head) {
list->head = newNode;
newNode->pre = NULL;
newNode->next = NULL;
} else {
newNode->next = list->head;
list->head->pre = newNode;
list->head = newNode;
}
}
// 将元素追加到双向链表的最后一个位置
void addLast(elementType e, doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
doublyLinkedlistNodePtr newNode = makeNode(e);
doublyLinkedlistNodePtr tail = list->head;
while (tail && tail->next) {
tail = tail->next;
}
if (!tail) {
list->head = newNode;
newNode->pre = NULL;
newNode->next = NULL;
} else {
newNode->next = tail->next;
newNode->pre = tail;
tail->next = newNode;
}
}
// 获取一个双向链表的迭代器对象
doublyLinkedlistIteratorPtr getDoublyLinkedlistIterator(doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
doublyLinkedlistIteratorPtr iterator = (doublyLinkedlistIteratorPtr) malloc(sizeof(struct doublyLinkedlistIterator));
if (!iterator) {
fatalOf("创建双向链表迭代器失败");
}
iterator->list = list;
iterator->cur = list->head;
return iterator;
}
// 判断当前迭代器对象中是否还包含下一个可遍历的元素
// 如果包含,返回1,否则返回0
int iteratorHasNext(doublyLinkedlistIteratorPtr iterator) {
return iterator->cur != NULL;
}
// 返回迭代器中下一个可遍历的元素
// 确保在调用该方法之前,先调用iteratorHasNext()方法
// 否则该方法的行为将不能提供正常的保证
elementType iteratorNext(doublyLinkedlistIteratorPtr iterator) {
ensureArgsNotNull(1, iterator);
elementType e = iterator->cur->elem;
iterator->cur = iterator->cur->next;
return e;
}
// 清空迭代器对象,被清空的迭代器对象可以重新使用
void zeroIterator(doublyLinkedlistIteratorPtr iterator) {
ensureArgsNotNull(1, iterator, iterator->list);
iterator->cur = iterator->list->head;
}
// 回收迭代器空间
void freeIterator(doublyLinkedlistIteratorPtr iterator) {
ensureArgsNotNull(1, iterator);
free(iterator);
}
// 回收双向链表的空间
void freeDoublyLinkedlist(doublyLinkedlistPtr list) {
ensureArgsNotNull(1, list);
if (list->head) {
freeNodes(list->head);
}
free(list);
}
3.3 Makefile文件编写
cfiles=main.c doublylinkedlist.c
target=doublylinkedlist_test.exe
obj=main.o doublylinkedlist.o
cc=gcc -std=c99
$(target):$(obj)
$(cc) $(obj) -o $(target)
$(obj):$(cfiles)
$(cc) -c $(cfiles)
.PHONY:clean
clean:
del $(target) $(obj)
3.4 部分测试程序
#include <stdio.h>
#include "doublylinkedlist.h"
static void printList(doublyLinkedlistIteratorPtr iterator) {
while (iteratorHasNext(iterator)) {
printf("%d\n", iteratorNext(iterator));
}
}
static int cmp(elementType e1, elementType e2) {
if (e1 < e2) {
return -1;
} else if (e1 > e2) {
return 1;
} else {
return 0;
}
}
int main(void) {
elementType elems[] = {1, 2, 3, 4, 5, 6};
doublyLinkedlistPtr list = createWithArr(elems, 6, 0);
doublyLinkedlistIteratorPtr iterator = getDoublyLinkedlistIterator(list);
printList(iterator);
// makeEmpty(list);
printf("isEmpty %d\n", isEmpty(list));
printf("length: %d\n", length(list));
elementType e = 2;
int pos = locate(e, list, cmp);
printf("%d pos is %d\n", e, pos);
elementType pre = 1000;
e = 5;
if (findPrevious(e, &pre, list, cmp)) {
printf("%d pre is %d\n", e, pre);
} else {
printf("%d not pre\n", e);
}
elementType next = 1000;
e = 5;
if (findNext(e, &next, list, cmp)) {
printf("%d next is %d\n", e, next);
} else {
printf("%d not next\n", e);
}
int index = 1;
if (findByIndex(index, &e, list)) {
printf("pos %d elem is %d\n", index, e);
} else {
printf("pos %d not has elem\n", index);
}
e = 3;
if (deleteElement(e, list, cmp)) {
printf("delete %d success\n", e);
} else {
printf("delete %d failure\n", e);
}
zeroIterator(iterator);
printList(iterator);
index = 0;
e = 1000;
if (deleteElementByIndex(index, &e, list)) {
printf("delete pos %d elem is %d\n", index, e);
zeroIterator(iterator);
printList(iterator);
} else {
printf("delete pos %d failure\n");
}
index = length(list);
printf("index: %d\n", index);
e = 999;
if (insertByIndex(0, e, list)) {
zeroIterator(iterator);
printList(iterator);
} else {
printf("insert failure");
}
addFirst(4444, list);
addLast(4444, list);
zeroIterator(iterator);
printList(iterator);
freeIterator(iterator);
freeDoublyLinkedlist(list);
return 0;
}