• 【数据结构】之链表(C语言描述)


      链表是线性表的一种,是一种物理存储单元上非连续的存储结构,链表中的数据元素之间是通过指针链接实现的。

      链表由一系列节点组成,节点可以在运行时动态的生成。

      链表中国的每个节点分为两部分:一部分是存储数据的数据域,另一部分是存储下一个节点的地址的指针域。

      如果要在链表中查找某个位置的元素,需要从第一个元素开始,循着指针链一个节点一个节点的找,不像顺序表那样可以直接通过下标获取对应的元素,因此,链表不适合查询操作频繁的场景。

      如果要在链表中添加或删除某个元素,只需要通过指针操作,将要操作的节点链入指针链或从指针链中移除即可,不必像顺序表那样需要移动之后的所有节点,因此,链表更适合增删操作频繁的场景。

      使用链表不需要像顺序表那样,处处考虑要不要给表扩容,链表中的元素都是在运行时动态生成的,因此可以充分利用计算机的内存空间;但是,由于链表中的每个元素都需要包括数据域和指针域两块区域,因此空间开销也是比较大的。

      下面是用 C语言 描述的链表的代码:

      链表数据结构的头文件LinkedList.h中的代码:

    /**
     * 线性表(链式存储)
     * 注意:线性表的第一个节点不存储任何数据,只起到表头的作用
     */
    #include <stdio.h>
    #include <stdlib.h>
    
    // 类型定义
    typedef int Status;         // 方法的返回值
    typedef int LinkedElemType; // LinkedList数据结构中节点中存储的数据的类型
    
    // LinkedList中的节点的结构体
    typedef struct LinkedNode {
        LinkedElemType value;
        struct LinkedNode* nextNode;
    } LinkedNode;
    
    // LinkedList数据结构
    typedef struct LinkedList {
        LinkedNode* data;
        int length;
    } LinkedList;
    
    // 1.创建带头结点的空链表
    void initLinkedList(LinkedList* L) {
        L->data = (LinkedNode*)malloc(sizeof(LinkedNode));
        if(L->data != NULL) {
            L->data->nextNode = NULL;
            L->length = 0;
            printf("创建链表成功!
    ");
        }
    }
    
    // 2.销毁链表
    void destroyLinkedList(LinkedList* L) {
        LinkedNode* node = NULL;
        if(L->data == NULL) {
            printf("链表不存在!
    ");
            exit(1);
        }
        while(L->data != NULL) {
            node = L->data;
            L->data = L->data->nextNode;
            free(node);
        }
        printf("销毁链表成功!
    ");
    }
    
    // 3.清空链表(使链表只剩下表头)
    void clearLinkedList(LinkedList* L) {
        LinkedNode* node = NULL;
        if(L->data == NULL) {
            printf("链表不存在!
    ");
            exit(1);
        }
        while(L->data->nextNode != NULL) {
            node = L->data->nextNode;
            L->data->nextNode = node->nextNode;
            free(node);
        }
        L->length = 0;
        printf("清空链表成功!
    ");
    }
    
    // 4.返回链表的长度
    int getLinkedListSize(LinkedList* L) {
        return L->length;
    }
    
    // 5.判断链表中是否存储着数据
    Status isLinkedListEmpty(LinkedList* L) {
        return L->data->nextNode == NULL;
    }
    
    // 6.返回链表中第i个数据元素的值
    LinkedElemType getLinkedElemAtPos(LinkedList* L, int i) {
        LinkedNode* node = NULL;
        int index = -1;
        if(L->data == NULL) {
            printf("链表不存在!
    ");
            exit(1);
        }
        if(i < 1 || i > L->length) {
            printf("下标无效!
    ");
            exit(1);
        }
        node = L->data;
        for(index = 1; index <= i; index++) {
            node = node->nextNode;
        }
        return node->value;
    }
    
    // 7.在链表L中检索值为e的数据元素(第一个元素)
    LinkedNode* getLinkedElem(LinkedList* L, LinkedElemType e) {
        LinkedNode* node = L->data;
        if(L->data == NULL) {
            printf("链表不存在!
    ");
            return NULL;
        }
        while(node->nextNode != NULL) {
            node = node->nextNode;
            if(e == node->value) {
                return node;
            }
        }
        return NULL;
    }
    
    // 8.在链表L中第i个数据元素之前插入数据元素e
    void insertLinkedElemBefore(LinkedList* L, int i, LinkedElemType e) {
        LinkedNode* priorNode = L->data;
        LinkedNode* nextNode = NULL;
        LinkedNode* newNode = NULL;
        int index = -1;
        if(L->data == NULL) {
            printf("链表不存在!
    ");
            exit(1);
        }
        if(i < 1 || i >= L->length) {
            printf("下标无效!
    ");
            exit(1);
        }
        newNode = (LinkedNode*)malloc(sizeof(LinkedNode));
        newNode->value = e;
        for(index = 1; index < i; index++) {
            priorNode = priorNode->nextNode;
            nextNode = priorNode->nextNode;
        }
        priorNode->nextNode = newNode;
        newNode->nextNode = nextNode;
        L->length++;
        printf("在第%d个位置插入%d成功!
    ", i, e);
    }
    
    // 9.在表尾添加元素e
    void insertElemAtEnd(LinkedList* L, LinkedElemType e) {
        LinkedNode* currNode = NULL;
        LinkedNode* newNode = NULL;
        if(L->data == NULL) {
            printf("链表不存在!");
            exit(1);
        }
        currNode = L->data;
        while(currNode->nextNode != NULL) {
            currNode = currNode->nextNode;
        }
        newNode = (LinkedNode*)malloc(sizeof(LinkedNode));
        newNode->value = e;
        newNode->nextNode = NULL;
        currNode->nextNode = newNode;
        L->length++;
        printf("成功在表尾添加元素%d
    ", e);
    }
    
    // 10.删除链表中第i个位置上的元素
    void deleteLinkedElemAtPos(LinkedList* L, int i) {
        LinkedNode* priorNode = L->data;
        LinkedNode* nextNode = NULL;
        LinkedNode* oldNode = NULL;
        int index = -1;
        if(L->data == NULL) {
            printf("链表不存在!
    ");
            exit(1);
        }
        if(i < 1 || i > L->length) {
            printf("下标无效!
    ");
            exit(1);
        }
        for(index = 1; index < i; index++) {
            priorNode = priorNode->nextNode;
            nextNode = priorNode->nextNode;
        }
        oldNode = nextNode;
        priorNode->nextNode = nextNode->nextNode;
        L->length--;
        printf("成功删除第%d个位置上的元素%d!
    ", i, oldNode->value);
        free(oldNode);
    }
    
    // 11.返回给定元素的前驱节点
    LinkedNode* getPriorLinkedElem(LinkedList* L, LinkedNode* e) {
        LinkedNode* priorNode = NULL;
        LinkedNode* currNode = NULL;
        if(L->data == NULL) {
            printf("链表不存在!");
            return NULL;
        }
        if(e == L->data->nextNode) {
            return L->data->nextNode;
        }
        priorNode = L->data;
        currNode = priorNode->nextNode;
        while(currNode->nextNode != NULL) {
            if(currNode == e) {
                return priorNode;
            }
            priorNode = currNode;
            currNode = priorNode->nextNode;
        }
        return NULL;
    }
    
    // 12.返回给定元素的后继节点
    LinkedNode* getNextLinkedElem(LinkedList* L, LinkedNode* e) {
        LinkedNode* currNode = NULL;
        if(L->data == NULL) {
            printf("链表不存在!
    ");
            return NULL;
        }
        currNode = L->data;
        while(currNode->nextNode != NULL) {
            if(currNode == e) {
                return currNode->nextNode;
            }
            currNode = currNode->nextNode;
        }
        return NULL;
    }
    
    // 13.遍历链表
    void traverseLinkedList(LinkedList* L) {
        LinkedNode* currNode = NULL;
        if(L->data == NULL) {
            printf("链表不存在!
    ");
            exit(1);
        }
        currNode = L->data->nextNode;
        while(currNode != NULL) {
            printf("%-4d", currNode->value);
            currNode = currNode->nextNode;
        }
        printf("
    ");
    }
    
    testLinkedList() {
        // 声明链表对象
        LinkedList list;
        // 测试节点
        LinkedNode* testNode;
        // 初始化链表
        initLinkedList(&list);
        // 销毁链表
        // destroyLinkedList(&list);
        // 清空链表
        clearLinkedList(&list);
        // 获取链表长度
        printf("当前链表长度:%d
    ", getLinkedListSize(&list));
        // 判断链表中是否存储着数据
        printf("链表中是否存储着数据:%s
    ", isLinkedListEmpty(&list) ? "" : "");
        // 在表尾添加元素
        insertElemAtEnd(&list, 1);
        insertElemAtEnd(&list, 2);
        insertElemAtEnd(&list, 4);
        insertElemAtEnd(&list, 5);
        insertElemAtEnd(&list, 6);
        // 遍历链表中的元素
        traverseLinkedList(&list);
        // 获取某个位置的元素值
        printf("当前链表中第2个元素的值是:%d
    ", getLinkedElemAtPos(&list, 2));
        // 在某元素前插入新元素
        insertLinkedElemBefore(&list, 3, 3);
        insertLinkedElemBefore(&list, 3, 3);
        // 遍历链表中的元素
        traverseLinkedList(&list);
        // 删除某位置的元素
        deleteLinkedElemAtPos(&list, 4);
        // 遍历链表中的元素
        traverseLinkedList(&list);
        // 获取对应值的第一个元素
        testNode = getLinkedElem(&list, 3);
        // 返回某节点的前驱节点
        printf("测试节点的前驱节点的值是:%d
    ", getPriorLinkedElem(&list, testNode)->value);
        // 返回某节点的后继节点
        printf("测试节点的后继节点的值是:%d
    ", getNextLinkedElem(&list, testNode)->value);
    }

      主函数所在的文件main.c中的代码:

    #include <LinkedList.h>
    
    //主函数
    int main() {
        testLinkedList(); // 线性表(链式存储)结构的测试
        return 0;
    }

      运行结果如下:

    创建链表成功!
    清空链表成功!
    当前链表长度:0
    链表中是否存储着数据:否
    成功在表尾添加元素1
    成功在表尾添加元素2
    成功在表尾添加元素4
    成功在表尾添加元素5
    成功在表尾添加元素6
    1   2   4   5   6
    当前链表中第2个元素的值是:2
    在第3个位置插入3成功!
    在第3个位置插入3成功!
    1   2   3   3   4   5   6
    成功删除第4个位置上的元素3!
    1   2   3   4   5   6
    测试节点的前驱节点的值是:2
    测试节点的后继节点的值是:4
    
    Process returned 0 (0x0)   execution time : 0.031 s
    Press any key to continue.

      链表分为好几种,上面介绍的这种链表叫做线性链表,它的特点是:线性存储,只能通过前一个节点找到后一个节点,不能通过后一个节点找到前一个节点;

      链表还有其他的几种,下面来简单介绍:

    1、 循环链表:

      链表的头尾节点相连,形成一个环。

      实现方式:我们只需要将线性链表稍加改造,将尾节点的下一个节点的指针指向头结点即可。

    2、双向链表:

      既可以通过前一个节点找到后一个节点,也可以通过后一个节点找到前一个节点。

      实现方式:在线性链表的节点数据结构体中,不但要定义指向一个节点的指针,也要定义指向前一个节点的指针。

  • 相关阅读:
    php函数
    字符滚动效果0515复习笔记+注释
    0514复习Windows操作及DOM的使用
    超链接文字随状态改变变色2016-0514
    js笔记之影音插入0514
    js类型转换,运算符,语句
    JS学习1简介
    json文件的json.parse(data)方法时候碰到的问题
    样式属性
    css样式表0513补
  • 原文地址:https://www.cnblogs.com/itgungnir/p/6643962.html
Copyright © 2020-2023  润新知