C语言链表各类操作详解

1.单链表的读取
获取链表第i个数据的算法思路:
  1. 声明一个指针p指向链表的第一个结点,初始化j从1开始;
  2. 当j< i 时,遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
  3. 若到链表末尾p为空,说明第i个元素不存在;
  4. 否则查找成功,返回结点p的数据。
/*初始条件:顺序线性表L已存在,1≤i≤ListLength(L)*/
/*操作结果: 用e返回L中第i个数据元素的值*/
Status GetElem(LinkList L,int i ,ElemType *e)
{
int j;
LinkList p; /*声明一指针p*/
p = L->next; /*让p指向链表L的第一个结点*/
j = 1;  /*计数器*/
while(p && j<i) /*当p不为空或者j还没有大于等于i时,循环继续*/
    {
    p = p->next;  /*让p指向下一个结点*/
    ++j;
    }
if (!p || j>i)
    return ERROR; /*第i个元素不存在*/
*e = p->data;  /*取出第i个结点的数据*/
return OK;
}

由于单链表的结构中没有定义表长,所以不能事先知道要循环多少次,因此不方便用for循环来控制循环。

2.单链表的插入

单链表第i个数据插入结点的算法思路是:

  1. 声明一个指针p指向链表的第一个结点,初始化j从1开始;
  2. 当j< i 时,遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
  3. 若到链表末尾p为空,说明第i个元素不存在;
  4. 否则查找成功,在系统中生成一个空结点s;
  5. 将数据元素e赋值给s->data;
  6. 单链表的插入标准语句 s->next = p->next; p->next = s;
  7. 返回成功。
/*初始条件:顺序线性表L已存在,1≤i≤ListLength(L)*/
/*操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1*/
Status ListInsert(LinkList *L,int i ,ElemType e)
{
int j;
LintList p,s; 
p = *L;
j = 1;
while (p && j<i) /*寻找第i个结点*/
    {
    p = p->next;
    ++j;
    }
if (!p || j >i)
    return ERROR;  /*第i个元素不存在*/

s = (LinkList)malloc(sizeof(Node)); /*生成新的结点*/
s->data = e;
s->next = p->next;  /*将p的后继结点赋值给s的后继*/
p->next = s;  /*将s赋值给p的后继*/
return OK;
}
3.单链表的删除

实际上就是一步,p->next = p->next->next. 用p来取代p->next;

q=p->next;
p->next = q->next;

单链表第i个数据删除结点的算法思路:

  1. 声明一个指针p指向链表的第一个结点,初始化j从1开始;
  2. 当j< i 时,遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
  3. 若到链表末尾p为空,说明第i个元素不存在;
  4. 否则查找成功,将欲删除的结点p->next赋值给q;
  5. 单链表的删除标准语句 p->next = q->next;
  6. 将q结点中的数据赋值给e,作为返回;
  7. 释放q结点;
  8. 返回成功。
#include <stdio.h>
#define ERROR 0
#define OK 1
Status ListDelete(LinkList *L,int i ,ElemType *e)
{
int j;
LinkList p,q;
p = *L;
j = 1;
while(p->next && j <i)
    {
    p = p->next;
    ++j;
    }
if(!(p->next) || j>i)
    return ERROR;
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return OK;
}

对于基本的插入与删除操作,它们其实都是两部分组成:

  1. 遍历查找第i个结点;
  2. 插入和删除结点。
4.单链表的整表创建

单链表是一种动态结构。
   对于每个链表来说,它所占用的空间大小和位置是不需要预先分配的。
   所以创建单链表的过程,就是从“空表”的初始状态,一次建立各元素结点,并插入链表中、

单链表的整表创建的算法思路:
(头插法)

1.声明以指针p和计数器变量i;
2.创建一个空链表L;
3.让L的头结点的指针指向NULL;
4.循环:

  • 生成以新结点赋值给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));  /*生成新结点*/
    p->data = rand()%100+1;  /*随机生成100以内的数字*/
    p->next = (*L)->next;
    (*L)->next = p;  /*插入到表头*/
    }
}

尾插法 把每次新结点都插在终端结点的后面

代码:

/*随机产生n个元素的值,建立带表头结点的单链线性表(尾插法)*/
void CreateListTail(LinkList *L,int n)
{
LinkList p,r;
int i;
srand(time(0)); 
*L = (LinkList)malloc(sizeof(Node)) 
r =*L    /*r为指向尾部的结点*/
for(i=0;i<n;i++)
    {
    p = (Node *)malloc(sizeof(Node))  /*生成新结点*/
    p->data = rand() %100 +1;
    r->next = p;   /*将表尾终端结点的指针指向新结点*/
    r = p;  /*将当前的新结点定义为表尾终端结点*/
    }
r->next = NULL;   /*表示当前链表结束*/
5.单链表的整表删除

当我们不打算使用这个单链表时候,我们需要把它销毁,其实就是在内存中将它释放掉。

思路:
1.声明结点p和q;
2.将第一个结点赋值给p;
3.循环:

  • 将下一结点赋值给q;

  • 释放p;

    /*初始条件:顺序线性表L已存在,操作结果:将L重置为空表*/
      Status ClearList(LinkList *L)
      {
      LinkList p,q;
      p = (*L)->next;   /*p指向第一个结点*/
      while(p)  /*没到表尾*/
          {
          q = p->next;
          free(p);
          p = q;
          }
      (*L)->next = NULL;  /*头结点指针域为空*/
      return OK;
      }
    
##### 6.总结
**单链表结构与顺序存储结构优缺点**
**时间性能**
顺序存储结构:
       查找为O(1),因其是随机存取结构;插入与删除需要平均移动表长一半的元素,故为O(n);

单链表:
      查找为O(n),查找算法的时间复杂度取决于i的位置,当i=1时,则不需要遍历,第一个就取出数据了,而当i=n时则遍历n-1次才可以。
      因此最坏情况为O(n);单链表在确定出某位置的指针后,插入和删除时间仅为O(1);

**空间性能**
顺序存储结构:需要预分配存储空间; 
单链表:不需要分配存储空间,只要有就可以分配,元素个数也不受限制。

* 若线性表需要频繁查找,很少进行插入与删除操作时,宜采用顺序存储结构;若需要频繁插入和删除时,宜采用单链表结构。
* 当线性表中的元素个数变化较大或未知时,最好使用单链表。如果实现知道线性表的大致长度则使用顺序存储结构效率会高很多。


  • 4
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值