22.C语言 链表

目录

链表的概念

单链表的实现

1   链表的概念

引例1 建立学生信息表

假设需要建立一个学生信息表,学生人数无法估计,而且学生人数经常发生变化,该如何实现?

 

 

 单链表:线性表的链接存储结构。

     存储思想:用一组任意的存储单元存放线性表的元素。

                                不连续、分散分布

 单链表

*单链表存储特点:

1.逻辑次序和物理次序不一定相同。

2.元素之间的逻辑关系用指针表示。

举例:

(a1,a2,a3,a4)的存储示意图

 由上图可知,单链表

单链表是由若干结点构成的;

单链表的结点只有一个指针域。

结点(node):由数据域和指针域构成

 data:存储数据元素

next:存储指向后继结点的地址

单链表的结点结构:

typedef struct node

{

        DataType data;

        struct node *next;

}Node,*Link;

这样就可以动态分配内存:

p=(Link)malloc(sizeof(Node));

这里的意思就是:

Node st;      等价于    struct node st;

Link  p;        等价于    struct node *p;

p=&st;

p = (Link)malloc(sizeof(Node));

等价于

p = (struct node*)malloc(sizeof(Node));   

如何引用数据元素?

(*p).data = p->data

如果想访问指针域就使用:p->data的方式

什么是存储结构?

重点在数据元素之间的逻辑关系的表示,所以,将实际存储地址抽象。

因为链表的存储空间是不连续的

空表:

head==NULL

非空表:

 空表和非空表不统一,缺点?

如何将空表与非空表统一?

头结点:在单链表的第一个元素结点之前附设一个类型相同的结点,以便空表和非空表处理统一。

2 单链表的实现

(1)单链表的遍历操作

-操作接口:void displayNode(Link head);

void displayNode(Link head)
{
    p = head->next;
    while(p!=NULL)    
    {
        printf("%d",p->data);
        p=p->next;
    }
}

p=head->next;   //头结点指向下一个结点的地址

p=p->next;    //使p指向下一个结点的地址

p++能否完成指针右移?

(2)求单链表的元素个数

-操作接口:int length(Link head);

                                        count++ 

伪代码:

        1.工作指针p初始化;累加器count初始化;

        2.重复执行下述操作,直到p为空:

                2.1 工作指针p后移

                2.2 count++;

        3.返回累加器count的值;

int length(Link head)
{
    p = head->next; 
    count=0;
    while(p!=NULL)
    {
        p=p->next;
        count++;
    }
    return count; 
}

(3)单链表的查找操作

int queryNode(Link head,DataType x)
{
    p=head->next;
    count = 0;
    while(p!=NULL) 
    {
        print (data);
        return true;
    }
    p=p->next;
}
//如果循环结束了,说明没有找到
return false;
}

(4)单链表的插入操作

-操作接口:void insertNode(Link head,int i,DataType x);

如何实现结点a(i-1)、x和a(i)之间逻辑关系的变化?

算法描述:

让p移动到正确的位置;

node=(Link)malloc(sizeof(Node));
node->data=x;
node->next=p->next;
p->next=node;

①创建一个结点让node只写它:

node=(Link)malloc(sizeof(Node));

②把x的值赋给node结点的数据域

node->data=x;

③让node的指针域指向a(i )结点首地址

node->next=p->next;

④让a(i-1)的指针域指向node结点的首地址

p->next=node;

由于单链表带头结点,表头、表中、表尾三种情况的操作语句一致。

插入操作的算法描述如下总结:

1.工作指针p初始化;

2.查找第i-1个结点并使工作指针p指向该结点;

3.若查找不成功,则返回false;

否则,

3.1生成一个元素值为x的新结点s;

3.2将新结点s插入到结点p之后;

3.3返回true;

单链表的插入操作

bool insertNode(Link head,int i,DataType x)
{
    p=head;
    count = 0;
    while(p!=NULL&&count<i-1)
    {
        p = p->next; 
        count++;
    }
    if(p==NULL)
        return false;

 

bool insertNode(Link head,int i,DataType x)
{
    p=head;                       //工作指针p指向头结点                             
    count=0; 
    while(p!=NULL&&count<i-1)     //查找第一个i-1个结点   
    {
        p=p->next;    
        count++;
    }
    if(p==NULL)    
        return false;             //没找到第i-1个结点   
    else
    { 
        node=(Link)malloc(sizeof(Node));//申请一个结点node
        node->data=x;
        node->next = p->next;            //结点node插入结点p之后
        p->next = node;
        return ture;
    }

(5)创建一个单链表---头插法

操作接口:Link newList(DataType a[],int n);

头插法:将待插入结点插在头结点的后面。

 初始化头结点

 算法描述:

head=(Link)malloc(sizeof(Node));

head->next=NULL;

头插法

插入第一个元素结点:

 算法描述:

node=(Link)malloc(sizeof(Node));

node->data=a[0];

node->next=head->next;

head->next=node;

Link newList(DataType a[],int n)
{
    //创建头结点
    head=(Link)malloc(sizeof(Node));
    head->next=NULL;
    //创建后续结点
    for(i=0;i<n;i++)
    {
        node=(Link)malloc(sizeof(Node));
        node->data=a[i];
        node->next=head->next;
        head->next=node;
    }
    return head;
}

尾插法:

操作接口:Link newList(DataType a[],int n);

尾插法:将待插入结点插在终端结点的后面。

插入第一个元素结点

 算法描述:

node=(Link)malloc(sizeof(Node));
node->data=a[0];
rear->next=node;
rear=node;

注意点:rear->next=NULL

尾插法

Link newList(DataType a[],int n )
{
    head=(Link)malloc(sizeof(Node));        //生成头结点
    head->next=NULL;
    rear=head;                              //尾指针初始化  
    for(i=0;i<n;i++)
    {
        node=(Link)malloc(sizeof(Node));
        node->data=a[i];
        rear->next=node;
        rear=node;
    }
    rear->next=NULL;
    return head;
}

(6)单链表结点的删除

操作接口:bool deleteNode(Link head,Datatype x);

如何删除p所指向的结点?

 算法描述:

q->next=p->next;

free(p);

思考:

删除很简单,但如何能让指针移动到合适的位置,并且保证p指向要删除的结点,q则指向p的前驱结点?

但是,查找结点过程中,如何保证p,q指针一前一后?

q=p;

p=p->next;

思路:

在查找过程中,如果发现p所指向的结点data值不是要找的x,则p,q同时后移;一旦找到,则执行删除操作。

如果没有找到x的数据

思路:

在查找过程中,如果若一直没有找到data域为x的结点,则进入如上状态。

此时:退出循环,返回false。

删除时注意分析边界情况——要删除的表为空表

空表的状态是怎么样的?

head->next=NULL;

head=NULL;

思路:

在查找过程中,如果发现待删除的表是空表(左边两种情况都是空表),则提前返回false。

算法描述:

if(head==NULL||head->next==NULL)

{

//空表

return false;

}

伪代码:

1.判断是否是空表,如果是空表返回false;

2.工作指针p,q初始化;

3.若p指针不为空,则继续下列循环:

3.1如果找到data域等于x的结点,则:

3.1.1摘链,将结点p的从链表上摘下;

3.1.2释放被删结点;

3.1.3提前返回true,代表删除成功。

否则:

3.2.1q移动到 p所在的位置

3.2.2p移动到下一个结点

4.循环结束了,说明没有找到和x相等的结点,则返回false。

bool deleteNode(Link head,DataType x)
{
    if(head==NULL || head->next==NULL)
    {
        return false;
    }
    p=head->next;    
    q=head;
    while(p!=NULL)
    {
        if(p->data==x)
        {
            q->next = p->next;
            free(p);
            return true;
        }
        else
        {
            q=p;
            p=p->next;
        }
    }
    return false;
}

(7)单链表的释放

-操作接口:void clearLink(Link head);

-将单链表中所有结点的存储空间释放。

注意:保证链表未处理的部分不断开

算法描述:

q=head;

head=head->next;

free(p);

提问:这段代码循环执行,循环退出的条件是什么?

循环链表的实现

循环链表

从单链表中某结点p出发如何找到其前驱?

将单链表的首尾相接,将终端结点的指针域由空指针改为指向头结点,构成单循环链表,简称循环链表。

3循环链表的实现

循环链表

循环链表的插入

算法描述:

node=(Link)malloc(sizeof(Node));

node->data=x;

node->next=p->next;

p->next=node;

循环链表没有明显的尾端   --->   如何避免死循环

循环退出条件:

p!=NULL----->p!=head

p->next!=NULL---->p->next!=head

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值