zoukankan      html  css  js  c++  java
  • 数据结构_线性表之链表(1)

    这是链表的第一篇。链表方便插入删除,但是不是随机存取。实际应用中,用于频繁的增加删除操作。

    1.基础知识。

    单链表:

    <1>带头结点单链表

    L为头指针,指向第一个结点,当有头结点时,第一个结点为头结点;当没有头结点时,第一个结点为存储第一个元素的a1结点

     <2>不带头结点的单链表

     两者区别:通常使用有头结点的链表,因为有头结点比较方便。因为对于类似头插法这样的操作,需要对第一个存储元素的结点进行操作时,很明显带有头结点的头指针可以保持不变,因为元素操作都在头结点与a1结点之间完成;而对于没有头结点的链表,需要频繁改变头指针的位置,才能保持头指针始终指向第一个结点。

    2.基本操作

    单链表模块

    <1>头插法建立单链表:总是在头部操作,进行插值

     代码:

     typedef struct Node{
          int data;
          struct Node *next;
     }Node;
    //头插法创建单链表,插入的元素总在头结点之后
    Node *CreateList_L(Node* &L,int n){//前提L已经初始化完成 
        Node *p;    int num;//p保存结点,num保存数据
        L = (Node*)malloc(sizeof(Node));//创建头结点
        L->next = NULL;
        for(int i=0;i<n;i++){
            p = (Node*)malloc(sizeof(Node));//创建新的结点
            cin>>num;
            p->data = num;//存入结点数据域
            p->next = L->next;//p指向下一个 
            L->next = p;//再将p给单链表L的表头 
        }
        return L; //返回值为Node*型
    }

    <2>尾插法建立单链表:总是在尾部操作,进行插值

    尾插法需要增加一个尾指针,尾指针总是用于指向最后一个尾结点

    第一步:

     第二步:

     以此类推,每一次r都往后移动。

    代码:

     typedef struct Node{
          int data;
          struct Node *next;
     }Node;
    //尾插法创建单链表,插入的元素总在最后一个位置
    Node *CreateList_L(Node* &L,int n){//前提L已经初始化完成 
        Node *p,*r;    int num;//p保存结点,num保存数据,r用于指向最后一个结点
        L = (Node*)malloc(sizeof(Node));//创建头结点
        L->next = NULL;
    r = L;//初始时头尾指针指向同一个结点
    for(int i=0;i<n;i++){ p = (Node*)malloc(sizeof(Node));//创建新的结点 cin>>num; p->data = num;//存入结点数据域 p->next = r->next;//p指向的下一个位置为NULL r->next = p;//r指针后移,指向尾结点
    r = p;
    } return L; //返回值为Node*型 }

     <3>按序号查找结点值

    //单链表结构 
    typedef struct LNODE
    {
        int data;
        struct LNODE *next;
    }node;
     
    node *FindById(Lnode *L,int id)
    {
        Lnode *p = L->next;
        int j=1;
            if(i==0)
                 return L;//i=0返回头结点
            if(i<1)
                 return NULL;//i不合法,返回NULL:
        while(p!=NULL&&j<id)
        {
            p=p->next;
            j++;
        }
            return p;//返回第id个结点的指针,若id大于表长,则返回NULL;
    }
     

    <4>按值查找

    node *FindByValue(Lnode *L,int num)
    {
        Lnode *p = L->next;
        while(p!=NULL&&p->data!=num)
        {
            p=p->next;
        }
            return p;//找到则返回找到的结点的指针,否则返回NULL;
    }

    插入结点操作:

    1、p=FindById(L,id-1);//查找要插入位置的前驱结点
    2、s->next=p->next;//s为要插入的结点
    3、p->next=s;

    删除结点操作:

    1、p=FindById(L,i-1);//查找要删除位置的前驱结点
    2、q=p->next;//令q指向要删除的结点
    3、p->next=q->next;//将结点*q断开
    4free(q);//释放q内存

    获取单链表长度

    有头结点时:

    int GetLength(Lnode *L)
    {
        Lnode *p = L->next;
        int i=0;
        while(p!=NULL&&p->data!=num)
        {
            p=p->next;
            i++;
        }
            return i;//返回长度;
    }

    无头结点时:

    int GetLength(Lnode *L)
    {
        Lnode *p = L;//与有头结点的区别
        int i=0;
        while(p!=NULL&&p->data!=num)
        {
            p=p->next;
            i++;
        }
            return i;//返回长度;
    }

    <5>单链表逆置

     

     重复下面操作

     

     代码

    双链表模块

    //双链表结构 
    typedef struct LNODE
    {
        int data;
        struct LNODE *prior,*next;
    }node;

     1.双向链表的插入操作:

     插入结点代码:

    1、s->next=p->next;//将结点*s插入到*p之后
    2、p->next->prior=s;
    3、s->prior=p;
    4、p->next=s;

    语句顺序有多种,但是一定要保证第4步一定要1,2两句之后;但是我觉得上面语句最好记;因为和单链表的插入一样,都是先处理p->next指向的结点;处理完再处理其他的。

    2.双链表的结点删除操作

    双链表的删除比起单链表简单多了,因为单链表删除结点的时候,无法像双链表那样来回切换前后结点,因为双链表多了个prior指针域。

     

    删除结点代码:

    1、p->next=q->next;//将结点q链断开
    2、q->next->prior=p;
    3.free(q)

     上面是通过两个结点信息,一个结点信息也可以删除。

  • 相关阅读:
    CF666E Forensic Examination SAM+倍增,线段树和并
    CQOI2018 九连环 打表找规律 fft快速傅里叶变换
    CF993E Nikita and Order Statistics 多项式卷积 快速傅里叶变换
    NOIP2016 天天爱跑步 线段树合并
    BZOJ 3123 SDOI2013 森林 启发式合并+主席树
    [USACO1.4]等差数列 Arithmetic Progressions
    [LUOGU] 1108 低价购买
    [HDU5807] Keep In Touch
    [一道在别人课件上看到的没有出处的题]
    [LUOGU] 4933 大师
  • 原文地址:https://www.cnblogs.com/hmy-666/p/13504686.html
Copyright © 2011-2022 走看看