• 数据链表


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

    链表的特点:链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(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;
    }
  • 相关阅读:
    leetcode319
    leetcode516
    leetcode46
    leetcode337
    leetcode287
    leetcode328
    leetcode241
    2018-6-30-dotnet-设计规范-·-抽象类
    2018-6-30-dotnet-设计规范-·-抽象类
    2018-8-10-WPF-如何画出1像素的线
  • 原文地址:https://www.cnblogs.com/lemongirl/p/7906357.html
Copyright © 2020-2023  润新知