03.线性表(二)链式存储结构.单链表1

链式存储结构.单链表1 1.基本概念 为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置) (1)数据域:存储线性表数据元素数据信息的域称为数据域; (2)指针域:把存储直接后继位置(下一个数据元素的地址)的域称为指针域,指针域中存储的信息为指针或链; (3)结点(Node):由数据域和指针域两部分信息组成数据元素ai的存储映像,称为结点。 (4)头指针:把链表中第一个结点的存储位置叫做头指针,因此整个链表的存取就必须从头指针开始进行。 (5)头结点:为了更加方便地对链表进行操作,我们在单链表的第一个结点前附设一个结点,即为头结点。头结点的数据域可以不存储任何信息,也可以存储如线性表的长度等附加信息,头结点的指针域存储指向第一个结点的指针。


升华笔记一:头指针和头结点的异同? 头指针: a.头指针是指链表指向第一个结点的存储位置指针,若链表有头结点,则是指向头结点的指针。 b.头指针具有标识作用,所以常用头指针冠以链表的名字; c.无论链表是否为空,头指针均不为空。头指针是链表的必要元素。 头结点: a.头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(也可以存放链表的长度); b.有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就同一了; c.头结点不一定是链表必要素。


2.单链表 n个结点(ai的存储映像)链结成一个链表,即为线性表(a1,a2....an)的链式存储结构,因为此链表的每个结点只包含一个指针域,所以称之为单链表。 03.线性表(二)链式存储结构.单链表1
3.链式存储结构指针代码示例 /*线性表的单链表存储结构 * 结点由存放数据元素的数据域和存放后继结点地址的指针域组成*/ typedef int ElemType;
typedef struct Node { ElemType data; //int类型成员变量,作为结点的数据域 struct Node *next; //struct Node类型成员指针变量,作为结点的指针域 }Node; typedef struct Node *LinkList; //定义LinkList 注释: (1)Node相当于struct Node结构体;*LinkList相当于struct Node结构体 (2)假设指针p是指向线性表第i个元素的指针,则结点ai数据域和指针域为p->data、p->next即: p->data的值为该结点ai的数据域,内容是一个数据元素; p->next的值是一个指针,指向第(i+1)个元素(结点)的存储地址,即指向ai+1的指针 即p->data=ai p->next->data=ai+1 4.单链表的读取、插入与删除操作 (1)单链表的读取操作 实现操作: GetElem(LinkList L,int i,ElemType *e),从单链表L中取出第i个数据元素,存放到指针变量i指向的地址空间中。 算法思路:不像线性表的顺序存储结构可以很容易的从任意位置读取一个元素,单链表的数据元素读取必须从头开始找,即工作指针p后移。 a.声明一个结点p指向链表第一个结点,初始化j从1开始遍历 b.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1,直到链表末尾p为空。则说明第i个元素不存在 c.如果p为空结点或者遍历位置超过i,抛出异常 d.查找成功,返回结点p的数据。 源码实现: /*初始条件:顺序线性表L已存在,1=《i<=ListLength(L) *操作结构:用e返回L中第i个数据元素的值*/ #define OK 1 #define ERROR 0 typedef int Status;
typedef int ElemType;
typedef struct Node *LinkList; //定义LinkList
Status GetElem(LinkList L,int i,ElemType *e) { int j; LinkList p; //声明一个结点p p=p->next; //让结点p指向链表L的第一个结点 j=1; while(p&&j<i) //从链表第一个结点开始遍历,直到j=i-1,让p指向存储i-1位置所在的结点 { p=p->next; //让p指向下一个结点 j++; } if(!p "| j>i) return ERROR; *e=p->data; //查找成功,将链表中的第i个元素的数据存储到指针e指向的空间 return OK; } 注释:p为新定义的一个结点,while(p&&j<i)的作用是使结点p从链表第一个结点开始遍历,直到j=i-1,让p指向存储i-1位置所在的结点,直到链表末尾p为空是说明第i个元素不存在。
(2)单链表插入操作 实现操作: ListInsert(LinkList *L,int i,ElemType e),将数据元素e插入到单链表L第i个位置 算法思路: s->next =p->next; p->next=s; a.声明一个结点p指向链表第一个结点,初始化j从1开始遍历 b.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1,直到链表末尾p为空,则说明第i个元素不存在。 c.如果p为空结点或者遍历位置超过i,抛出异常 d.如果查找成功, -在系统中生成一个空结点s -将数据元素e赋值给s->data; -单链表的插入标准语句s->next=p->next,p->next=s 源码实现: /*初始条件:顺序线性表L已存在,1=《i<=ListLength(L) *操作结构:将数据元素e插入到单链表L第i个位置*/ #define OK 1 #define ERROR 0 typedef int Status;
typedef int ElemType;
typedef struct Node *LinkList; //定义LinkList
Status ListInsert(LinkList *L,int i,ElemType e) { int j; LinkList p,s; //声明一个结点p p=*L; j=1; while(p&&j<i) //从链表第一个结点开始遍历,直到j=i-1,让p指向存储i-1位置所在的结点 { p=p->next; //让p指向下一个结点 j++; } if(!p || j>i) return ERROR; s=(LinkList)malloc(sizeof(Node)); //生成新结点,即在内存中找了一小块空地,准备用来存放e数据s结点 s->data=e; //关键1:查找成功,将数据元素e赋值给s结点的数据域 s->next =p->next;//关键2:将结点p的后继结点赋值给s的后继(p->next为指向结点p下一个存储地址所在的结点) p->next=s; //关键3:设置结点p的后继结点为结点s return OK; } 注释:p为新定义的一个结点,while(p&&j<i)的作用是使结点p从链表第一个结点开始遍历,直到j=i-1,让p指向存储i-1位置所在的结点,直到链表末尾p为空是说明第i个元素不存在。 (3)单链表删除操作 实现操作: ListDelete(LinkList *L,int i,ElemType *e),删除单链表L中第i个数据元素,并将数据存放至指针e指向的空间中 算法思路:{ p->next=p->next->next(或者q=p->next;p->next=q->next) } a.声明一个结点p指向链表第一个结点,初始化j从1开始遍历 b.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1,直到链表末尾p为空,则说明第i个元素不存在。 c.如果p为空结点或者遍历位置超过i,抛出异常 d.如果查找成功 -将欲删除的结点p->next赋值给q -设置结点p的后继结点为q->next,即p->next=q->next; -将结点q的数据域数据存放至e e.释放q结点,返回成功标识 源码实现: /*初始条件:顺序线性表L已存在,1=《i<=ListLength(L) *操作结构:删除单链表L中第i个数据元素,并将数据存放至指针e指向的空间中*/ #define OK 1 #define ERROR 0 typedef int Status;
typedef int ElemType;
typedef struct Node *LinkList; //定义LinkList
Status ListInsert(LinkList *L,int i,ElemType *e) { int j; LinkList p,q; //声明一个结点p p=*L; j=1; while(p->next&&j<i) //从链表第一个结点开始遍历,直到j=i-1,让p指向存储i-1位置所在的结点 { p=p->next; //让p指向下一个结点 j++; } if(!p || j>i) return ERROR; q=p->next; //设置q为p的后继结点 p->next=q->next; //将q的后继结点设置为p的后继结点 *e=q->data; //将q结点中的数据给e free(q); //设置完成后,让系统回收此结点,释放内存 return OK; }


升华笔记二:s、p、s->data、s->next、p->next辨析? (1)LinkList p,s:声明两个结点p、s,包含数据域和指针域; (2)s->data:为结点s的数据域,其值为一个数据 (3)s->next::为结点s的指针域,其值为下一个结点存储地址 (4)s->next=p->next:将结点p的下一个结点存储地址赋值给s结点指针域 (5)p->next=s; 将结点s设置为p的下一个结点 事实上,(4)(5)我们可以这样理解: 假设初始链表相连两个结点p、q,即......-p-q-......,现在我们需要在结点p、q之间插入一个结点s,则 方法如下:由于p->next=q 则s->next=q //结点s后继为q p->next=s //结点p后继为s,此时即可形成......-p-s-q-......


5.顺序存储结构性能分析 (1)查找:最好情况时间复杂度为O(1),最坏情况事件复杂度为O(2) (2)删除、插入:单链表的插入和删除主要由两部分组成:第一部分是遍历查询第i个元素;第二部分就是插入和删除元素 从整个算法来说,我们很容易推导出:它们的时间复杂度都是O(n)。如果在我们不知道第i个元素的指针位置,单链表数据结构在插入和删除操作上,与线性表的顺序存储解雇是没有太大的优势。但是如果我们希望从第i个位置,插入10个元素,对于顺序存储结构意味着,每一次插入都需要移动n-i个元素,每次都是O(n)。而对于单链表,我们只需要在第一次找到第i个位置的指针,此时为O(n),接下来只是简单地通过赋值移动指针而已,时间复杂度都是O(1)。 总结:对于插入或删除数据越频繁的操作,单链表的效率优势就越明显。

分类:默认分类 时间:2015-03-08 人气:1
本文关键词:
分享到:

相关文章

Copyright (C) quwantang.com, All Rights Reserved.

趣玩堂 版权所有 京ICP备15002868号

processed in 0.040 (s). 9 q(s)