数据结构系列-线性表篇

70 阅读10分钟

线性表之单链表

一、单链表简单介绍

单链表是线性表的链式实现,单链表是一种比较简单的结构,单链表由若干个节点组成,节点包含数据域和指针域,其中每一个指针域负责记录单链表中下一个节点的位置

二、时间复杂度

2.1 查找数据元素

单链表不像顺序表那样可以通过索引计算偏移量的方式定位一个元素,想要在单链表中查找一个元素,需要对整个单链表从头到尾进行遍历,因此这需要O(N)的时间复杂度

2.2 插入一个元素的时间复杂度

在单链表中插入一个新的数据元素,只需要对相应的指针进行改变,因此是O(1)的操作

2.3 删除一个元素的时间复杂度

在单链表中删除一个数据元素,也只需要对相应的指针进行改变,因此也是O(1)的操作

三、单链表的C语言实现

3.1 头文件定义

#ifndef _LINKEDLIST_H
#define _LINKEDLIST_H

// 定义单链表的元素类型
typedef int elementType;

// 声明单链表结构类型
struct linkedlist;

// 定义单链表的迭代器对象
struct linkedlistIterator;

// 定义单链表结构指针类型
typedef struct linkedlist *linkedlistPtr;

// 定义单链表迭代器对象的指针类型
typedef struct linkedlistIterator *linkedlistIteratorPtr;

// 定义元素指针类型
typedef elementType *elementTypePtr;

// 定义顺序表元素比较函数
typedef int (*cmpFunc)(elementTypePtr, elementTypePtr);

// 从一个数组中创建单链表
// arr是指向数组基地址的指针
// len是数组元素的个数
// mode表示采用的是头插法还是尾插法,如果mode为0,那么是头插法,否则是尾插法
linkedlistPtr createWithArr(elementTypePtr arr, int len, int mode);

// 清空单链表
void makeEmpty(linkedlistPtr list);

// 判断单链表是否为空
// 如果为空,返回true,否则返回false
int isEmpty(linkedlistPtr list);

// 返回单链表元素的个数
int length(linkedlistPtr list);

// 判断指定的元素是否是单链表的最后一个元素
// 如果是返回1,否则返回0
int isLast(elementTypePtr e, linkedlistPtr list, cmpFunc cmp);

// 返回指定元素在单链表中的位置
// 如果不存在返回-1
int locate(elementTypePtr e, linkedlistPtr list, cmpFunc cmp);

// 寻找指定元素e在单链表中的直接前驱
// 如果不存在,返回0,否则返回1
// e的直接前驱使用pre指针接收
int findPrevious(elementTypePtr e, elementTypePtr pre, linkedlistPtr list, cmpFunc cmp);

// 返回指定元素e在单链表中的直接后继
// 如果不存在,那么返回0,否则返回1
// e的直接后继使用next指针接收
int findNext(elementTypePtr e, elementTypePtr next, linkedlistPtr list, cmpFunc cmp);

// 返回单链表中指定位置处的数据元素
// 如果不存在,那么返回0,否则返回1
// 元素使用指针e接收
int findByIndex(int index, elementTypePtr e, linkedlistPtr list);

// 删除单链表中指定的元素
// 删除成功,返回1,否则返回0
int deleteElement(elementTypePtr e, linkedlistPtr list, cmpFunc cmp);

// 删除单链表指定位置上的元素
// 删除成功,返回1,否则返回0
// 被删除的元素使用del指针来接收,该参数可以为NULL
int deleteElementByIndex(int index, elementTypePtr del, linkedlistPtr list);

// 将e插入在单链表指定的index位置上,index从0算起
// 如果插入成功,返回1,否则返回0
int insertByIndex(int index, elementTypePtr e, linkedlistPtr list);

// 将元素e追加到单链表的最后一个位置
void addLast(elementTypePtr e, linkedlistPtr list);

// 将元素追加到单链表的最后一个位置
void addFirst(elementTypePtr e, linkedlistPtr list);

// 获取一个单链表的迭代器对象
linkedlistIteratorPtr getLinkedlistIterator(linkedlistPtr list);

// 判断当前迭代器对象中是否还包含下一个可遍历的元素
// 如果包含,返回1,否则返回0
int iteratorHasNext(linkedlistIteratorPtr iterator);

// 返回迭代器中下一个可遍历的元素
// 确保在调用该方法之前,先调用iteratorHasNext()方法
// 否则该方法的行为将不能提供正常的保证
elementType iteratorNext(linkedlistIteratorPtr iterator);

// 清空迭代器,被清空了的迭代器可以重新使用 
void zeroIterator(linkedlistIteratorPtr iterator);

// 回收迭代器空间
void freeIterator(linkedlistIteratorPtr iterator);

// 回收单链表的空间
void freeLinkedlist(linkedlistPtr list);

#endif

3.2 源文件定义

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include "linkedlist.h"

// 定义单链表节点类型
struct linkedlistNode {
    elementType element;
    struct linkedlistNode *next;
};

// 定义单链表节点指针类型
typedef struct linkedlistNode *linkedlistNodePtr;

// 定义单链表结构类型
// 定义的单链表是带一个哑结点的单链表
struct linkedlist {
    linkedlistNodePtr dummy;
};

// 定义单链表的迭代器
struct linkedlistIterator {
    // 待迭代的单链表
    linkedlistNodePtr dummy;
    // 当前正在迭代的元素
    linkedlistNodePtr cur;
};

// 错误信息打印函数
static void fatalOf(const char *msg) {
    fprintf(stderr, "file: %s, func: %s, line: %d, info: %s\n", 
            __FILE__, __func__, __LINE__, msg);
}

// 确保传入的参数不为NULL
static void ensureArgNotNull(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("非法NULL参数");
        }
    }
}

// 采用头插法的方式,将arr中的元素插入到list中
static void headInsert(elementTypePtr arr, int len, linkedlistPtr list) {
    for (int i = 0; i < len; i++) {
        linkedlistNodePtr node = (linkedlistNodePtr) malloc(sizeof(struct linkedlistNode));
        if (!node) {
            fatalOf("创建新的单链表节点失败");
        }
        node->element = *(arr + i);
        node->next = list->dummy->next;
        list->dummy->next = node;
    }
}

// 采用尾插法的方式,将arr中的元素插入到list中
static void tailInsert(elementTypePtr arr, int len, linkedlistPtr list) {
    linkedlistNodePtr tail = list->dummy;
    for (int i = 0; i < len; i++) {
        linkedlistNodePtr node = (linkedlistNodePtr) malloc(sizeof(struct linkedlistNode));
        if (!node) {
            fatalOf("创建新的单链表节点失败");
        }
        node->element = *(arr + i);
        node->next = tail->next;
        tail->next = node;
        tail = node;
    }
}

// 销毁从cur开始的链表中的节点
static void freeNodes(linkedlistNodePtr cur) {
    linkedlistNodePtr next = NULL;
    while (cur) {
        next = cur->next;
        free(cur);
        cur = next;
    }
}

// 从一个数组中创建单链表
// arr是指向数组基地址的指针
// len是数组元素的个数
// mode表示采用的是头插法还是尾插法,如果mode为0,那么是头插法,否则是尾插法
linkedlistPtr createWithArr(elementTypePtr arr, int len, int mode) {
    if (!arr || len < 0) {
        fatalOf("非法的参数");
    }

    linkedlistPtr list = (linkedlistPtr) malloc(sizeof(struct linkedlist));
    if (!list) {
        fatalOf("创建单链表失败");
    }
    
    list->dummy = (linkedlistNodePtr) malloc(sizeof(struct linkedlistNode));
    if (!list->dummy) {
        fatalOf("创建单链表的哑结点失败");
    }
    list->dummy->next = NULL;
    
    switch (mode) {
    case 0:
        headInsert(arr, len, list);
        break;
    default:
        tailInsert(arr, len, list);
        break;
    }
    return list;
}

// 清空单链表
void makeEmpty(linkedlistPtr list) {
    ensureArgNotNull(2, list, list->dummy);
    freeNodes(list->dummy->next);
    list->dummy->next = NULL;
}

// 判断单链表是否为空
// 如果为空,返回true,否则返回false
int isEmpty(linkedlistPtr list) {
    ensureArgNotNull(2, list, list->dummy);

    return NULL == list->dummy->next;
}

// 返回单链表元素的个数
int length(linkedlistPtr list) {
    ensureArgNotNull(2, list, list->dummy);

    linkedlistNodePtr cur = list->dummy->next;
    int len = 0;
    while (cur) {
        cur = cur->next;
        len++;
    }
    return len;
}

// 判断指定的元素是否是单链表的最后一个元素
// 如果是返回1,否则返回0
int isLast(elementTypePtr e, linkedlistPtr list, cmpFunc cmp) {
    ensureArgNotNull(4, e, list, list->dummy, cmp);

    linkedlistNodePtr cur = list->dummy->next;
    while (cur && cur->next) {
        cur = cur->next;
    }
    if (!cur) {
        return 0;
    }
    return !cmp(&cur->element, e);
}

// 返回指定元素在单链表中的位置
// 如果不存在返回-1
int locate(elementTypePtr e, linkedlistPtr list, cmpFunc cmp) {
    ensureArgNotNull(4, e, list, list->dummy, cmp);

    linkedlistNodePtr cur = list->dummy->next;
    int pos = 0;
    while (cur && (cmp(&cur->element, e) != 0)) {
        cur = cur->next;
        pos++;
    }
    if (!cur) {
        return -1;
    }
    return pos;
}

// 寻找指定元素e在单链表中的直接前驱
// 如果不存在,返回0,否则返回1
// e的直接前驱使用pre指针接收
int findPrevious(elementTypePtr e, elementTypePtr pre, linkedlistPtr list, cmpFunc cmp) {
    ensureArgNotNull(4, e, list, list->dummy, cmp);

    linkedlistNodePtr preNode = list->dummy;
    linkedlistNodePtr cur = list->dummy->next;

    while (cur && (cmp(&cur->element, e) != 0)) {
        preNode = cur;
        cur = cur->next;
    }
    // 要么当前链表为空,要么当前链表中不存在该元素
    if (!cur) {
        return 0;
    }
    // 如果当前元素是链表中的第一个元素
    if (preNode == list->dummy) {
        return 0;
    }
    // 如果用户设置了pre指针接收
    if (pre) {
        *pre = preNode->element;
    }
    return 1;
}

// 返回指定元素e在单链表中的直接后继
// 如果不存在,那么返回0,否则返回1
// e的直接后继使用next指针接收
int findNext(elementTypePtr e, elementTypePtr next, linkedlistPtr list, cmpFunc cmp) {
    ensureArgNotNull(4, e, list, list->dummy, cmp);

    linkedlistNodePtr cur = list->dummy->next;
    while (cur && (cmp(&cur->element, e) != 0)) {
        cur = cur->next;
    }
    if (!cur) {
        return 0;
    }
    if (!cur->next) {
        return 0;
    }
    if (next) {
        *next = cur->next->element;
    }
}

// 返回单链表中指定位置处的数据元素
// 如果不存在,那么返回0,否则返回1
// 元素使用指针e接收
int findByIndex(int index, elementTypePtr e, linkedlistPtr list) {
    ensureArgNotNull(3, e, list, list->dummy);

    int i = 0;
    linkedlistNodePtr cur = list->dummy->next;
    while (i < index && cur) {
        cur = cur->next;
        i++;
    }
    if (i > index || !cur) {
        return 0;
    }
    *e = cur->element;
    return 1;
}

// 删除单链表中指定的元素
// 删除成功,返回1,否则返回0
int deleteElement(elementTypePtr e, linkedlistPtr list, cmpFunc cmp) {
    ensureArgNotNull(4, e, list, cmp, list->dummy);

    linkedlistNodePtr pre = list->dummy;
    
    while (pre->next && (cmp(&pre->next->element, e)) != 0) {
        pre = pre->next;
    }
    if (!pre->next) {
        return 0;
    }
    linkedlistNodePtr next = pre->next;
    pre->next = next->next;
    *e = next->element;
    free(next);
    return 1;
}

// 删除单链表指定位置上的元素
// 删除成功,返回1,否则返回0
// 被删除的元素使用del指针来接收,该参数可以为NULL
int deleteElementByIndex(int index, elementTypePtr del, linkedlistPtr list) {
    ensureArgNotNull(2, list, list->dummy);

    int i = -1;
    linkedlistNodePtr pre = list->dummy;
    while (i < index - 1 && pre->next) {
        i++;
        pre = pre->next;
    }
    if (i > index - 1 || !pre->next) {
        return 0;
    }
    linkedlistNodePtr delNode = pre->next;
    if (del) {
        *del = delNode->element;
    }
    pre->next = delNode->next;
    free(delNode);
    return 1;
}

// 将e插入在单链表指定的index位置上,index从0算起
// 如果插入成功,返回1,否则返回0
int insertByIndex(int index, elementTypePtr e, linkedlistPtr list) {
    ensureArgNotNull(3, e, list, list->dummy);

    int i = -1;
    linkedlistNodePtr pre = list->dummy;
    while (i < index - 1 && pre) {
        i++;
        pre = pre->next;
    }
    if (i > index - 1 || !pre) {
        return 0;
    }
    linkedlistNodePtr newNode = (linkedlistNodePtr) malloc(sizeof(struct linkedlistNode));
    if (!newNode) {
        fatalOf("创建新的单链表节点失败");
    }
    newNode->element = *e;
    newNode->next = pre->next;
    pre->next = newNode;
    return 1;
}

// 将元素e追加到单链表的最后一个位置
void addLast(elementTypePtr e, linkedlistPtr list) {
    ensureArgNotNull(3, e, list, list->dummy);

    linkedlistNodePtr tail = list->dummy->next;
    while (tail && tail->next) {
        tail = tail->next;
    }
    linkedlistNodePtr newNode = (linkedlistNodePtr) malloc(sizeof(struct linkedlistNode));
    if (!newNode) {
        fatalOf("创建新的单链表节点失败");
    }
    newNode->element = *e;
    if (tail) {
        newNode->next = tail->next;
        tail->next = newNode;
    } else {
        list->dummy->next = newNode;
    }
}

// 将元素追加到单链表的最后一个位置
void addFirst(elementTypePtr e, linkedlistPtr list) {
    ensureArgNotNull(3, e, list, list->dummy);

    linkedlistNodePtr newNode = (linkedlistNodePtr) malloc(sizeof(struct linkedlistNode));
    if (!newNode) {
        fatalOf("创建新的单链表节点失败");
    }
    newNode->element = *e;
    newNode->next = list->dummy->next;
    list->dummy->next = newNode;
}

// 获取一个单链表的迭代器对象
linkedlistIteratorPtr getLinkedlistIterator(linkedlistPtr list) {
    ensureArgNotNull(2, list, list->dummy);
    linkedlistIteratorPtr iterator = (linkedlistIteratorPtr) malloc(sizeof(struct linkedlistIterator));
    if (!iterator) {
        fatalOf("创建迭代器对象失败");
    }
    iterator->dummy = list->dummy;
    iterator->cur = list->dummy;
    return iterator;
}

// 判断当前迭代器对象中是否还包含下一个可遍历的元素
// 如果包含,返回1,否则返回0
int iteratorHasNext(linkedlistIteratorPtr iterator) {
    return iterator->cur->next != NULL;
}

// 返回迭代器中下一个可遍历的元素
// 确保在调用该方法之前,先调用iteratorHasNext()方法
// 否则该方法的行为将不能提供正常的保证
elementType iteratorNext(linkedlistIteratorPtr iterator) {
    elementType e = iterator->cur->next->element;
    iterator->cur = iterator->cur->next;
    return e;
}

// 回收迭代器的内存空间,注意销毁一个迭代器并不会导致原来的链表数据被销毁
void freeIterator(linkedlistIteratorPtr iterator) {
    ensureArgNotNull(1, iterator);
    free(iterator);
}

// 将迭代器置为0位置,可以重新迭代
void zeroIterator(linkedlistIteratorPtr iterator) {
    ensureArgNotNull(1, iterator);
    iterator->cur = iterator->dummy;
}

// 回收单链表的空间
void freeLinkedlist(linkedlistPtr list) {
    ensureArgNotNull(1, list);
    freeNodes(list->dummy);
    list->dummy = NULL;
}

3.3 Makefile文件编写

# 设置编译后的可执行文件名称
run_target=linkedlist_test
# 定义目标文件变量
obj=linkedlist.o main.o
# 定义源文件变量
cfiles=linkedlist.c main.c

# 定义gcc命令
cc99 = gcc -std=c99

$(run_target): $(obj)
	$(cc99) $(obj) -o $(run_target)

$(obj): $(cfiles)
	$(cc99) -c $(cfiles)

.PHONY:
clean:
	del -rf $(obj) $(run_target)

3.4 测试代码

#include <stdio.h>

#include "linkedlist.h"

static int cmp(elementTypePtr e1, elementTypePtr e2) {
    if (*e1 < *e2) {
        return -1;
    } else if (*e1 > *e2) {
        return 1;
    } else {
        return 0;
    }
}

static void print(linkedlistIteratorPtr iterator) {
     while (iteratorHasNext(iterator)) {
        elementType e = iteratorNext(iterator);
        printf("%d\n", e);
    }
}

int main(void) {
    elementType elems[] = {1, 2, 3, 4, 5, 6, 7, 8};
    linkedlistPtr list = createWithArr(elems, 8, 1);
    printf("get iterator...\n");
    linkedlistIteratorPtr iterator = getLinkedlistIterator(list);
    printf("iterator...\n");
    print(iterator);
    // 清空迭代器
    elementType e = 8;
    zeroIterator(iterator);
    printf("is empty: %d\n", isEmpty(list));
    printf("length: %d\n", length(list));
    printf("isLast: %d\n", isLast(&e, list, cmp));
    printf("locate(%d): %d\n", e, locate(&e, list, cmp));
    
    elementType pre = 1000;
    e = 3;
    if (findPrevious(&e, &pre, list, cmp)) {
        printf("%d's pre is %d\n", e, pre);
    } else {
        printf("%d has not pre!\n", e);
    }

    elementType next = 1000;
    e = 3;
    if (findNext(&e, &next, list, cmp)) {
        printf("%d's next is %d\n", e, next);
    } else {
        printf("%d has not next!\n", e);
    }

    int index = 3;
    if (findByIndex(index, &e, list)) {
        printf("the pos %d value is %d\n", index, e);
    } else {
        printf("the pos %d not has value", index);
    }

    e = 8;
    if (deleteElement(&e, list, cmp)) {
        printf("delete %d success\n", e);
    } else {
        printf("delete failure");
    }
    zeroIterator(iterator);
    print(iterator);

    index = 6;
    elementType del;
    if (deleteElementByIndex(index, &del, list)) {
        printf("delete pos %d success, ele is %d\n", index, del);
    } else {
        printf("delete pos %d failure", index);
    }
    zeroIterator(iterator);
    print(iterator);
    
    e = 1000;
    index = 0;
    if (insertByIndex(index, &e, list)) {
        zeroIterator(iterator);
        printf("insert success\n");
        print(iterator);
    } else {
        printf("insert failure\n");
    }

    e = 999;
    addLast(&e, list);
    addFirst(&e, list);
    zeroIterator(iterator);
    print(iterator);

    freeIterator(iterator);
    freeLinkedlist(list);
    return 0;
}