zoukankan      html  css  js  c++  java
  • 【数据结构】链表

    【数据结构】链表

    思想

    二维指针作为函数参数的作用:

    • 在函数外部定义一个指针p
    • 在函数内给指针赋值
    • 函数结束后对指针p生效,那么我们就需要二级指针。

    初始化 (头节点要改变)

    • Status InitList(LinkList* L);
    • L指针——>分配的第一个元素的内存空间
    • L的下一个指针——>下一个元素的指针

    销毁(头节点要改变)

    • Status DestroyList(LinkList* L)
    • 创建一个新的节点负责之后分别遍历指向L
    • 从第一个元素开始free,然后把新的指针指向下一个 一直循环 直到新的指针是空指针 结束
    • 最后把第一个节点指向NULL

    置空(头节点值为NULL)

    • Status ClearList(LinkList L)
    • 创建两个指针 首先一个负责遍历的指向每个节点防止后面free的时候把此节点的next清除,其次另外一个由第一个提供
    • 最后把第一个节点的next指向NULL

    判空

    • Status ListEmpty(LinkList L)
    • 链表只有头结点时,L != NULL && L->next == NULL,认为该链表为空

    计数

    • int ListLength(LinkList L)
    • 初始化一个指针指向头节点,循环直到指向最后一个
    • 循环内部有个计数变量

    取值(获取链表中第i个元素,将其存储到e中。)

    • 寻找第i-1个结点,且保证该结点的后继不为NULL
    • 如果遍历到头了,或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
    • 判断遍历到头的方法:p->next == NULL
    • j值:在遍历查找第i个的时候,循环判断条件j < i - 1 j在循环内部自增,保证没有不合规的
    • 如果不合规,即j > i - 1

    查找(返回链表中首个与e满足Compare关系的元素位序,元素e是Compare函数第二个形参)

    • int LocateElem(LinkList L, ElemType e, Status(Compare)(ElemType, ElemType))链表 | e | compare函数关系
    • 循环遍历,判断条件p != NULL && !Compare(p->data, e),i记录次数

    前驱(取元素cur_e的前驱pre,如果存在,将其存储到pre_e中,返回OK)

    • Status PriorElem(LinkList L, ElemType cur_e, ElemType* pre_e)
    • 首先判断是否有前驱,pre->data == cur_e就没有
    • 定义一个指针next指向pre,定义另外个指针指向pre的下一个
    • 然后从第二个开始循环遍历找cur_e,循环时next赋值给pre,然后自增到下一个
    • 找到的cur时next.data,所以pre就是前驱
    • 存储*pre_e = pre->data

    后继(获取元素cur_e的后继,如果存在,将其存储到next_e中,返回OK)

    • Status NextElem(LinkList L, ElemType cur_e, ElemType* next_e)
    • 循环判断条件:pre->next != NULL && pre->data != cur_e
    • 循环内pre自增
    • 找到了过后返回*next_e = pre->next->data;

    插入(向链表第i个位置上插入e,插入成功则返回OK,否则返回ERROR)

    • Status ListInsert(LinkList L, int i, ElemType e)
    • 找到第i个节点:定义一个指针p循环遍历i-1次自增,j值:在遍历查找第i个的时候,循环判断条件j < i - 1 j在循环内部自增,保证没有不合规的。如果不合规,即j > i - 1
    • 生成新节点:定义一个新的指针s,指向malloc后的节点。s.data=e
    • 旧节点——>新节点——>下一个:s->next = p->next(新节点的下一个就是旧节点的下一个); p->next = s;(旧节点的下一个就是s)

    删除(删除链表第i个位置上的元素,并将被删除元素存储到e中。删除成功则返回OK,否则返回ERROR。)

    • Status ListDelete(LinkList L, int i, ElemType* e)(这个时候取e的指针是因为要改变e的值了)
    • 寻找节点,自增,计数防止错误(同上)
    • 节点1——>要删除的节点——>节点2 :节点1的next指向节点2,e的值为要删除的节点的值,并释放要删除的节点

    遍历(用visit函数访问链表L)

    • void ListTraverse(LinkList L, void(Visit)(ElemType))
    • 循环调用visit(p.data),p自增,循环条件:p到最后不是NULL

    头插法创建链表(数据来源于terminal或者文件)

    • Status CreateList_Head(LinkList* L, int n, char* path)(path是定义的FILE不用管)
    • 创建第一个头节点,p指向这个malloc后的节点,然后存数据进p.data,ReadData(fp, “%d”, &(p->data));见后文
    • 先配置p的下一个是L的下一个p.next=(*L).next,然后再把配置好的p赋值给*L,之后循环创建新节点

    尾插法创建链表

    • Status CreateList_Tail(LinkList* L, int n, char* path)
    • 定义指向*L的指针q,循环:生成新节点,配置q的下一个就是新节点,配置完了q自增,继续循环。
    • 最后因为q没有指向,所以q指向NULL

    代码

    链表头文件 LinkList.h

    /*===============================
     * 线性表的链式存储结构(链表)
     *
     * 包含算法: 2.8、2.9、2.10、2.11
     ================================*/
    
    #ifndef LINKLIST_H
    #define LINKLIST_H
    
    #include <stdio.h>
    #include <stdlib.h>     // 提供 malloc、realloc、free、exit 原型
    #include <string.h>     // 提供 strstr 原型
    #include "Status.h"     //**▲01 绪论**//
    
    /* 单链表元素类型定义 */
    typedef int ElemType;
    
    /*
     * 单链表结构
     *
     * 注:这里的单链表存在头结点
     */
    typedef struct LNode {
        ElemType data;      // 数据结点
        struct LNode* next; // 指向下一个结点的指针
    } LNode;
    
    // 指向单链表结点的指针
    typedef LNode* LinkList;
    
    
    /*
     * 初始化
     *
     * 初始化成功则返回OK,否则返回ERROR。
     */
    Status InitList(LinkList* L);
    
    /*
     * 销毁(结构)
     *
     * 释放链表所占内存。
     */
    Status DestroyList(LinkList* L);
    
    /*
     * 置空(内容)
     *
     * 这里需要释放链表中非头结点处的空间。
     */
    Status ClearList(LinkList L);
    
    /*
     * 判空
     *
     * 判断链表中是否包含有效数据。
     *
     * 返回值:
     * TRUE : 链表为空
     * FALSE: 链表不为空
     */
    Status ListEmpty(LinkList L);
    
    /*
     * 计数
     *
     * 返回链表包含的有效元素的数量。
     */
    int ListLength(LinkList L);
    
    /*
     * ████████ 算法2.8 ████████
     *
     * 取值
     *
     * 获取链表中第i个元素,将其存储到e中。
     * 如果可以找到,返回OK,否则,返回ERROR。
     *
     *【备注】
     * 教材中i的含义是元素位置,从1开始计数,但这不符合编码的通用约定。
     * 通常,i的含义应该指索引,即从0开始计数。
     */
    Status GetElem(LinkList L, int i, ElemType* e);
    
    /*
     * 查找
     *
     * 返回链表中首个与e满足Compare关系的元素位序。
     * 如果不存在这样的元素,则返回0。
     *
     *【备注】
     * 元素e是Compare函数第二个形参
     */
    int LocateElem(LinkList L, ElemType e, Status(Compare)(ElemType, ElemType));
    
    /*
     * 前驱
     *
     * 获取元素cur_e的前驱,
     * 如果存在,将其存储到pre_e中,返回OK,
     * 如果不存在,则返回ERROR。
     */
    Status PriorElem(LinkList L, ElemType cur_e, ElemType* pre_e);
    
    /*
     * 后继
     *
     * 获取元素cur_e的后继,
     * 如果存在,将其存储到next_e中,返回OK,
     * 如果不存在,则返回ERROR。
     */
    Status NextElem(LinkList L, ElemType cur_e, ElemType* next_e);
    
    /*
     * ████████ 算法2.9 ████████
     *
     * 插入
     *
     * 向链表第i个位置上插入e,插入成功则返回OK,否则返回ERROR。
     *
     *【备注】
     * 教材中i的含义是元素位置,从1开始计数
     */
    Status ListInsert(LinkList L, int i, ElemType e);
    
    /*
     * ████████ 算法2.10 ████████
     *
     * 删除
     *
     * 删除链表第i个位置上的元素,并将被删除元素存储到e中。
     * 删除成功则返回OK,否则返回ERROR。
     *
     *【备注】
     * 教材中i的含义是元素位置,从1开始计数
     */
    Status ListDelete(LinkList L, int i, ElemType* e);
    
    /*
     * 遍历
     *
     * 用visit函数访问链表L
     */
    void ListTraverse(LinkList L, void(Visit)(ElemType));
    
    /*
     * ████████ 算法2.11 ████████
     *
     * 头插法创建链表
     *
     *
     *【备注】
     *
     * 教材中默认从控制台读取数据。
     * 这里为了方便测试,避免每次运行都手动输入数据,
     * 因而允许选择从预设的文件path中读取测试数据。
     *
     * 如果需要从控制台读取数据,则path为NULL或者为空串,
     * 如果需要从文件中读取数据,则需要在path中填写文件名信息。
     */
    Status CreateList_Head(LinkList* L, int n, char* path);
    
    /*
     * 尾插法创建链表
     *
     *
     *【备注】
     *
     * 教材中默认从控制台读取数据。
     * 这里为了方便测试,避免每次运行都手动输入数据,
     * 因而允许选择从预设的文件path中读取测试数据。
     *
     * 如果需要从控制台读取数据,则path为NULL或者为空串,
     * 如果需要从文件中读取数据,则需要在path中填写文件名信息。
     */
    Status CreateList_Tail(LinkList* L, int n, char* path);
    
    #endif
    

    链表操作函数文件 LinkList.cpp

    /*===============================
     * 线性表的链式存储结构(链表)
     *
     * 包含算法: 2.8、2.9、2.10、2.11
     ================================*/
    
    #include "LinkList.h" //**▲02 线性表**//
    
    /*
     * 初始化
     *
     * 只是初始化一个头结点。
     * 初始化成功则返回OK,否则返回ERROR。
     */
    Status InitList(LinkList* L) {
        (*L) = (LinkList) malloc(sizeof(LNode));
        if(*L == NULL) {
            exit(OVERFLOW);
        }
        
        (*L)->next = NULL;
        
        return OK;
    }
    
    /*
     * 销毁(结构)
     *
     * 释放链表所占内存,头结点也会被清理。
     */
    Status DestroyList(LinkList* L) {
        LinkList p;
        
        // 确保链表结构存在
        if(L == NULL || *L == NULL) {
            return ERROR;
        }
        
        p = *L;
        
        while(p != NULL) {
            p = (*L)->next;
            free(*L);
            (*L) = p;
        }
        
        *L = NULL;
        
        return OK;
    }
    
    /*
     * 置空(内容)
     *
     * 这里需要释放链表中非头结点处的空间。
     */
    Status ClearList(LinkList L) {
        LinkList pre, p;
        
        // 确保链表存在
        if(L == NULL) {
            return ERROR;
        }
        
        p = L->next;
        
        // 释放链表上所有结点所占内存
        while(p != NULL) {
            pre = p;
            p = p->next;
            free(pre);
        }
        
        L->next = NULL;
        
        return OK;
    }
    
    /*
     * 判空
     *
     * 判断链表中是否包含有效数据。
     *
     * 返回值:
     * TRUE : 链表为空
     * FALSE: 链表不为空
     */
    Status ListEmpty(LinkList L) {
        // 链表只有头结点时,认为该链表为空
        if(L != NULL && L->next == NULL) {
            return TRUE;
        } else {
            return FALSE;
        }
    }
    
    /*
     * 计数
     *
     * 返回链表包含的有效元素的数量。
     */
    int ListLength(LinkList L) {
        LinkList p;
        int i;
        
        // 确保链表存在且不为空表
        if(L == NULL || L->next == NULL) {
            return 0;
        }
        
        i = 0;
        p = L->next;
        
        // 遍历所有结点
        while(p != NULL) {
            i++;
            p = p->next;
        }
        
        return i;
    }
    
    /*
     * ████████ 算法2.8 ████████
     *
     * 取值
     *
     * 获取链表中第i个元素,将其存储到e中。
     * 如果可以找到,返回OK,否则,返回ERROR。
     *
     *【备注】
     * 教材中i的含义是元素位置,从1开始计数,但这不符合编码的通用约定。
     * 通常,i的含义应该指索引,即从0开始计数。
     */
    Status GetElem(LinkList L, int i, ElemType* e) {
        LinkList p;
        int j;
        
        // 确保链表存在且不为空表
        if(L == NULL || L->next == NULL) {
            return ERROR;
        }
        
        p = L;
        j = 0;
        
        // 寻找第i-1个结点,且保证该结点的后继不为NULL
        while(p->next != NULL && j < i - 1) {
            p = p->next;
            ++j;
        }
        
        // 如果遍历到头了,或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
        if(p->next == NULL || j > i - 1) {
            return ERROR;
        }
        
        *e = p->next->data;
        
        return OK;
    }
    
    /*
     * 查找
     *
     * 返回链表中首个与e满足Compare关系的元素位序。
     * 如果不存在这样的元素,则返回0。
     *
     *【备注】
     * 元素e是Compare函数第二个形参
     */
    int LocateElem(LinkList L, ElemType e, Status(Compare)(ElemType, ElemType)) {
        int i;
        LinkList p;
        
        // 确保链表存在且不为空表
        if(L == NULL || L->next == NULL) {
            return 0;
        }
        
        i = 1;          // i的初值为第1个元素的位序
        p = L->next;    // p的初值为第1个元素的指针
        
        while(p != NULL && !Compare(p->data, e)) {
            i++;
            p = p->next;
        }
        
        if(p != NULL) {
            return i;
        } else {
            return 0;
        }
    }
    
    /*
     * 前驱
     *
     * 获取元素cur_e的前驱,
     * 如果存在,将其存储到pre_e中,返回OK,
     * 如果不存在,则返回ERROR。
     */
    Status PriorElem(LinkList L, ElemType cur_e, ElemType* pre_e) {
        LinkList pre, next;
        
        // 确保链表存在且不为空表
        if(L == NULL || L->next == NULL) {
            return ERROR;
        }
        
        // 指向第1个元素
        pre = L->next;
        
        // 第1个元素没有前驱
        if(pre->data == cur_e) {
            return ERROR;
        }
        
        // 指向第2个元素
        next = pre->next;
        
        // 从第2个元素开始,查找cur_e的位置
        while(next != NULL && next->data != cur_e) {
            pre = next;
            next = next->next;
        }
        
        // 如果没找到元素cur_e,查找失败,返回ERROR
        if(next == NULL) {
            return ERROR;
        }
        
        *pre_e = pre->data;
        
        return OK;
    }
    
    /*
     * 后继
     *
     * 获取元素cur_e的后继,
     * 如果存在,将其存储到next_e中,返回OK,
     * 如果不存在,则返回ERROR。
     */
    Status NextElem(LinkList L, ElemType cur_e, ElemType* next_e) {
        LinkList pre;
        
        // 确保链表存在且不为空表
        if(L == NULL || L->next == NULL) {
            return ERROR;
        }
        
        // 指向第1个元素
        pre = L->next;
        
        // 从第1个元素开始,查找cur_e的位置,且保证该结点的后继不为NULL
        while(pre->next != NULL && pre->data != cur_e) {
            pre = pre->next;
        }
        
        // 如果没找到cur_e,或者找到了,但它没有后继,均返回ERROR
        if(pre->next == NULL) {
            return ERROR;
        }
        
        *next_e = pre->next->data;
        
        return OK;
    }
    
    /*
     * ████████ 算法2.9 ████████
     *
     * 插入
     *
     * 向链表第i个位置上插入e,插入成功则返回OK,否则返回ERROR。
     *
     *【备注】
     * 教材中i的含义是元素位置,从1开始计数
     */
    Status ListInsert(LinkList L, int i, ElemType e) {
        LinkList p, s;
        int j;
        
        // 确保链表存
        if(L == NULL) {
            return ERROR;
        }
        
        p = L;
        j = 0;
        
        // 寻找第i-1个结点,且保证该结点本身不为NULL
        while(p != NULL && j < i - 1) {
            p = p->next;
            ++j;
        }
        
        // 如果遍历到头了,或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
        if(p == NULL || j > i - 1) {
            return ERROR;
        }
        
        // 生成新结点
        s = (LinkList) malloc(sizeof(LNode));
        if(s == NULL) {
            exit(OVERFLOW);
        }
        s->data = e;
        s->next = p->next;
        p->next = s;
        
        return OK;
    }
    
    /*
     * ████████ 算法2.10 ████████
     *
     * 删除
     *
     * 删除链表第i个位置上的元素,并将被删除元素存储到e中。
     * 删除成功则返回OK,否则返回ERROR。
     *
     *【备注】
     * 教材中i的含义是元素位置,从1开始计数
     */
    Status ListDelete(LinkList L, int i, ElemType* e) {
        LinkList p, q;
        int j;
        
        // 确保链表存在且不为空表
        if(L == NULL || L->next == NULL) {
            return ERROR;
        }
        
        p = L;
        j = 0;
        
        // 寻找第i-1个结点,且保证该结点的后继不为NULL
        while(p->next != NULL && j < i - 1) {
            p = p->next;
            ++j;
        }
        
        // 如果遍历到头了,或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
        if(p->next == NULL || j > i - 1) {
            return ERROR;
        }
        
        // 删除第i个结点
        q = p->next;
        p->next = q->next;
        *e = q->data;
        free(q);
        
        return OK;
    }
    
    /*
     * 遍历
     *
     * 用visit函数访问链表L
     */
    void ListTraverse(LinkList L, void(Visit)(ElemType)) {
        LinkList p;
        
        // 确保链表存在且不为空表
        if(L == NULL || L->next == NULL) {
            return;
        }
        
        p = L->next;
        
        while(p != NULL) {
            Visit(p->data);
            p = p->next;
        }
        
        printf("
    ");
    }
    
    /*
     * ████████ 算法2.11 ████████
     *
     * 头插法创建链表
     *
     *
     *【备注】
     *
     * 教材中默认从控制台读取数据。
     * 这里为了方便测试,避免每次运行都手动输入数据,
     * 因而允许选择从预设的文件path中读取测试数据。
     *
     * 如果需要从控制台读取数据,则path为NULL或者为空串,
     * 如果需要从文件中读取数据,则需要在path中填写文件名信息。
     */
    Status CreateList_Head(LinkList* L, int n, char* path) {
        int i;
        LinkList p;
        FILE* fp;
        int readFromConsole;    // 是否从控制台读取数据
    
        // 如果没有文件路径信息,则从控制台读取输入
        readFromConsole = path == NULL || strcmp(path, "") == 0;
    
        if(readFromConsole) {
            // 建立头结点
            *L = (LinkList) malloc(sizeof(LNode));
            (*L)->next = NULL;
    
            printf("请输入%d个降序元素:", n);
    
            for(i = 1; i <= n; ++i) {
                // 生成新结点
                p = (LinkList) malloc(sizeof(LNode));
    
                // 填充数据,并插入到链表中
                scanf("%d", &(p->data));
    
                p->next = (*L)->next;
                (*L)->next = p;
            }
        } else {
            // 打开文件,准备读取测试数据
            fp = fopen(path, "r");
            if(fp == NULL) {
                return ERROR;
            }
    
            // 建立头结点
            *L = (LinkList) malloc(sizeof(LNode));
            (*L)->next = NULL;
    
            for(i = 1; i <= n; ++i) {
                // 生成新结点
                p = (LinkList) malloc(sizeof(LNode));
    
                // 填充数据,并插入到链表中
                ReadData(fp, "%d", &(p->data));
    
                p->next = (*L)->next;
                (*L)->next = p;
            }
    
            fclose(fp);
        }
    
        return OK;
    }
    
    /*
     * 尾插法创建链表
     *
     *
     *【备注】
     *
     * 教材中默认从控制台读取数据。
     * 这里为了方便测试,避免每次运行都手动输入数据,
     * 因而允许选择从预设的文件path中读取测试数据。
     *
     * 如果需要从控制台读取数据,则path为NULL或者为空串,
     * 如果需要从文件中读取数据,则需要在path中填写文件名信息。
     */
    Status CreateList_Tail(LinkList* L, int n, char* path) {
        int i;
        LinkList p, q;
        FILE* fp;
        int readFromConsole;    // 是否从控制台读取数据
    
        // 如果没有文件路径信息,则从控制台读取输入
        readFromConsole = path == NULL || strcmp(path, "") == 0;
    
        if(readFromConsole) {
            // 建立头结点
            *L = (LinkList) malloc(sizeof(LNode));
            (*L)->next = NULL;
    
            printf("请输入%d个升序元素:", n);
    
            for(i = 1, q = *L; i <= n; ++i) {
                // 生成新结点
                p = (LinkList) malloc(sizeof(LNode));
    
                // 填充数据,并插入到链表中
                scanf("%d", &(p->data));
    
                q->next = p;
                q = q->next;
            }
    
            q->next = NULL;
        } else {
            // 打开文件,准备读取测试数据
            fp = fopen(path, "r");
            if(fp == NULL) {
                return ERROR;
            }
    
            // 建立头结点
            *L = (LinkList) malloc(sizeof(LNode));
            (*L)->next = NULL;
    
            for(i = 1, q = *L; i <= n; ++i) {
                // 生成新结点
                p = (LinkList) malloc(sizeof(LNode));
    
                // 填充数据,并插入到链表中
                ReadData(fp, "%d", &(p->data));
    
                q->next = p;
                q = q->next;
            }
    
            q->next = NULL;
    
            fclose(fp);
        }
    
        return OK;
    }
    
    

    链表主文件 LinkList-main.cpp

    #include <stdio.h>
    #include "Status.h"
    #include "LinkList.h"                        //**▲02 线性表**//
    
    // 判断data>e是否成立
    Status CmpGreater(ElemType data, ElemType e) {
        return data > e ? TRUE : FALSE;
    }
    
    // 测试函数,打印元素
    void PrintElem(ElemType e) {
        printf("%d ", e);
    }
    
    
    int main(int argc, char** argv) {
        LinkList L;
        int i;
        ElemType e;
        
        printf("████████ InitList 
    ");
        {
            printf("█ 初始化单链表 L ...
    ");
            InitList(&L);
        }
        PressEnterToContinue();
        
        
        printf("████████ ListEmpty 
    ");
        {
            ListEmpty(L) ? printf("█ L 为空!!
    ") : printf("█ L 不为空!
    ");
        }
        PressEnterToContinue();
        
        
        printf("████████ ListInsert 
    ");
        {
            for(i = 1; i <= 8; i++) {
                printf("█ 在 L 第 %d 个位置插入 "%d" ...
    ", i, 2 * i);
                ListInsert(L, i, 2 * i);
            }
        }
        PressEnterToContinue();
        
        
        printf("████████ ListTraverse 
    ");
        {
            printf("█ L 中的元素为:L = ");
            ListTraverse(L, PrintElem);
        }
        PressEnterToContinue();
        
        
        printf("████████ ListLength 
    ");
        {
            printf("█ L 的长度为 %d 
    ", ListLength(L));
        }
        PressEnterToContinue();
        
        
        printf("████████ ListDelete 
    ");
        {
            printf("█ 删除前的元素:L = ");
            ListTraverse(L, PrintElem);
        
            printf("█ 尝试删除 L 中第 6 个元素...
    ");
        
            if(ListDelete(L, 6, &e) == OK) {
                printf("█ 删除成功,被删除元素是:"%d"
    ", e);
            } else {
                printf("█ 删除失败,第 6 个元素不存在!
    ");
            }
        
            printf("█ 删除后的元素:L = ");
            ListTraverse(L, PrintElem);
        }
        PressEnterToContinue();
        
        
        printf("████████ GetElem 
    ");
        {
            GetElem(L, 4, &e);
            printf("█ L 中第 4 个位置的元素为 "%d" 
    ", e);
        }
        PressEnterToContinue();
        
        
        printf("████████ LocateElem 
    ");
        {
            i = LocateElem(L, 7, CmpGreater);
            GetElem(L, i, &e);
            printf("█ L 中第一个元素值大于 "7" 的元素是 "%d" 
    ", e);
        }
        PressEnterToContinue();
        
        
        printf("████████ PriorElem 
    ");
        {
            ElemType cur_e = 6;
        
            if(PriorElem(L, cur_e, &e) == OK) {
                printf("█ 元素 "%d" 的前驱为 "%d" 
    ", cur_e, e);
            } else {
                printf("█ 元素 "%d" 的前驱不存在!
    ", cur_e);
            }
        }
        PressEnterToContinue();
        
        
        printf("████████ NextElem 
    ");
        {
            ElemType cur_e = 6;
        
            if(NextElem(L, cur_e, &e) == OK) {
                printf("█ 元素 "%d" 的后继为 "%d" 
    ", cur_e, e);
            } else {
                printf("█ 元素 "%d" 的后继不存在!
    ", cur_e);
            }
        }
        PressEnterToContinue();
        
        
        printf("████████ ClearList 
    ");
        {
            printf("█ 清空 L 前:");
            ListEmpty(L) ? printf(" L 为空!!
    ") : printf(" L 不为空!
    ");
            
            ClearList(L);
            
            printf("█ 清空 L 后:");
            ListEmpty(L) ? printf(" L 为空!!
    ") : printf(" L 不为空!
    ");
        }
        PressEnterToContinue();
        
        
        printf("████████ DestroyList 
    ");
        {
            printf("█ 销毁 L 前:");
            L ? printf(" L 存在!
    ") : printf(" L 不存在!!
    ");
            
            DestroyList(&L);
            
            printf("█ 销毁 L 后:");
            L ? printf(" L 存在!
    ") : printf(" L 不存在!!
    ");
        }
        PressEnterToContinue();
        
        
        printf("████████ CreateList_Head 
    ");
        {
            LinkList L;
            CreateList_Head(&L, 5, "TestData_Head.txt");
            printf("█ 头插法建立单链表 L = ");
            ListTraverse(L, PrintElem);
        }
        PressEnterToContinue();
        
        
        printf("████████ CreateList_Tail 
    ");
        {
            LinkList L;
            CreateList_Tail(&L, 5, "TestData_Tail.txt");
            printf("█ 尾插法建立单链表 L = ");
            ListTraverse(L, PrintElem);
        }
        PressEnterToContinue();
        
        return 0;
    }
    
    

    测试文件 TestData_Head.txt 和 TestData_Tail.txt

    注意:LinkList.cpp文件操作这一段,可以直接忽略除了数字的其他类型

    // 填充数据,并插入到链表中    
    ReadData(fp, "%d", &(p->data));
    

    文件数据导入函数 ReadData()的思想(后续修改)

    ///fscanf()读取出来的都是字符串,因此不能直接给int类型,不然数据就出现不是你写入txt//中的数据,要把字符串变成数字才行,一下就是数字字符串变成数字atoi()
    //用法如下:a="4560"
    //int b=atoi(a)
    #include<stdio.h>
    #include<time.h>
    #include<stdlib.h>
    #include<string.h>
    int read(int arr[])
    {
           int i=0,n;
           char name[100];
           FILE *fp;//定义文件指针
           if((fp=fopen("data.txt","r"))==NULL)//如果文件名不存在
           {printf("cantfind the file!");}//则输出没有找到文件
           while(!feof(fp))//判断文件是否结束
           {  
                  fscanf(fp,"%s",name);//逐个将文件中的数据放入字符串中
                  arr[i++]=atoi(name);//把字符串转变成数字(int)类型
                  printf("%d
    ",arr[i-1]);
           }
           n=i;//n为数组中数据个数
           fclose(fp);//关闭文件
           return n;//返回n即数据个数的值
    }
    
  • 相关阅读:
    c++ 指定目录下的文件遍历
    c++ 实现键盘钩子
    c++ 用模板类实现顺序储存的线性表
    c++ 递归算法实现排列组合
    matlab 基础知识
    QT 给工程添加图片
    QT5.9 QString和字符串转换的乱码问题
    【内核】——进程3,内核同步
    Java多线程——Thread的native底层实现
    【内核】——文件和文件系统的内部结构4 系统调用的实现
  • 原文地址:https://www.cnblogs.com/SiriusZHT/p/14310785.html
Copyright © 2011-2022 走看看