数据链表

Posted lemongirl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据链表相关的知识,希望对你有一定的参考价值。

链表的定义:链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。

链表的特点:链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(malloc),每个节点包括两个部分:

      一个是存储数据元素的数据域

      另一个是存储下一个节点的地址的指针域

一.单向链表的操作

 1.链表的结构

  ♦ 链表由一个个节点构成,每个节点一般采用结构体的形式组织。

    比如:typedef struct stu

       {

         int num;

         float score;

         struct stu *next;

       }STU;

  ♦ 链表节点分为两个域

    数据域:存放各种实际的数据,如:num、score等

    指针域:存放下一节点的首地址

  ♦ 链表的一般结构如下:

    

  ♦ 链表的特点:

    • 链表作为一个线性存储结构,链表的使用比数组更加灵活(数组都是在静态存储区中定义的数组)。

    • 构造一个链表时,不一定在程序中指定链表的长度(节点个数),可以在程序的运行过程中动态的生成一个链表。

    • 链表使用完后可以通过调用free释放节点的方式完成对整个链表空间的释放。

 2.链表的创建

  构建算法:首先申请一个链表节点,然后为该节点成员赋值,最后将链表节点添加到链表中

  ① 添加到链表尾部:顺序创建,新加入的节点放在链表尾部

    ♦ 当链表为空时,将链表头直接指向待添加节点(该节点成为了链表的第一个节点)

    ♦ 链表不为空时,首先遍历链表找到链表尾节点,然后将待添加节点挂接到尾节点上

     

STU* link_create_end(STU *head,STU *p_new)
{
    STU *p_mov = head;
    if(head==NULL)//当第一次加入链表为空时,head执行p_new
    {
        head = p_new;
        p_new->next = NULL;
    }
    else//第二次及以后加入链表
    {
        while(p_mov->next!=NULL)
        {
            p_mov = p_mov->next;//找到原有链表的最后一个节点
        }
        p_mov->next = p_new;//将新申请的节点加入链表
        p_new->next = NULL;
    }
    return head;
}

  ②添加到链表头部:逆序创建,新加入的节点放在链表头部

    ♦ 当链表为空时,将链表头直接指向待添加节点(该节点成为了链表的第一个节点)

    ♦ 链表不为空时,首先将之前的第一个链表节点挂接到新插入的节点上,然后将链表头指向新插入的节点

     

STU *link_create_head(STU *head,STU *p_new)
{
    if(head==NULL)
    {
        head = p_new;
        p_new->next = NULL;
    }
    else
    {
        p_new->next = head;
        head = p_new;
    }
    return head;
}

 3.链表的遍历:遍历输出链表所有节点

  ♦ 得到链表第一个节点的地址,即head的值

  ♦ 设一个临时指针变量p_mov,指向第一个节点head,即可获取p_mov所指节点的信息

  ♦ 使p_mov后移一个节点,即可访问下一节点,直到链表的尾节点(注意结尾判断条件)

  

void link_print(STU *head)
{
    printf("num\\tname\\tscore\\n");
    while(head!=NULL)
    {
        printf("%d\\t%s\\t%.2f\\n",head->num,head->name,head->score);
        head = head->next;
    }
}

 4.链表的查找:按照指定关键字查找节点

   得到链表第一个节点的地址,即head的值

   设一个临时指针变量p_mov,指向第一个节点head,即可获取p_mov所指节点的信息

   比较是否是要查找的节点

    • 是,则返回相应节点地址,停止查找

    • 不是,使p_mov后移一个节点,即可访问下一节点,直到链表的尾节点(注意结尾判断条件),最后若找不到返回NULL

  

STU *search_link_num(STU *head,int num)    //按学号查找
{
    STU *p_mov = head;
    while(p_mov!=NULL)
    {
        if(p_mov->num == num)    //找到了
        {
            return p_mov;
        }
        p_mov = p_mov->next;
    }
    return NULL;    //没有找到
}
STU *search_link_name(STU *head,char *name)    //按姓名查找
{
    STU *p_mov = head;
    while(p_mov!=NULL)
    {
        if(strcmp(p_mov->name,name) == 0)
        {
            return p_mov;    //找到了
        }
        p_mov = p_mov->next;    //没有找到
    }
    return NULL;
}

 5.链表的有序插入:在一个链表的指定位置插入节点,要求链表本身必须是已经按某种规律排好序的。

  ①.原链表为空:只需使head指向被插节点即可。

  ②.在第一个节点之前插入:使head指向被插节点,被插节点的next指向原来的第一节点。  

STU *link_insert_head(STU *head,STU *p_new)    //在表头插入
{
    if(head==NULL)    //链表为空
    {
        head = p_new;
        p_new->next = NULL;
    }
    else
    {
        p_new->next = head;     //新来的节点作为头节点
        head = p_new;
    }
    return head;
}

  ③.在中间位置插入:使插入位置的前一节点的next指向被插节点,被插节点的next指向插入位置的后一节点。

STU *link_insert_order(STU *head,STU *p_new)    //按顺序插入
{
    STU *pf = head,*pb = head;
    if(head==NULL)// 链表为空链表
    {
        head = p_new;
        p_new->next = NULL;
    }
    while((pb->num<p_new->num) && (pb->next!=NULL))//循环找
    {
        pf = pb;
        pb = pb->next;
    }
    if(pb->num >= p_new->num)//找到一个节点的num比新来的节点num大,插在pb的前面
    {
        if(pb == head)//找到的节点是头节点,插在最前面
        {
            p_new->next = head;
            head = p_new;
        }
        else
        {
            pf->next = p_new;
            p_new->next = pb; 
        }
    }
    else//没有找到pb的num比p_new->num大的节点,插在最后
    {
        pb->next = p_new;
        p_new->next = NULL; 
    }
    return head;
}

  ④.在表末尾插入:是链表尾节点next指向被插节点,被插节点next指向NULL。

STU *link_insert_end(STU *head,STU *p_new)    //在尾部插入
{
    STU *p_mov = head;
    if(head==NULL)    //没有节点
    {
        head = p_new;
        p_new->next = NULL;
    }
    else
    {
        while(p_mov->next!=NULL)    //找到链表最后一个节点
        {
            p_mov = p_mov->next;
        }
        p_mov->next = p_new;  //最后一个节点指向新插入的节点
        p_new->next = NULL;
    }
    return head;
}

 6.链表的删除:删除是将某一节点从链中摘除,并释放相应的空间。

  ♦ 删除的第一步是找到要删除的节点,同查找算法,若找不到或链表为空,提示未找到

  ♦ 找到后根据情况删除此节点

    ①.被删节点是第一个节点:只需使head指向第二个节点即可。

    

    ②.被删节点不是第一个节点:使被删节点的前一节点指向被删节点的后一节点即可。

     

STU *delete_link_num(STU *head,int num)    //按学号删除
{
    STU *pf = head,*pb = head;
    if(pb==NULL)//链表为空,不用删
    {
        printf("链表为空,没有您要找的结点\\n");
        return ;
    }
    while((pb->num != num) && (pb->next!=NULL))//循环找,要删除的节点
    {
        pf = pb;
        pb = pb->next;
    }
    if(pb->num == num)//找到了一个节点的num和num相同
    {
        if(pb == head)//要删除的节点是头节点
        {
            head = pb->next;
        }
        else
        {
            pf->next = pb->next;
        }
        free(pb);
    }
    else//没有找到
    {
        printf("没有您要找的结点\\n");
    }
    return head;
}
STU *delete_link_name(STU *head,char *name)    //按姓名删除
{
    STU *pb = head,*pf = head;
    if(pb == NULL)//链表为空,不用删
    {
        printf("链表为空,没有您要找的结点\\n");
        return ;
    }
    while((strcmp(pb->name,name)!=0) && (pb->next!=NULL))//循环找,要删除的节点
    {
        pf = pb;
        pb = pb->next;
    }
    if(strcmp(pb->name,name)==0)//找到了一个节点的name和name相同
    {
        if(pb == head)//要删除的节点是头节点
        {
            head = pb->next;
        }
        else
        {
            pf->next = pb->next;
        }
        free(pb);
    }
    else//没有找到
    {
        printf("没有您要找的结点\\n");
    }
    return head;
}

 7.链表的释放

  ♦ 同遍历链表类似,区别在于p_mov每指向某个节点后都将该节点释放

  ♦ 释放前要先保存下一个节点,释放后备份恢复给p_mov,否则释放了当前节点,下一个节点的地址就将丢失

  ♦ 依次将所有节点释放后,最后返回NULL(标示释放完毕)

   

STU *free_link(STU *head)
{
    STU *pb = head;
    while(head!=NULL)
    {
        pb = head;
        head = head->next;
        free(pb);
    }
    return NULL;
}

 8.链表排序:当链表本身是无序的时候,我们需要对链表的所有数据进行排序,算法同冒泡法、选择法。

STU *link_order(STU *head)    //排序
{
    STU *pf = head,*pb,temp;
    if(head == NULL)
    {
        printf("链表为空\\n");
        return ;
    }
    if(head->next == NULL)
    {
        printf("只有一个结点,不用排序\\n");
        return ;
    }
    while(pf->next!=NULL)//以pf指向的节点为基准节点,
    {
        pb = pf->next;//pb从基准元素的下个元素开始
        while(pb!=NULL)
        {
            if(pf->num>pb->num)
            {
                temp = *pf;
                *pf = *pb;
                *pb = temp;
                temp.next = pf->next;
                pf->next = pb->next;
                pb->next = temp.next;
            }
            pb = pb->next;
        }
        pf = pf->next;
    }
    return head;
}

 9.链表逆序:将链表的所有节点逆序存放,原来的头结点变为尾节点,原来的尾节点变为头结点。 

STU *link_reverse(STU *head)    //链表的逆序
{
    STU *pf = head,*pb,*ps;
    if(head == NULL)
        return NULL;
    pb = pf->next;
    while(pb!=NULL)
    {
        ps = pb->next;
        pb->next = pf;
        pf = pb;
        pb = ps;
    }
    head->next = NULL;
    head = pf;
    return head;
}

二.双向链表的操作

 1.双向链表的结构

  typedef struct student

  {
    int num;
    char name[30];
    struct student *next;  //指向后一个节点的指针域
    struct student *prior;  //指向前一个节点的指针域
  }STU;

  

 2.双向链表的插入

STU *insert_link_oder(STU *head,STU *pnew)    //双向链表的顺序插入
{
    STU *pb = head,*pf = head;
    
    if(head == NULL)
    {
        head = pnew;
        head->next = head;
        head->prior = head;
    }else
    {
        while((pb->num < pnew->num)&&(pb->next != head))
        {
            pf = pb;
            pb = pb->next;            
        }
        if(pb->num >= pnew->num)
        {
            if(pb == head)
            {
                head->prior->next = pnew;
                pnew->prior = head->prior;
                pnew->next = head;
                head->prior = pnew;
                head = pnew;
            }
            else
            {
                pf->next = pnew;
                pnew->prior = pf;
                pnew->next = pb;
                pb->prior = pnew;
            }
        }
        else
        {
            pb->next = pnew;
            pnew->prior = pb;
            pnew->next = head;
            head->prior = pnew;
        }
    }
    return head;
}

 3.双向链表的删除 

STU *delete_link_num(STU *head,int num)    //双向链表按学号删除
{
    STU *pf = head,*pb = head;
    if(head == NULL)
        return NULL;
    while(pb->next != head)
    {
        if(pb->num == num)
        {
            if(pb == head)
            {
                pb->prior->next = pb->next;
                pb->next->prior = pb->prior;
                head = pb->next;
            }
            else
            {
                pf->next = pb->next;
                pb->next->prior = pf;                
            }
            free(pb);
            break;
        }
        pf = pb;
        pb = pb->next;
    }    
    if(pb->num == num)
    {
        pf->next = head;
        head->prior = pf;
        free(pb);
        if(pb == head)
            head = NULL;
    }
    return head;
}

 4.双向链表排序

STU *oder_link(STU *head)    //双向链表排序
{
    STU *pf = head,*pb = head,temp;
    if(head == NULL)
        return NULL;
    while(pf->next != head)
    {
        pb = pf->next;
        while(pb != head)
        {
            if(pf->num < pb->num)
            {
                /* 交换 */
                temp = *pf;
                *pf = *pb;
                *pb = temp;
                
                temp.next = pf->next;
                temp.prior = pf->prior;
                pf->next = pb->next;
                pf->prior = pb->prior;
                pb->next = temp.next;
                pb->prior = temp.prior;
            }
            pb = pb->next;
        }
        pf = pf->next;
    }    
    return head;
}

以上是关于数据链表的主要内容,如果未能解决你的问题,请参考以下文章

片段中的Android ListView - 获取新数据后刷新表的问题

从底部工作表对话框片段中获取价值

如何将数据从 BottomSheetDialogFragment 返回到父片段

PostgreSQL 是不是支持表(片段)的透明压缩?

SQL Select 语句的用法

JavaScript笔试题(js高级代码片段)