京东秒杀
国美-超级5
索尼Xperia旗舰
限时优惠页 - 京东
自营热卖

单链表

清风归客 1年前   阅读数 69 0
    线性表🤔——你会更上一层楼滴。好了,话不多说,言归正传。

根据链表结点所含指针个数、指针方向和指针连接方式,可将链表分为单链表、循环链表、双向链表、二叉链表、十字链表、邻接表、邻接多重表等。

一:定义和表示

1:结点
2:数据域
3:指针域
4:指针/域
5:链表(线性链表/单链表)

在使用链表时,关心的只是它所表示的线性表中数据元素之间的逻辑顺序,而不是每个数据元素在存储器中的实际位置。

  单链表可由头指针唯一确定,在C语言中可用“结构指针”来描述:
         //-----单链表的存储结构-----
         typedef   struct   LNode
         {
            ElemType   data;   //结点的数据域
            struct   LNode   *next;   //结点的指针域
         } LNode,*LinkList;  //LinkList为指向结构体LNode的指针类型
  • 首元结点是指链表中存储第一个数据元素a1的结点。

  • 头结点是在首元结点之前附设的一个结点,其指针域指向首元结点。头结点的数据域可以不存储任何信息,也可存储与数据元素类型相同的其他附加信息。

  • 头指针是指向链表中第一个结点的指针。(若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点。)

  • 一般情况下,为了方便处理,在单链表的第一个结点之前附设一个结点,称为头结点。

头结点增加前:
在这里插入图片描述
头结点增加后:
在这里插入图片描述

  • 增加头结点的好处
    (1)便于首元结点的处理
    (2)便于空表和非空表的统一处理
    (3)无论链表是否为空,头指针都是指向头结点的非空指针
    注:非空单链表中头指针指向头结点
    空表中头结点的指针域为空
    在这里插入图片描述
    顺序表和单链表的区别:
    顺序表中由于逻辑上相邻的两个元素在物理位置上相邻,所以每个元素的存储位置都可从线性表的起始位置计算得到。
    单链表中每个元素的存储位置都是随意的。
    (在此可回顾下顺序表https://blog.csdn.net/qq_45947684/article/details/104012019

二:基本操作的实现

  • 创建单链表
    1:前插法
void CreateList_H(LinkList &L,int n)
{//逆位序输入n个元素的值,建立带表头结点的单链表L
   L=new LNode;
   L->next=NULL;  //建立一个带表头结点的空链表
   for(i=0;i<n;i++)
   {
     p=new LNode;  //生成新结点*p
     cin>>p->data;  //输入元素值赋给新结点*p的数据域
     p->next=L->next; L->next=p;  //将新结点*p插入到头结点之后
     }
}
     
  • 单链表前插法创建单链表算法的平均时间复杂度为O(n)
  • 过程
    在这里插入图片描述
    2:后插法
void CreateList_H(LinkList &L,int n)
{//正位序输入n个元素的值,建立带表头结点的单链表L
  L=new LNode;  
  L->next=NULL;   //建立一个带表头结点的空链表
  r=L;
  for(i=0;i<n;i++)
  {
    p=new LNode;  //生成新结点
    cin>>p->data;  //输入元素值赋给新结点*p的数据域
    p->next=NULL; r->next=p;  //将新结点*p插入尾结点*r之后
    r=p;  //r指向新的尾结点*p
  }
}
  • 单链表后插法创建单链表算法的平均时间复杂度为O(n)
  • 过程
    在这里插入图片描述
  • 初始化
Status InitList (LinkList &L)
{//构造空链表L
  L=new LNode;  //生成新节点作为头结点,用头指针L指向头结点
  L->next =NULL;  //头结点的指针域置空
  return OK;
}
  • 取值
Status  GetElem_L(LinkList L,int i,ElemType &e)
{   //在带头结点的单链表中根据序号i获取元素的值,用e返回L中第i个数据元素的值 
	p=L->next;int j=1;  //初始化 ,p指向首元结点,计数器j初值赋为1
	while(p&&j<i)     //顺域链向后扫描,直到p为空或p指向第i个元素
	{  
		p=p->next;  //p指向下一结点
		++j;   //计数器j加1
	}
	if(!p||j>i)return ERROR;   //i值不合法i>n或i<=0
		e = p->data;       //取第i个结点的数据域
		return OK;
}  

(若每个位置上元素的取值概率相等,则ASL=(n-1)/2 )

  • 单链表取值算法的平均时间复杂度为O(n)

  • 查找

LNode *LocateElem(LinkList L,ElemType e)
{//在带头结点的单链表L中查找值为e的元素
  p=L->next;  ///初始化 ,p指向首元结点
  while(p && p->data!=e)  ///顺域链向后扫描,直到p为空或p所指结点的数据域等于e
    p=p->next;  //p指向下一结点
  return p;  //查找成功返回值为e的结点地址p,查找失败p为NULL
}
  • 单链表查找算法的平均时间复杂度为O(n)

  • 插入

Status ListInsert(LinkList &L,int i,ElemType e)
{//在带头结点的单链表L中第i个位置插入值为e的新结点
  p=L;
  j=0;
  while(p && (j<i-1))
     {p=p->next;++j;} //查找第i-1个结点,p指向该结点
  if(!p||j>i-1) return ERROR;  //i>n-1或者i<1
  s=new LNode;  //生成新结点*s
  s->data=e;   //将结点*s的数据域值为e
  s->next=p->next;  //将结点*s的指针域指向结点ai
  p->next=s;  //将结点*p的指针域指向结点*s
  return OK;
}
  • 单链表插入算法的平均时间复杂度为O(n)

  • 单链表第 i 个位置上插入新结点的过程在这里插入图片描述

  • 插入前后变化: s->next = p->next;p->next = s;在这里插入图片描述

  • 删除

Status ListDelete(LinkList &L,int i)
{//在带头结点的单链表L中,删除第i个元素
  p=L;
  j=0;
  while((p->next) && (j<i-1))  //查找第i-1个结点,p指向该结点
    {p=p->next; ++j;}
  if(!(p->next)||(j>i-1) return ERROR;   //当i>n或i<1时,删除位置不合理
  q=p->next;   //临时保存被删结点的地址以备释放
  p->next=q->next;  //改变删除结点前驱结点的指针域
  delete q;  //释放删除结点的空间
  return OK;
}
  • 单链表删除算法的平均时间复杂度为O(n)
  • 单链表第 i 个结点删除过程在这里插入图片描述
  • 删除前后变化: p->next = p->next->next;
    在这里插入图片描述
发布了3 篇原创文章 · 获赞 11 · 访问量 1381

注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: