单链表相关问题

本文详细介绍了单链表的基本操作,包括整表创建与删除、静态链表的插入与删除等,并对比了单链表与顺序存储结构的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.单链表的整表创建

单链表整表创建的算法思路:
1.声明一指针p和计数器变量i;
2.初始化一空链表L;
3.让L的头结点的指针指向NULL,即建立一个带头结点的单链表;
4.循环:

生成一新结点赋值给p;

随机生成一数字赋值给p的数据域p->data;

将p插入到头结点与前一新结点之间。

实现代码算法如下:

/* 随机产生n个元素的值,建立带表头结点的单链
   线性表L(头插法) */
void CreateListHead(LinkList *L, int n)
{
    LinkList p;
    int i;
    /* 初始化随机数种子 */
    srand(time(0));                            
    *L = (LinkList)malloc(sizeof(Node));
    /* 先建立一个带头结点的单链表 */
    (*L)->next = NULL;                         
    for (i = 0; i < n; i++)
    {
        /* 生成新结点 */
        p = (LinkList)malloc(sizeof(Node));    
        /* 随机生成100以内的数字 */
        p->data = rand() % 100 + 1;            
        p->next = (*L)->next;
        /* 插入到表头 */
        (*L)->next = p;                        
    }
}

这段算法代码里,我们其实用的是插队的办法,就是始终让新结点在第一的位置。我也可以把这种算法简称为头插法。

2.单链表的整表删除

当我们不打算使用这个单链表时,我们需要把它销毁,其实也就是在内存中将它释放掉,以便于留出空间给其他程序或软件使用。
单链表整表删除的算法思路如下:
1.声明一指针p和q;
2.将第一个结点赋值给p;
3.循环:
将下一结点赋值给q;
释放p;
将q赋值给p。
实现代码算法如下:

/* 初始条件:顺序线性表L已存在,操作结果:将L
   重置为空表 */
Status ClearList(LinkList *L)
{
    LinkList p, q;
    /* p指向第一个结点 */
    p = (*L)->next;       
    /* 没到表尾 */
    while (p)             
    {
        q = p->next;
        free(p);
        p=q;
    }
    /* 头结点指针域为空 */
    (*L)->next = NULL;    
    return OK;
}


这段算法代码里,常见的错误就是有同学会觉得q变量没有存在的必要。在循环体内直接写free(p); p = p->next;即可。可这样会带来什么问题?
要知道p指向一个结点,它除了有数据域,还有指针域。你在做free(p);时,其实是在对它整个结点进行删除和内存释放的工作。这就好比皇帝快要病死了,却还没有册封太子,他儿子五六个,你说要是你脚一蹬倒是解脱了,这国家咋办,你那几个儿子咋办?这要是为了皇位,什么亲兄弟血肉情都成了浮云,一定会打起来。所以不行,皇帝不能马上死,得先把遗嘱写好,说清楚,哪个儿子做太子才行。而这个遗嘱就是变量q的作用,它使得下一个结点是谁得到了记录,以便于等当前结点释放后,把下一结点拿回来补充。

3.单链表结构与顺序存储结构优缺点

1.若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构。若需要频繁插入和删除时,宜采用单链表结构。比如说游戏开发中,对于用户注册的个人信息,出了注册时插入数据外,绝大多数情况都是读取,所以应该考虑用顺序存储结构。而游戏中的玩家的武器或者装备列表,随着玩家的游戏过程中,可能会随时增加或删除,此时再用顺序存储就不太合适了,单链表结构就可以大展拳脚。当然,这只是简单的类比,现实中的软件开发,要考虑的问题会复杂很多。


2.当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构,这样可以不需要考虑存储空间的大小问题。而如果事先知道线性表的大致长度,比如一年12月,一周就是星期一至星期日共七天,这种用顺序结构效率会高很多。

4.静态链表的插入操作

/* 在L中第i个元素之前插入新的数据元素e  */
  Status ListInsert(StaticLinkList L, int i, ElemType e)
  {
      int j, k, l;
      /* 注意k首先是最后一个元素的下标 */
      k = MAX_SIZE - 1;                   
      if (i < 1 || i > ListLength(L) + 1)
          return ERROR;
      /* 获得空闲分量的下标 */
      j = Malloc_SSL(L);                  
      if (j)
      {
         /* 将数据赋值给此分量的data */
         L[j].data = e;                  
         /* 找到第i个元素之前的位置 */
         for (l = 1; l <= i - 1; l++)    
             k = L[k].cur;
         /* 把第i个元素之前的cur赋值给新元素的cur */
         L[j].cur = L[k].cur;        
         /* 把新元素的下标赋值给第i个元素之前元素的cur */

5.静态链表的删除操作

/*  在L中第i个元素之前插入新的数据元素e   */  
Status ListInsert(StaticLinkList L, int i, ElemType e)     
{    
    int j, k, l;     
    k = MAXSIZE - 1;   /* 注意k首先是最后一个元素的下标 */  
    if (i < 1 || i > ListLength(L) + 1)     
        return ERROR;     
    j = Malloc_SSL(L);   /* 获得空闲分量的下标 */  
    if (j)     
    {     
        L[j].data = e;   /* 将数据赋值给此分量的data */  
        for(l = 1; l <= i - 1; l++)   /* 找到第i个元素之前的位置 */  
           k = L[k].cur;             
        L[j].cur = L[k].cur;    /* 把第i个元素之前的cur赋值给新元素的cur */  
        L[k].cur = j;           /* 把新元素的下标赋值给第i个元素之前元素的ur */  
        return OK;     
    }     
    return ERROR;     
}  
  
/*  删除在L中第i个数据元素   */  
Status ListDelete(StaticLinkList L, int i)     
{   
    int j, k;     
    if (i < 1 || i > ListLength(L))     
        return ERROR;     
    k = MAXSIZE - 1;     
    for (j = 1; j <= i - 1; j++)     
        k = L[k].cur;     
    j = L[k].cur;     
    L[k].cur = L[j].cur;     
    Free_SSL(L, j);     
    return OK;     
}   

6.静态链表优缺点

优点:在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了顺序存储结构中的插入和删除操作需要移动大量元素 的缺点

缺点:没有解决连续存储分配带来的表长难以确定的问题;失去了顺序存储结构随机存取的特性

7.从尾到头输出链表

void PrintReversely(Node* pHead)  
{  
    if (pHead)  
    {  
        if (pHead->next)  
        {  
            PrintReversely(pHead->next);  
        }  
        printf("%d",pHead->data);  
    }   
}  


8.从尾到头输出一个字符串

void ReversePrintStr(char str[])  
{  
    if (*str != '/0')  
    {  
        if (*(str+1) != '/0')  
        {  
            ReversePrintStr(str+1);  
        }   
        printf("%c",*str);  
    }  
}  


9.定义一个函数求字符串的长度,要求该函数体内不能声明任何变量

int StrLen(char str[])  
{  
    if(*str == '/0')  
        return 0;  
    return (StrLen(str+1) + 1);   
}  


10.链表结点的定义

struct Node  
{  
    int   data;  
    Node* next;  
};  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值