zoukankan      html  css  js  c++  java
  • 单链表

    单链表

    CONTENT

      链表介绍

       实现代码

       写在最后

    一、链表介绍

      
      链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

      单链表时一种在内存上节点物理空间并不连续的线性表,主要优势是空间利用率比较高。

    二、实现代码

     

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    
    /**********线性表---单链表**********/
    // 说明:本代码主观性比较强,仅供参考,欢迎交流和学习
    #define TRUE 1
    #define FALSE 0
    #define OK 1
    #define ERROR 0
    #define INFEASBLE -1
    #define OVERFLOW -2
    
    typedef int Status;
    typedef int Elemtype;
    
    // 定义链表的节点
    typedef struct LNode
    {
        Elemtype data;
        struct LNode *next;
    } LNode, *LinkList;
    // LNode可以代替 struct LNode
    // *LinkList --> *LNode  --> struct LNode* 三者等价
    
    // 基本操作:
    
    // 逆位序输入n个元素,建立代表头节点的单链性表L
    void CreatList_L(LinkList &L, int n);
    // 取出链表中第i个节点中的信息
    Status GetElem_L(LinkList L, int i, Elemtype &e);
    // 在表头后第i个位置之前插入元素e
    Status ListInsert_L(LinkList L, int i, Elemtype e);
    // 删除链表的第i个元素并返回
    Status ListDelet_L(LinkList L, int i, Elemtype &e);
    // 打印链表
    Status ListPrint_L(LinkList L);
    // 合并两个有序的链表为一个链表
    void MergeList_L(LinkList La, LinkList Lb, LinkList Lc);
    // 清空链表
    void ListClear_L(LinkList L);
    // 获取链表长度
    int ListLength_L(LinkList L);
    
    int main()
    {
        LinkList L;
        // LNode *L;  // 两者等效
        // 1 创建链表
        CreatList_L(L, 3);
        // 2 取出链表第2个元素
        Elemtype e;
        GetElem_L(L, 2, e);
        printf("链表第2个元素为:%d
    ", e);
        // 3 打印链表
        ListPrint_L(L);
        // 4 计算链表长度
        printf("L_Length = %d
    ", ListLength_L(L));
        // 5 插入元素
        ListInsert_L(L, 4, 4);
        printf("在第3个节点后插入元素后的链表为:
    ");
        ListPrint_L(L);
        // 6 删除节点
        ListDelet_L(L, 2, e);
        printf("节点已删除,删除的元素为:%d
    ", e);
        printf("链表剩余元素为:
    ");
        ListPrint_L(L);
        // 7 清空链表
        ListClear_L(L);
        printf("链表已清空
    ");
        return 0;
    }
    
    // 逆位序输入n个元素,建立代表头节点的单链性表L
    // 头插法插入元素
    void CreatList_L(LinkList &L, int n)
    {
        L = (LinkList)malloc(sizeof(LNode));
        L->next = NULL;
        // 创建链表头,注意链表头也是一个结构体,但是表头里一般不储存数据
        // 将表头的指针初始化为NULL表示链表的长度为零,因为没有后继
        for (int i = n; i > 0; i--)
        {
            LinkList p = (LinkList)malloc(sizeof(LNode)); // 创建节点
            scanf("%d", &p->data);                        // 输入插入元素
            p->next = L->next, L->next = p;
            // 将p前继指向p后继的指针赋值给p使p->next指向p的后继
            // 同时也将p的前继的指针指向p
            // 实现 p前继->p->p后继
            // 同解如 ListInsert_L()最后
        }
    }
    
    // 取出链表中第i个节点中的信息
    Status GetElem_L(LinkList L, int i, Elemtype &e)
    {
        LinkList p = L->next;
        int j = 0;
        while (p && j < i - 1)
            p = p->next, j++; // 寻找在连表内的第i个元素
        if (!(p->next) || j > i)
            return ERROR; // 若链表长度小于i,返回错误
        e = p->data;      // 获取元素信息
        return OK;
    }
    
    // 在表头后第i个位置之后插入元素e
    Status ListInsert_L(LinkList L, int i, Elemtype e)
    {
        LinkList p = L;
        int j = 0;
        while (p && j < i - 1) // 当遇到尾节点时p == NULL 会结束函数
            p = p->next, j++;
        // p = p->next 作用:将等钱当前节点移动到下一个节点
        // j单纯用于计数,表示节点移动次数
        if (!p || j >= i)
            return ERROR;                                // 若移动到的节点是NULL或者超出链表长度就返回ERROR
        LinkList node = (LinkList)malloc(sizeof(LNode)); // 创建新的节点
        node->data = e;                                  // 给新的节点写入元素
        node->next = p->next;                            // KEY1
        p->next = node;                                  // KEY2
        // 插入前:p->p后   (p后是插入前p后的第一个元素)
        // 插入中:node->p后  KEY1
        //         p->node   KEY2
        // 插入后:p->node->p后
        // 同解如 CreatList_L()最后
        return OK;
    }
    
    // 删除链表的第i个元素并返回
    Status ListDelet_L(LinkList L, int i, Elemtype &e)
    {
        LinkList p = L;
        int j = 0;
        while (p->next && j < i - 1)
            p = p->next, j++;
        // 1. 寻找范围在链表内(while的条件中p->next限制只能在链表内找)
        // 2. 寻找第i个节点,并且让p指向它的前趋
        // 3. 为什么要让p指向前驱?
        //  删除的原理是,让第i个节点的前驱的指针直接指向第i个节点的下一个节点
        if (!(p->next) || j >= i)
            return ERROR;  // 若未找到元素将返回错误
        LinkList q;        // 定义一个临时的指针,指向节点i
        q = p->next;       // KEY1
        p->next = q->next; // KEY2
        e = q->data;       // 写入数据
        // KEY1: p->next指向节点i,所q->next也会指向节点i
        // KEY2:获取节点i的后继的节点地址赋值给q->next会使节点i的前驱与后继连接
        // 删除前 p前驱->p->p后继
        // 删除后 p前驱->p后继
        free(q); // 释放q防止内存积压
        return OK;
    }
    
    // 打印链表
    Status ListPrint_L(LinkList L)
    {
        LinkList pMove = L->next; // 定义一个指针分别去找链表中的每一个元素
        if (!(L->next))
        {
            printf("NULL
    ");
            return ERROR;
        }
        while (pMove)
        {
            printf("%d ", pMove->data); // 输出的格式根据元素的不同更换
            pMove = pMove->next;        // 移动到下一个节点
        }
        putchar('
    ');
        return OK;
    }
    
    // 合并两个有序的链表为一个链表(使用前提La与Lb为有序表)
    void MergeList_L(LinkList La, LinkList Lb, LinkList Lc)
    {
        LinkList pa, pb, pc;          // 创建三个指针,以读取链表
        pa = La->next, pb = Lb->next; // 初始化La和Lb的指针
        Lc = pc = La;                 // 以La为表头
        while (pa && pb)
            if (pa->data <= pb->data)
                pc->next = pa, pc = pa, pa = pa->next; // pc是Lc的指针,不断为Lc连接新的节点,然后使pc后移到新的节点,最后又将pa移动到La的下一个节点
            else
                pc->next = pb, pc = pb, pb = pb->next; // 同理
        pc->next = pa ? pa : pc;
        // 插入链表剩余的部分,如果pa是空说明pc不空,就把pc的后半段插入Lc,否则pb的后半段插入Lc
        free(Lb); //节点Lb已经没有作用了,释放内存
    }
    
    // 清空链表
    void ListClear_L(LinkList L)
    {
        LinkList pClr, p;
        p = pClr = L->next;
        // p是移动指针不断移动到下一个节点,pClr应释放的节点
        while (p)
        {
            p = p->next;
            free(pClr);
            pClr = p;
        }
        free(pClr);
        L->next = NULL;
        return;
    }
    
    // 获取链表长度
    int ListLength_L(LinkList L)
    {
        LinkList p = L->next;
        int count = 0;
        while (p)
            p = p->next, count++; // 节点不为空时,执行循环并计次
        return count;
    }

     

    三、写在最后

      代码个人观点比较强,若代码中存在bug欢迎各位reader指出。

     

  • 相关阅读:
    《VR入门系列教程》之21---使用Unity开发GearVR应用
    《VR入门系列教程》之20---使用Oculus移动端SDK
    《VR入门系列教程》之19---GearVR开发初识
    《VR入门系列教程》之18---Oculus代码剖析
    《VR入门系列教程》之17---发布第一个应用
    《VR入门系列教程》之16---第一个OculusVR应用
    阿里巴巴开发手册提取信息
    国富论
    富通天下(W 笔试)
    富通天下(T 面试)
  • 原文地址:https://www.cnblogs.com/kirk-notes/p/14550993.html
Copyright © 2011-2022 走看看