单链表的常用算法

取值:取单链表中第i个元素的内容(有头结点的单链表)

算法思路

从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存取结构,是顺序结构

算法步骤

  1. 从第一个结点(L - > next)顺链扫描,用指针p指向当前扫描到的结点,p初值p=L - > next。

  2. j做计数器,累计当前扫描过的结点数,j初值为1。

  3. 当p指向扫描到的下一结点时,计数器j加1。

  4. 当j==i时,p所指的结点就是要找的第i个结点。


Status GetElem_L(LinkList L,inti,ElemType&e){	//获取线性表L中的某个数据元素的内容,通过变量e返回

p=L->next;j=1;	//初始化

while(p&&j<i){	//向后扫描,直到p指向第i个元素或p为空

p=p->next;++j;

}

if(!p || j>i)return ERROR;	//第i个元素不存在

e=p->data;	//取第个元素

return OK;

}//GetElem_L

查找1:根据指定数据获取该数据所在的位置(该数据的地址)

算法步骤

  1. 从第一个结点起,依次和e相比较

  2. 如果找到一个其值与e相等的数据元素,则返回其在链表中的"位置"或"地址"

  3. 如果查遍整个链表都没有找到其值和e相等的元素,则返回0或"NULL"。


Lnode *LocateELem_L(LinkList L,Elemtype e){

//在线性表L中查找值为e的数据元素

//找到,则返回L中值为e的数据元素的地址,查找失败返回NULL

p=L->next;

while(p &&p->data!=e)

p=p->next;

return p;

}

查找2:根据指定数据获取该数据位置序号


//在线性表L中查找值为e的数据元素的位置序号

int LocateELem_L(LinkList L,Elemtype e){

//返回L中值为e的数据元素的位置序号,查找失败返回0

p=L->next;j=1;

while(p &&p->data!=e)

{p=p->next;j++;}

if(p)return j;

else return 0;

}

插入:在第i个结点前插入值为e的新结点

算法步骤

  1. 首先找到$a_$ 的存储位置p

  2. 生成一个数据域为e的新结点s

  3. 插入新结点:①新结点的指针域指向结点$a_i$($s-> next = p-> next$)

    ​ ②结点$a_$的指针域指向新结点($p->next = s$)


//在L中第i个元素之前插入数据元素e

Status ListInsert_L(LinkList &L,int i,ElemType e){

p=L;j=0;

while(p&&j<i-1){p=p->next;++j;}//寻找第i-1个结点,p指向i-1结点

if(!p‖j>i-1)return ERROR;//i大于表长+1或者小于1,插入位置非法

s=new LNode;s->data=e;

/生成新结点s,将结点s的数据域置为e

s->next=p->next;

/将结点s插入L中

p->next=s;

return OK;

}//ListInsert L

住:$p->next$有两层意思,

  1. $p->next$ 作为指针: 在这种情况下,p是一个指向链表中某个结点的指针。因为链表的每个结点都有一个指向下一个结点的指针(通常称为 next 指针),因此p->next 表示 p 指针所指向的结点的下一个结点的地址。

  2. $p->next$ 作为结点的指针域: 在这种情况下,p 是指向链表中某个结点的指针。每个结点都有一个 next域,用于存储指向下一个结点的指针。所以,p->next 表示 p 指针所指向的结点的 next 域,也就是指向下一个结点的指针。

650623b5838e046139853b26ff64ce8

删除:删除第i个结点

算法步骤

  1. 首先找到$a_$的存储位置p,保存要删除的$a_i$值。

  2. 令$p->next$指向$a_{i+1}$

  3. 释放结点$a_$的空间

    
    //将线性表L中第个数据元素删除
    
    Status ListDelete_L(LinkList &L,int i,ElemType &e){
    
    p=L;j=0;
    
    while(p->next &&j<i-1){p=p->next;++j;	//寻找第i个结点,并令p指向其前驱
    
    if(!(p->next)||j>i-1)return ERROR;//删除位置不合理
    
    q=p->next;	//临时保存被删结点的地址以备释放
    
    p->next=q->next;	//改变删除结点前驱结点的指针域
    
    e=q->data;	//保存删除结点的数据域
    
    delete q;	//释放删除结点的空间
    
    return OK;
    
    }//ListDelete_L
    
    
    image-20230807100156430

单链表的查找、插入、删除算法时间效率分析

1.查找:

因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间

复杂度为$O(n)$

2.插入和删除

因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度

为$O(1)$。

但是,如果要在单链表中进行前插或删除操作,由于要从头查找前驱

结点,所耗时间复杂度为$O(n)$)。

单链表的建立(头插法)

算法步骤:

  1. 从一个空表开始,重复读入数据

  2. 生成新结点,将读入 数据存放到新结点的数据域中

  3. 从最后一个结点开始,依次将各结点插入到链表的前端

image-20230807110349975

void CreateList_H(LinkList &L,int n){

L=new LNode;

L->next=NULL;//先建立一个带头结点的单链表

for(i=n;i>0;--i){

p=new LNode;	//生成新结点p=(LNode*)malloc(sizeof(LNode)

cin>>p->data;	//输入元素值scanf(&p->data)i

p->next=L->next;	//插入到表头

L->next=p;

 }

}//CreateList_H                      

算法的时间复杂度是:$O(n)$

单链表的建立(尾插法)

算法步骤:

  1. 从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针指向链表

    的尾结点。

  2. 初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,

    将新结点插入到尾结点后,r指向新结点。


//正位序输入个元素的值,建立带表头结点的单链衣L

void CreateList_R(LinkList &L,int n){

L=new LNode;

L->next=NULL;

r=L;	//尾指针r指向头结点

for(i=0;i<n;++i){

p=new LNode;cin>>p->data;	//生成新结点,输入元素值

p->next=NULL;

r->next=p;	//插入到表尾

r=p;	//r指向新的尾结点

 }

}//CreateList_R

算法的时间复杂度是:$O(n)$