【数据结构】第二章 线性表的基本知识及代码实现(2)

目录:

一、线性表的链式存储结构

二、单链表基本操作的实现

一、线性表的链式存储结构

1、用一组物理位置任意的存储单元来存放线性表的数据元素

2、相关术语:

1)结点:数据元素的存储映像,由数据域与指针域两部分组成。

2)链表:n个结点由c指针链组成一个链表

3)头指针:是指向链表中第一个结点的指针

首元结点:是指链表中存储第一个元素的结点

头结点:是在链表的首元结点之前附设的一个结点

3、单链表的存储结点:

typedef struct Lnode{ /*声明结点类型和指向结点的指针类型*/
ElemType data;        /*结点的数据域*/
struct Lnode *next;   /*结点的指针域*/
}Lnode,*LinkList;     /*Lnode为结点类型,*LinkList为指针类型*/

eg.储存学生学号、姓名、成绩的单链表结点类型定义操作如下:

Typedef struct{
char num[8];
char name[8];
int score;
}ElemType;

typedef struct Lnode{
ElemType data;        /*数据域*/
struct Lnode *next;   /*指针域*/
}Lnode,*LinkList

二、单链表基本操作的实现

1、单链表的初始化

1)生成新结点作头结点,用头指针L指向头结点。

2)将头结点的指针域置空。

Status InitList_L(LinkList &L){
L=new LNode;   /*或L=(LinkList)malloc(sizeof(LNode));*/
L->next=NULL;
return OK;
}

2、判断链表是否为空(即判断头结点指针域是否为空)

int ListEmpty(LinkList L){  /*若L为空表,则返回1,否则返回0*/
if(L->next)         /*非空*/
return 0;
else
return 1;
}

3、单链表的销毁:链表销毁后不存在(从头指针开始,依次释放所有结点)

1)p=L

2)  L=L->next; delete p(free(p));

3)  结束条件:L==NULL

     循环条件:L!=NULL 或 L

Status DestroyList_L(LinkList &L){    /*销毁单链表*/
Lnode *p;   /*或LinkList p*/
while(L){
p=L;
L=L->next;
delete p;
}
return OK;
}

4、清空单链表(依次释放所有结点,并将头结点指针域设置为空)

1)p=L->next

2)q=p->next

      delete p;

3)反复执行:p=q;q=q->next

4)L->next=NULL

5)结束条件:p==NULL

循环条件:p!=NULL

Status ClearList(ListList &L){
Lnode *p,*q;            /*或LinkList p,q*/
p=L->next;
while(p){               /*没到表尾*/
q=p->next;
delete p;
p=q;}
L->next=NULL;           /*头结点指针域为空*?
return OK;
}

5、求单链表的表长(从首元结点开始,依次计数所有的结点)

int ListLength_L(LinkList L){  /*返回L中元素的个数*/
LinkList p;
p=L->next;                     /*p指向首元结点*/
i=0;
while(p){                      /*遍历L,统计结点数*/
i++;
p=p->next;
}
return i;
}

6、取值—取单链表中第i个元素
1)从第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,int i,ElemType &e){  /*获取线性表中第i个元素的内容,通过变量e返回*/
p=L->next;j=1;            /*初始化*/
while(p&&j<i){            /*向后扫描,直到p指向第i个元素或p为空*/
p=p->next;++j;
}
if(!p||j>i)               /*第i个元素不存在*/
return ERROR;
e=p->data;                /*取第i个元素*/
return OK;
}//GetElem _L

7、按值查找

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

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

3)如果查遍整个链表都没有与e相等的元素,则返回0或error

Lnode *LocateElem_L(LinkList L,Elemtype e){
/*在线性表中查找值为e的数据元素,找到则返回值为e的数据元素的地址,查找失败则返回NULL*/
while(p&&p->data!=e)
p=p->next;
return p;
}

8、插入操作—在第i个结点插入值为e的新结点

1)首先找到ai-1的存储位置p

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

3)插入新结点:1、新结点时指针域指向结点ai

                             2、结点ai-1的指针域指向新结点(两步不可以调换位置,因为会丢失ai的地址)

Status LinkInsert_L(LinkList &L,int i,ElemType e){
p=L;j=0;
while (p&&j<i-1){            /*寻找第i-1个结点,p指向第i-1个结点*/
p=p->next;++j;
if(!p||j>i-1) return ERROR;  /*非法情况*/
s=new LNode;s->data =e;       /*建立新结点,将结点s的数据域设置为e*/
s->next=p->next;              /*将s结点的指针域指向ai*/
p->next =s;                   /*将p结点的指针域指向s*/
return OK;
}//LinkInsert _L

9、删除—删除第i个结点

1)首先找到ai-1的存储位置p,保存要删除ai的值

2)令p->next指向ai+1

3)释放ai的空间

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

10、建立单链表:头插法——元素插在链表头部,也叫前插法

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

2)建立一个新结点,将读入的数据存放在新结点的数据域中

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

4)  算法的时间复杂度为:O(n)

5)重要操作:p->next=L->next;

                        L->next=p; (不可调换位置,会失去L的后继结点的地址)

void CreateList_L(LinkList &L,int n){
    L=newLNode;     /*生成新结点*/
    L->next=NULL;   /*将头结点的指针域置空*/
    for(i=n;i>0;--i){ /*从最后一个结点开始,依次将各结点插入到链表的前端*/
    p=newLNode;
    cin>>p->data;
    p->next=L->next;
    L->next=p;
    }
}//CreateList_H

11、建立单链表:尾插法——元素插入在链表的尾部,也叫做后插法

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

2)初始时,r与L均指向头结点,每读入一个新数据则申请一个新结点,当新结点插入到尾结点后

人指向新结点。

3)算法复杂度为O(n)。

void CreateList_R(LinkList &L,int n)
{
    L=new LNode;                 /*生成新结点*/
    L->next=NULL;                /*将新结点的指针域置为空*/
    r=l;                         /*尾指针L指向头结点*/
    for(i=0;i<n;i++)
    {
        p=new LNode;cin>>p->data;/*生成新结点,并为结点的数据域赋值*/
        p->next=NULL;            /*将新结点的指针域赋值为空*/
        r->next=p;               /*将p插入表尾*/
        r=p;                     /*r指向新的尾结点*/
    }
}//CreateList_R

12、带尾指针循环链表的合并

1)p存储表头结点

2)Tb的表头连接到Ta的表尾

3)释放Tb的表头结点

4)Tb的表尾接到Ta的表头

5)时间复杂度为O(1)

LinkList Connect(LinkList Ta,LinkList Tb){ //假设Ta,Tb都是非空的单循环链表
    p=Ta->next;                            //p存储表头结点
    Ta->next=Tb->next->next;               //Tb表头连接Ta的表尾
    delete Tb->next;                       //释放Tb的头结点
    Tb->next=p;                            //修改指针
    return Tb;
}

13、双向链表的定义

1)在单链表的每个结点里再增加一个指向其直接前驱的指针域prior,这样链表中就形成了有两个方向不同的链,故称为双向链表

2)让头结点的前驱指针指向链表的最后一个结点,让最后一个结点的后继结点指向头结点

3)在双向链表中有些操作(如:ListLength、GetElem等),因仅涉及一个方向的指针,故它们的算法与线性链表的相同。但在插入,删除的时候,则需要同时修改两个方向上的指针,两者操作的时间复杂度均为O(n)

typedef struct DuLNode{
    Elemtype data;
    struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;

14、双向链表的插入

void ListInsert_DuL(DuLinkList &L,int i,ElemType e){
    if(!(p=GetElemP_DuL(L,i))) return ERROR;  //非法情况
    s=new DuLNode;         //建立新结点
    s->data=e;
    s->prior=p->prior;
    p->prior->next=s;
    s->next=p;
    p->prior=s;
    return OK;
}//LinkInsert_DuL

15、双向链表的删除

​​​​​​​

void ListDelete_DuL(DuLink &L,int i,ElemType &e){
    //删除带头结点的双向循环链表的第i个元素,并用e返回
    if(!(p=GetElemP_DuL(L,i))) return ERROR;
    e=p->data;
    p->prior->next=p->next;
    p->next->prior=p->prior;
    free(p);
    return OK;
}  //ListDelete_DuL

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值