《数据结构》第二章 线性表

第二章 线性表

2.1 线性表的定义和特点

诸如此类由n(n>=0)个数据特性相同的元素构成的有限序列,成为线性表。

线性表中的元素个数n(n>=0)定义为线性表的长度,当n=0时称之为空表。

2.2 案例引入

2.3 线性表的类型定义

线性表:是一种相当灵活的数据结构,其长度可根据要增长或缩短。

2.4 线性表的顺序表示和实现

2.4.1 线性表的顺序表示

指的是用一组地址连续的存储单元一次存储线性表的数据元素。(也称为线性表的顺序存储结构或顺序映像)

特点:逻辑上相邻的数据元素,其物理位置也是相邻的。

​ 随机存取

2.4.2 顺序表中基本操作和实现
1.初始化

顺序表的初始化操作就是构造一个空的顺序表。

2.取值

取值操作是根据指定的位置序号i,获取顺序表中第i个数据元素的值。

3.查找

查找操作是根据指定的元素值e,查找顺序表中的第1个值与e相等的元素。

4.插入
顺序表的插入

1.判断插入位置 i 是否合法(i 值的合法范围是 1≤i≤n+ I), 若不合法 则返回 ERROR。

2.判断顺序表的存储空间是否已满,若满则返回 ERROR。

3.将第n个至第i个位置的元素依次向后移动一个位置,空出第i个位置(i=n+i时无需移动)

4.将要插入的新元素e放入第i个元素。

5.表长加1。

Status Listinsert(SqList &L, int i, ElemType e)
   {
        //在顺序表 L 中第 i 个位置之前插入新的元素 e, i值的合法范围是 1...i...L.length+l
        if ((i < l) || (i > L.length + l)) {
            return ERROR;                       //i值不合法
        }
        if (L.length == MAXSIZE) {
            return ERROR;                       //当前存储空间已满
        }
        for (j = L.length - 1; j >= i - 1; j--) {
            L.elem[j + l] = L.elem[j];                     //插入位置及之后的元素后移
        }
        L.elem[i - l] = e;                                //将新元素e放入第l个位置
        ++L.length;                                       //表长加1
        return OK;
    }
5.删除

线性表的删除操作是指将表的第i个元素删去,

将长度为n的线性表:(a1+…+ai-1+ai+ai+1+…an)

变成长度为n-1的线性表:(a1+…+ai-1+ai+ai+1+…an)

顺序表的删除

1.判断删除位置i是否合法(合法值为1<=i<=n),若不合法则返回ERROR。

2.将第i+1个至第n个元素依次向前移动一个位置(i=n时无法移动)

3.表长减1

Status ListDelete(SqList &L, int i) {
        //在顺序表L中删除第J.个元素,J.值的合法范围是 1.;;i.;;L. length
            if ((i < l) || (i > L.length)) {
                return ERROR;                               //i值不合法
            }
            for (j = i; j <= L.length - 1; j++) {
                L.elem[j - 1]=1.elem[j];                    //被删除元素之后的元素前移
            }
            --L.length;                                     //表长减 1
            return OK;
        }

2.5 线性表的链式表示和实现

2.5.1 单链表的定义和表示

线性表链式存储结构的特点是:用一组任意的存储单元存储线性表的数据元素。

结点:数据域和指针域组成数据元素的存储映像。

数据域:存储数据元素信息的域。

指针域:存储直接后继存储位置的域。(存储域中存储的信息称作指针或链)

单链表线性链表:n个结点【ai(1<=i<=n)的存储映像】链接成的一个链表,每个结点值包含一个指针域。

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

头结点:首元结点之前附设的一个结点,其指针域指向首元结点。

头指针:指向链表中的第一个结点的指针。

2.5.2 单链表基本操作的实现
1.初始化

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

2.头结点的指针域置空。

Status InitList(LinkList &L)
{//构造一个空的单链表
	L=new LNode;		//生成新结点作为头结点,用头指针执行L指向头结点
	L->next = NULL;		//头结点的指针置空
	return OK;			
}
2.取值

1.用指针p指向首元结点,用j做计数器初值赋为1.

2.从首元结点开始依次顺着链域next向下访问,只要指向当前结点的指针p部位空

(NULL),并且没有到达序号为i的结点,则循环执行以下操作:

==1.==p指向下一结点;

==2.==计数器相应加1;

3.退出循环时,如果指针p为空,或者计数器j大于i、,说明指定的序号i值不合法

(i大于表厂n或i小于等于0),取值失败返回ERROR;否则取值成功,此时j=i时,p所致的接待你就是要找的第i个结点,用参数e保存当前结点的数据域,返回OK。

    Status GetElem(LinkList L, int i, ElemType &e) {
        //在带头结点的单链表L中根据序号l.获取元素的值,用e返回L中第l.个数据元素的值
        p = L->next;
        j = 1;                              //初始化,p指向首元结点,计数器]初值赋为1
        while (p && j < i) {                //顺链域向后扫描,直到p为空或p指向第l.个元素
            p = p->next;                    //p指向下一个结点
            ++j;                            //计数器j相应加1
        }

        if (!p || j > i) return ERROR;      // i值不合法 i>n或i≤0
        e = p->data;                        //取第i个结点的数据域
        return OK;
    }

3.查找

单链表的按值查找(该数据的地址)

1.用指针p指向首元结点。

2.从首元结点开始依次顺着链域next向下查找,只要指向当前结点的指针p不为空,

并且p所指结点的数据域不等于给定值e,则循环执行以下操作:p指向下一结点。

3.返回p;若查找成功,p此时指向结点的地址值,若查找失败,则p的值为NULL。

LNode *LocateElem(LinkList L,ElemType e)
{	//在带头结点的单链表L中查找值为e的元素
	p=L->next;		//初始化,p指向首元结点
	while(p && P->date ! e)		//顺链域向后查找,知道p为空或p所指结点的数据域等于e
		p = p -> next;			//p指向下一结点
		return p;		//查找成功返回值为e的结点地址p,查找失败p为NULL
}

按值查找变化(第几个数据元素)

根据指定数据获取该数据的位置序号

LNode *LocateElem(LinkList L,ElemType e)
{//在带头结点的单链表L中查找值为e的元素
 p = L->next;j=1;		//初始化,p指向首元结点,j初始值为1
 while(p && p->date ! e)//顺链域向后查找,知道p为空或p所指结点的数据域等于e
 p = p -> next;j++		//p指向下一结点,j的值加1
 if(p)		
 return j;
 else return 0;
}
4.插入

将值为e的新结点插入到表的第 i 个结点的位置上,即插入到结点 ai-1与ai之间。

1.查找结点 ai-1 并由指针p指向该结点

2.生成一个新结点*s

3.将新结点*s 的数据域置为 e

4.将新结点*s 的指针域指向结点ai

5.将结点 * p 的指针域指向新结点*s

 Status Listinsert(LinkList &L, int i, ElemType e) {
        //在带头结点的单链表L中第i个位置插入值为e的新结点
        p = L;
        j = O;
        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;
    }

5.删除

1.查找结点a(i-1)并由指针p指向该结点。

2.临时保存待删除结点ai的地址在q中,以备释放。

3.将结点*p的指针域指向ai的直接后继结点。

4.释放结点ai的空间

    Status ListDelete(LinkList &L, int i) {
//在带头结点的单链表L中,删除第l个元素
        p = L;
        j = O;
        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;
    }

6.创建单链表

1.前插法

1.创建一个只有头结点的空链表。

2.根据待创建链表包括的元素个数n,循环n次执行以下操作:

1生成一个新结点*p;

2输入元素值赋给新结点*p的数据域;

3将新结点*p插入到头结点之后;

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->date;	//输入元素值赋给新结点*p的数据域
	p->next=L->next;L->next=p;		//将新结点*p插入头结点之后
}

2.后插法

1.创建一个只有表头的空链表

2.尾指针r初始化,指向头结点;

3.根据创建链表包括的元素个数n,循环n次执行以下操作:

1生成一个新结点*p;

2输入元素值赋给新结点*p的数据域;

3将新结点*p插入尾结点 *r之后;

4尾指针r指向信点尾结点*p;

Void CreateList_R(LinkList &L,int n)
{
//正位序输入n个元素的值,建立带表头结点的单链表L
L=new Lnode;		
L->next = NULL;		//先建立一个带头结点的空链表
e = L;				//尾指针r指向头结点
for(i=0;i<n;++i)
{
	p = new LNode;	//生成新结点
	cin>>p->date;	//输入元素值赋给新结点*p的数据域
	p->next = NULL; //将新结点*p插入尾结点*r之后
	r->next=p;		//r指向新的尾结点*p
	r=p;
}
}
2.5.3 循环链表

循环链表:另一种形式的链式存储结构。

特点:表中最后一个结点的指针域指向头结点,整个链表形成一个环。

(从表中任一结点出发均可找到表中的其他结点)

2.5.4 双向链表

双向链表的结点中有两个指针域,一个指向直接后继,另一个指向直接前驱。

//------双向链表的存储结构-------
typedef struct DuLNode
{
  ElemType date;			//数据域
  struct DuLNOde *prior;	//指向直接前驱
  struct DuLNode *next;		//指向直接后继
}DuLNode, DuLinkList;
1.双向链表的插入
Status ListInsert_DuL(DuLinkList &L,int i,ElemType e)
{//在带头接待你的双向链表L中第i个位置之前插入元素e
if(!(p=GetElem_Dul(L,i)))		//在L中确定第i个元素的位置指针p
	return ERROR;				//p为NULL时,第i个元素不存在
  s=new DuLNode;				//生成新结点*s
  s->date = e;					//将结点*s数据域置为e
  s->prior =p->prior;			//将结点*s插入L中1
  p->prior->next=s;				//2
  s->next=p;					//3
  p->prior=s;					//4
	return OK;
}
2.双向链表的删除
Status ListDelete_Dul(DuLinkList &L,int i)
{//删除带头结点的双向链表L中的第i个元素
	if(!(p=GetElem_DuL(L,i)))		//在L中确定第i个元素的位置指针p
		return ERROR;				//p为NULL时,第i个元素不存在
	p->prior->next=p->next;			//修改被删结点的前驱结点的后继指针
    p->next->prior=p->prior;		//修改被删结点的后继结点的前驱指针
    delete p;						//释放被删结点的空间
    return OK;
}

2.6 顺序表和链表的比较

2.6.1 空间性能的比较
1.存储空间的分配
2.存储密度的大小

存储密度:数据元素本身所占用的存储量和整个结点结构所占用的存储量之比。

存储密度 = 数据元素本身占用的存储量 / 结点结构占用的存储量

2.6.2 时间性能的比较

1.存取元素的效率

2.插入和删除操作的效率

2.7 线性表的应用

2.7.1 线性表的合并

有序表:线性表中的数据元素相互之间可以比较,数据元素在线性表中依值

非递减(递增或相等)或非递增(递减或相等)有序排列。

有序集合:集合中的元素有序排列。

2.7.2 有序表的合并
1.顺序有序表的合并

1.创建一个表长为m+n的空表LC。

2.指针pc初始化,指向LC的第一个元素。

3.指针pa和pb初始化,分别指向LA和LB的第一个元素。

4.当指针pa和pb未到达相应表尾时,则依次比较pa和pb所指向元素值,从LA或LB中

”摘取“元素值较小的结点处插入LC的最后。

5.如果pb已到达LB的表尾,依次将LA的剩余元素插入LC的最后。

6.如果pb已到达LA的表尾,依次将LB的剩余元素插入LC的最后。

2.链式有序表的合并

链表结点之间的关系时通过指针指向建立起来的,所以用链表进行合并不需要另外建立存储空间,

合并时只需要把LA和LB的结点重新进行连接。

1.指针ap和pb初始化,分别指向LA和LB的第一个结点。

2.LC的结点取值为LA的头结点。

3.指针pc初始化,指向LC的头结点。

4.当指针pa和pb均未到达相应表尾时,则依次比较pa和pb所指向的元素值

从LA和LB中摘取元素值较小的结点插入LC的最后。

5.将非空表的剩余段插入pc所指结点之后。

6.释放LB的头结点。

2.8 案例分析与实现

2.9 小结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值