Bootstrap

单链表的实现和操作

目录

一.前言

二.单链表的定义和结构

三. 单链表的操作


一.前言

        线性表的链式表示又称为非顺序映像或链式映像。简而言之,链表可以理解为由指针链连接的n个结点组成的。其中每一个结点包括数据域和指针域。值得注意的是,与顺序表不同,链表中的逻辑次序与物理次序不一定相同。

二.单链表的定义和结构

        我们称结点只有一个指针域的链表称为单链表或线性链表。注意区分我们后面要学习的双向链表和循环链表。下面我们来看下单链表的组成,单链表可以是这样的结构:

它包含一个头指针,头结点以及所有的结点,其中存储的第一个数据元素的结点称为首元结点。

其中,头指针是必要的,头结点可有可没有。当我们的单链表没有头结点的时候,头指针为空时表示这个单链表是空表。有头结点的时候,头结点的指针域为空时表示空表。 

 由于单链表是由表头唯一确定的,所以单链表可以用头指针的名字来命名,若头指针是L,则把链表称为表L。下面我们来定义一个单链表类型:

typedef struct Lnode{
    ElemType data;    //结点的数据域
    struct Lnode* next   //结点的指针域,指向这个struct Lnode的数据类型
}Lnode,*LinkList;       //LinkList为指向结构体Lnode的指针类型

在完成了这一步之后,我们通常使用LinkList L 来定义一个链表L使用Lnode* p来定义一个结点指针p。接着我们对定义好了的单链表来进行初始化:

Status InitList_L(LinkList &L){
    L=new Lnode;        //给链表L分配内存空间
    L->next=NULL;       //初始化为一个空表
    return OK;
}

其中Status就表示一个数据类型,在我之前的博客里面关于顺序表的实现与操作中就有提到。Status后面的InitList_L就表示这个初始化函数的函数名,可以由我们自行定义,但最好是能够见名思意的。

三. 单链表的操作

1.判断链表是否为空

所谓空表就是链表中没有元素,但是头指针和头结点仍然存在。这个算法的核心思路就是判断头结点指针域是否为空,如下所示:

Status ListEmpty(LinkList L){
    if(L->next)    //表示不是空表,返回0
        return 0;
    else            //若是空表则返回1
        return 1;
}

其中,L->next就表示头结点的指针域,而我们的头指针则就是用L表示的

2. 单链表的销毁

就是从头指针开始,依次释放所有结点。所以可以利用一个循环来实现,循环结束的条件也就是指针指向空。表示没有结点可以再释放了。如下所示:

Status DestroyList_L(LinkList &L){
    Lnode* p;        //定义一个Lnode类型的指针p
    while(L){        //从头结点开始
        p=L;            
        L=L->next;    //指向下一个结点
        delete p;     //释放结点
     }
    return OK;
}

值得注意的是,我们类C语言中要想使用delete来释放一个空间,就得使用new来分配空间。

3. 单链表的清空

就是指链表仍然存在,但是链表中已经没有元素,成为了一个空链表(头指针和头结点仍然存在)

如下所示:

Status ClearList(LinkList &L){
    Lnode*p,*q;        //定义两个Lnode类型的指针
    p=L->next;        
    while(p){        //循环结束条件就是p指向空的时候
        q=p->next;
        delete p;
        p=q;
    }    
L->next=NULL;        //头结点指针域为空
return OK;
}

4. 求单链表的表长

        如下所示:

Status ListLength(LinkList L){
    LinkList p;
    p=L->next;    //p指向第一个结点
    i=0;
    while(p){        //遍历单链表,统计结点数
        i++;
        p=p->next;
    }
return i;
}
    

5. 单链表的取值

就是取单链表中第i个元素的内容。可以从链表的头指针出发,顺着链域next逐个结点往下搜索,直到搜索到第i个结点为止。因此,链表不是随机存取结构。

Status GetElem(LinkList L,int i,ElemType &e){
    Lnode* p;
    int j=1;
    p=L->next;
    while(p&&j<i){    //向后扫描,直到p为空或者指向到第i个元素为止
        p=p->next;
        ++j;
    }
if(!p||j>i) return ERROR;    //如果p指向空了或者j>i,表示链表中并没有这个元素
e=p->date;             //取第i个元素的数据,并通过e返回
return OK;
}

6. 单链表的查找

单链表的查找可分为两种,第一种就是根据指定数据获取该数据所在的位置(地址)。第二种则是根据指定数据获取该数据的位置序号。

如下所示,首先介绍第一种:

Lnode* LocateElem(LinkList L,ElemType e){
    p=L->next;
    while(p&&p->data!=e)
        p=p->next;
    return p;        //如果找到了就返回对应的地址,如果没找到,也就是p指向空的时候,返回NULL
}

第二种如下:

Status LocateElem(LinkList L,ElemType e){
    Lnode* p;
    p=L->next;    
    int j=1;
    while(p&&p->data!=e){
        p=p->next;
        j++
    }
if(p) return j;    //如果找到,返回L中值为e的数据元素的位置序号。
else return 0;    //否则返回0,表示没有找到
}

7. 单链表的插入

在第i个结点前插入值为e的新结点。核心思路就是使新结点的指针域指向结点a(i),使结点a(i-1)的指针域指向新结点。如下所示:

Status ListInsert(LinkList &L,int i,ElemType e){
    Lnode* p;
    p=L;
    int j=0;
    while(p&&j<i-1){    //寻找第i个结点,p指向i-1结点
        p=p->next;
        ++j
    }
if(!p||j>i-1) return ERROR;   //i大于表长+1或者小于1,插入位置非法
s=new Lnode;        //生成新结点s
s->data=e;        //新结点s的数据域为e
s->next=p->next;    //将结点s插入到L中去
p-next=s;
return OK;
}

8. 单链表的删除

就是删除第i个结点,如下所示:

Status ListDelete(LinkList& L,int i,ElemType &e){
    Lnode* p;
    p=L;
    int j=0;
    while(p->next&&j<i-1){    //寻找第i-1个结点,并让p指向它。
        p=p->next;
        ++j;
      }
    if(!(p->next)||j>i-1) return ERROR;    //删除位置不合理
    q=p->next;                //临时保存被删除结点的地址以备释放
    p->next=q->next;        //改变要被删除结点前驱结点的指针域
    e=q->data;            //保存删除结点的数据域
    delete q;            //释放删除结点的空间
return OK;
}

 

         

 

 

 

 

         

 

        

;