美文网首页
自学数据结构系列-实现链表

自学数据结构系列-实现链表

作者: 刘朝龙er | 来源:发表于2017-02-23 18:53 被阅读23次

    数据结构就是将大量而复杂的数据类型和特定的存储结构保存到主存储器,并在此基础上为了实现某个功能而执行的相应操作。 -- by 郝斌视频

    今天研究并实现了数据结构中线性结构之一的链表实现。

    链表是有 n 个结点离散分配,彼此通过指针相连的数据集合。

    与数组相比,链表拥有无限空间,插入删除元素快,但是链表的存取速度很慢。

    实现链表的功能

    1. 拼接 append
    2. 添加 insert
    3. 删除 delete
    4. 排序 sort
    5. 获取数组长度 len
    6. 判断数组是否为空 isempty
    7. 打印显示数组数据 show
    // 1. 创建链表
    void init_list(PLINKLIST pHead);
    // 2. 获取链表长度
    int  len_list(PLINKLIST pHead);
    // 3. 判断链表是否为空
    bool isempty_list(PLINKLIST pHead);
    // 5. 拼接链表
    bool append_list(PLINKLIST pHead, int val);
    // 6. 插入数据
    bool insert_list(PLINKLIST pHead, int pos, int val);
    // 7. 删除数据
    bool delete_list(PLINKLIST pHead, int pos, int * pVal);
    // 8. 数组排序
    void sort_list(PLINKLIST pHead);
    // 9. 打印显示
    void show_list(PLINKLIST pHead);
    

    按照上一篇数组实现的步骤,首先我们需要声明一个链表。

    链表的结构体

    1. 链表是一个离散存储,使用指针连接,因此就不需要数组,但是需要一个指针连接下一个对象;
    2. 每个对象都要保存一个一个数据
    typedef struct LinkList {
        int data;// 数据域
        struct LinkList * pNext; // 指针域
    }LINKLIST, *PLINKLIST;
    

    然后创建链表头结点并动态分配空间,每个结点都指向下一个结点,通过一个头结点我们可以获取整个链表的数据,因此需要知道一个头结点。

    /**
     创建链表
    
     @param pHead 操作链表
     */
    void init_list(PLINKLIST pHead)
    {
        pHead = (PLINKLIST)malloc(sizeof(LINKLIST));
        if (NULL == pHead) {
            printf("内存分配失败!\n");
            exit(-1);
        }
        
        pHead->pNext = NULL;
        
        return;
    }
    

    首先,实现我们第一个方法,获取链表的长度,大家也看到了这次声明的链表结构体并不存在长度 len有效长度 cnt

    所以,获取链表长度就没有那么简单了。

    知道链表头结点,通过获取下一个结点,可以获取到所有的结点和数据,那么我们就要遍历所有的结点,知道这个结点指向的下一个结点为 NULL 为止,表示整个链表结束了。

    /**
     获取链表长度
    
     @param pHead 操作链表
     @return 链表长度
     */
    int  len_list(PLINKLIST pHead)
    {
        int len = 0;
        PLINKLIST pNext = pHead;
        while (NULL != pNext->pNext) {
            pNext = pNext->pNext;
            len ++;
        }
        
        return len;
    }
    

    判断整个链表是否空,只要知道链表头结点是否有指向下一个结点

    /**
     判断链表是否为空
    
     @param pHead 操作链表
     @return 链表长度
     */
    bool isempty_list(PLINKLIST pHead)
    {
        return NULL != pHead->pNext;
    }
    

    链表的拼接,需要我们经过 n 次取下一个结点的操作,获取最后一个结点,然后创建一个新的结点,让链表最后一个结点的 pNext 指向我们新的结点。这里 n 就是当前链表的长度。

    /**
     追加链表
    
     @param pHead 操作链表
     @param val 追加数据值
     @return 是否追加成功
     */
    bool append_list(PLINKLIST pHead, int val)
    {
        PLINKLIST pNext = pHead;
        
        while (NULL != pNext->pNext) {
            pNext = pNext->pNext;
        }
        
        PLINKLIST pNew = (PLINKLIST)malloc(sizeof(LINKLIST));
        if (NULL == pNew) {
            printf("内存分配失败!\n");
            exit(-1);
        }
        pNew->data   = val;
        pNext->pNext = pNew;
        pNew->pNext  = NULL;
        
        return true;
    }
    

    链表的插入和拼接需要添加过滤条件,即插入的 pos(操作位置) 不能小于 1 并且不要大与当前链表数据总数 +1 ,删除的 pos 不能小于 1 并且不要大于当前链表数据总数。

    然后通过循环遍历,找到 pos - 1 对应的结点,在此节点进行插入和删除操作。

    插入操作

    1. 需要创建一个新节点
    2. 新节点下一个结点是 pos - 1 节点的下一个结点
    3. pos - 1 结点的下一个结点则变成了新节点。

    删除操作,我们需要释放 pos 结点

    1. 现在我们知道了 pos - 1 结点
    2. 那么 pos 结点就是 pos - 1 节点的下一个结点
    3. 在释放 pos 结点前我们需要修改 pos - 1 结点下一个结点的指向,也就是说 pos - 1 结点的下一个结点指向 pos 结点的下一个结点。
    /**
     插入数据v
    
     @param pHead 操作链表
     @param pos 插入数据位置
     @param val 插入数据值
     @return 是否插入成功
     */
    bool insert_list(PLINKLIST pHead, int pos, int val)
    {
        int index = 0;
        PLINKLIST pNext = pHead;
        while (NULL != pNext->pNext && index < pos - 1) {
            pNext = pNext->pNext;
            index ++;
        }
        
        if (NULL == pNext || index >= pos) {
            printf("插入位置有问题\n");
            return false;
        }
        
        PLINKLIST pNew = (PLINKLIST)malloc(sizeof(LINKLIST));
        if (NULL == pNew) {
            printf("内存分配失败!\n");
            exit(-1);
        }
        pNew->data = val;
        pNew->pNext = pNext->pNext;
        pNext->pNext = pNew;
        
        return true;
    }
    
    /**
     删除数据
    
     @param pHead 操作链表
     @param pos 删除数据位置
     @param pVal 删除数据值
     @return 是否删除数据成功
     */
    bool delete_list(PLINKLIST pHead, int pos, int * pVal)
    {
        int index = 0;
        PLINKLIST pNext = pHead;
        while (NULL != pNext->pNext && index < pos - 1) {
            pNext = pNext->pNext;
            index ++;
        }
        if (NULL == pNext->pNext || index >= pos) {
            printf("删除位置有问题\n");
            return false;
        }
        
        PLINKLIST pCurrent = pNext->pNext;
        *pVal = pCurrent->data;
        pNext->pNext = pCurrent->pNext;
        free(pCurrent);
        pCurrent = NULL;
        
        return true;
    }
    

    最后就是排序和显示操作了,原理跟数组相似,都是通过遍历来实现。

    /**
     数组排序
    
     @param pHead 操作链表
     */
    void sort_list(PLINKLIST pHead)
    {
        int i, j, t;
        PLINKLIST p, q;
        int len = len_list(pHead);
        
        for (i = 0, p = pHead->pNext; i < len - 1; ++ i, p = p->pNext) {
            for (j = i, q = p; j < len; ++ j, q = q->pNext) {
                if (p->data > q->data) {
                    t = p->data;
                    p->data = q->data;
                    q->data = t;
                }
            }
        }
        return;
    }
    
    /**
     打印显示
     
     @param pHead 操作链表
     */
    void show_list(PLINKLIST pHead)
    {
        PLINKLIST pNext = pHead->pNext;
        while (NULL != pNext) {
            printf("%d ", pNext->data);
            pNext = pNext->pNext;
        }
        
        printf("\n");
        return;
    }
    

    到目前为止,C 语言实现链表的任务基本完工,剩下的就是测试和拓展新功能了。

    如果各位客官有兴趣可以在此基础上完善,或者有什么需求也可以提出,如果我能完成也可以分享出来。

    各路大牛轻喷,新手练手,有错请指出。

    下面放出完整代码供大家学习,喜欢请 star 谢谢!

    相关文章

      网友评论

          本文标题:自学数据结构系列-实现链表

          本文链接:https://www.haomeiwen.com/subject/bhgbwttx.html