zoukankan      html  css  js  c++  java
  • 数据链表

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

    链表的特点:链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(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	name	score
    ");
        while(head!=NULL)
        {
            printf("%d	%s	%.2f
    ",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("链表为空,没有您要找的结点
    ");
            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("没有您要找的结点
    ");
        }
        return head;
    }
    STU *delete_link_name(STU *head,char *name)    //按姓名删除
    {
        STU *pb = head,*pf = head;
        if(pb == NULL)//链表为空,不用删
        {
            printf("链表为空,没有您要找的结点
    ");
            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("没有您要找的结点
    ");
        }
        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("链表为空
    ");
            return ;
        }
        if(head->next == NULL)
        {
            printf("只有一个结点,不用排序
    ");
            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;
    }
  • 相关阅读:
    一张图片入门Python
    4.1. 如何在Windows环境下开发Python
    你必须知道的EF知识和经验
    XUnit的使用
    如何使用NUnit
    Entity Framework 不支持DefaultValue
    Have You Ever Wondered About the Difference Between NOT NULL and DEFAULT?
    Validation failed for one or more entities. See 'EntityValidationErrors' property for more details
    Entity Framework 与多线程
    sqlite中的自增主键
  • 原文地址:https://www.cnblogs.com/lemongirl/p/7906357.html
Copyright © 2011-2022 走看看