线性表(Linear List)

线性表的抽象数据类型

线性表(Linear List)

定义
是由n (n≥0)个类型相同的数据元素a1
,a2,…,an组成的有限序列,记做(a1,a2,…,ai-1,ai,ai+1,…,an)。
线性表的逻辑结构图为:o-o-o-o-o-o-o
特点
同一性:线性表由同类数据元素组成,每一个ai必须属于同一数据对象。
有穷性:线性表由有限个数据元素组成,表长度就是表中数据元素的个数。
有序性:线性表中相邻数据元素之间存着序偶关系<ai,ai+1>。

线性表的抽象数据类型定义

在这里插入图片描述

线性表的顺序存储

顺序存储结构
• 用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
• 采用顺序存储结构的线性表通常称为顺序表

顺序存储结构的C语言定义
在这里插入图片描述

顺序存储结构线性表的基本操作

查找操作 按序号查找GetData(L,i): 按内容查找Locate(L,e):
插入操作 线性表的插入运算是指在表的第i (1≤i≤n+1)个位置,插入一个新元素e,使长度为n的线性表变成长度为
n+1的线性表
删除操作 指将表的第i(1≤i≤n)个元素删去,使长度为n的线性表变成长度为n-1的线性表
顺序表合并 有两个顺序表LA和LB,其元素均为非递减有序排列,编写一个算
法,将它们合并成一个顺序表LC,要求LC也是非递减有序排列。

顺序存储结构的优点和缺点
优点 无需为表示结点间的逻辑关系而增加额外的存储空间; 可方便地随机存取表中的任一元素。
缺点 插入或删除运算不方便,除表尾的位置外,在表的其它位置上进行插入或删除操作都必须移动大量的结点,效率较低;由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配。因此当表长变化较大时,难以确定合适的存储规模

线性链表

线性表的链式存储
• 用一组任意的存储单元存储线性表的数据元素;
• 利用指针实现了用不相邻的存储单元存放逻辑上相邻的元素;
• 每个数据元素ai,除存储本身信息外,还需存储其直接后继的信息。
链表:采用链式存储结构的线性表称为链表;从实现角度看,链表可分为动态链表和静态链表;从链接方式的角度看,链表可分为单链表、循环链表和双链表。

单链表

**结点(Node)**为了正确地表示结点间的逻辑关系,必须在存储线性表的每个数据元素值的同时,存储指示其后继结点的地址(或位置)信息。
在这里插入图片描述单链表:结点中只含一个指针域的链表叫单链表。
在这里插入图片描述带头结点的单链表示意图有时为了操作的方便,还可以在单链表的第一个结点之前附
设一个头结点。
单链表的存储结构描述

typedef struct Node / * 结点类型定义 * / 
{  
 	ElemType data;
 	struct Node * next;
}Node, *LinkList;/* LinkList为结构指针类型*/
单链表上的基本操作

• 建立单链表
头插法建表 从一个空表开始,重复读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头结点之后,直至读入结束标志止。

Linklist CreateFromHead()
{ 
LinkList L; Node *s; int flag=1;
/* 设置一个标志,初值为1,当输入“$”时,flag为0,建表结束*/
L=(Linklist)malloc(sizeof(Node))/*为头结点分配存储空间*/
L->next=NULL;
while(flag)
{ 
 c=getchar(); 
 if(c!=’$’) /*为读入的字符分配存储空间*/
 { 
 s=(Node*)malloc(sizeof(Node)); 
 s->data=c;
 s->next=L->next; 
 L->next=s; 
 }
 else flag=0;
 }
}

尾插法

Linklist CreateFromTail() /*将新增的字符追加到链表的末尾*/ 
{ 
 LinkList L; Node *r, *s; int flag =1;
 L=(Node * )malloc(sizeof(Node));/*为头结点分配存储空间*/
 L->next=NULL; 
 r=L; /*r指针始终动态指向链表的当前表尾*/
 while(flag)/*标志,初值为1。输入“$”时flag为0,建表结束*/
 { c=getchar();
 if(c!=’$’)
 { s=(Node*)malloc(sizeof(Node)); 
 s->data=c;
 r->next=s; 
 r=s }
 else 
 { 
 flag=0; 
 r->next=NULL; 
 }
 }
 }

• 单链表插入
要在带头结点的单链表L中第i个数据元素之前插入一个数据元素e,需要首先在单链表中找到第i-1个结点并由指针pre指示,然后申请一个新的结点并由指针s指示,其数据域的值为e,并修改第i-1个结点的指针使其指向s,然后使s结点的指针域指向第i个结点。

void InsList(LinkList L,int i,ElemType e)
{ /*在带头结点的单链表L中第i个结点之前插入值为e的新结点。 */
 Node *pre,*s; pre=L; int k=0;
 while(pre!=NULL&&k<i-1) 
 /*先找到第i-1个数据元素的存储位置,使指针Pre指向它*/
 { 
   pre=pre->next;  
   k=k+1; 
 }
 if(k!=i-1) 
 { 
   printf(“插入位置不合理!”)return; 
 }
 s=(Node*)malloc(sizeof(Node)); /*为e申请一个新的结点*/
 s->data=e; /*将待插入结点的值e赋给s的数据域*/
 s->next=pre->next; 
 pre->next=s; 
 }

• 单链表删除
欲在带头结点的单链表L中删除第i个结点,则首先要通过计数方式找到第i-1个结点并使p指向第i-1个结点,而后删除第i个结点并释放结点空间。

void DelList(LinkList L,int i,ElemType *e)
/*在带头结点的单链表L中删除第i个元素,并保存其值到变量*e中*/ 
{ 
Node *p,*r; p=L; int k =0;
while(p->next!=NULL&&k<i-1) /*寻找被删除结点i的前驱结点*/
 { 
 p=p->next; k=k+1; 
 }
if(k!=i-1) /* 即while循环是因为p->next=NULL而跳出的*/ 
{ printf(“删除结点的位置i不合理!”); return ERROR; 
}
r=p->next; p->next=p->next->next ; /*删除结点r*/
free(r); /*释放被删除的结点所占的内存空间*/ }

求单链表的长度
可以采用“数”结点的方法来求出单链表的长度,用指针p依次指向各个结点,从第一个元素开始“数”,一直“数”到最后一个结点(p->next=NULL)。

int ListLength(LinkList L) /*L为带头结点的单链表*/
 { 
 Node *p; 
 p=L->next;
 j=0; /*用来存放单链表的长度*/
 while(p!=NULL)
 { 
 p=p->next; j ++; 
 }
 return j; 
 }

• 单链表查找

按序号查找 设带头结点的单链表的长度为n,要查找表中第i个结点,则需要从单链表的头指针L出发,从头结点(L->next)开始顺着链域扫描,用指针p指向当前扫描到的结点,初值指向头结点(pL->next),用j做记数器,累计当前扫描过的结点数(初值为0),当j = i 时,指针p所指的结点就是要找的第i个结点 。

/ * 在带头结点的单链表L中查找第i个结点,若找到(1≤i≤n),则返回该结点的存储
位置; 否则返回NULL * /
Node * Get(LinkList L, int i)
{ 
 Node *p;
 p=L;j=0/ * 从头结点开始扫描 * /
 while ((p->next!=NULL)&&(j<i))
 {
   p=p->next; j++/ * 扫描下一结点 * /
 } / * 已扫描结点计数器 * /
 if(i= =j)return p; / * 找到了第i个结点 * /
 else return NULL/ * 找不到,i≤0或i>n * / }

按值查找 按值查找是指在单链表中查找是否有结点值等于e的结点,若有的话,则返回首次找到的其值为e的结点的存储位置,否则返回NULL。查找过程从单链表的头指针指向的头结点出发,顺着链逐个将结点的值和给定值e作比较

/ * 在带头结点的单链表L中查找其结点值等于key的结点,若找到则返
回该结点的位置p,否则返回NULL * /
Node *Locate( LinkList L,ElemType key)
{ Node *p;
 p=L->next; / * 从表中第一个结点比较 * /
 while (p!=NULL)
 if (p->data!=key)
 p=p->next; 
 else break; / * 找到结点key,退出循环 * /
 return p; 
 }
循环单链表

循环链表(Circular Linked List) 是一个首尾相接的链表。
(1)表中最后一个结点的指针指向第一个结点或表头结点(如有表头结点的话)。
(2)循环链表的运算与线性链表基本一致。但两者判断是否到表尾的条件不同。
循环链表合并
已知:有两个带头结点的循环单链表LA、LB,编写一个算法,将两个循环单链表合并为一个循环单链表,其头指针为LA。 算法思想:先找到两个链表的尾,并分别由指针p、q指向它们,然后将第一个链表的尾与第二个表的第一个结点链接起来,并修改第二个表的尾Q,使它的链域指向第一个表的头结点。

LinkList merge_1(LinkList LA,LinkList LB)
/*此算法将两个链表的首尾连接起来*/
{ 
 Node *p, *q; p=LA; q=LB;
 while (p->next!=LA) p=p->next; /*找到表LA的表尾*/
 while (q->next!=LB) q=q->next; /*找到表LB的表尾*/
 q->next=LA;
 /*修改表LB的尾指针,使之指向表LA的头结点*/
 p->next=LB->next;
 /*修改表LA的尾指针,使之指向表LB中的第一个结点*/
free(LB);
return(LA); 
}

LinkList merge-2(LinkList RA, LinkList RB)
{ /* 此算法将两个链表首尾连接起来 */
 Node *p; 
 p=RA->next; /* 保存链表RA的头结点地址 */
 RA->next=RB->next->next; 
 /*链表RB的开始结点链到链表RA的终端结点之后 */ 
 free(RB->next); /* 释放链表RB的头结点 */
 RB->next=p; /* 链表RA的头结点链到链表RB的终端结点之后 */
 return RB; /* 返回新循环链表的尾指针 */
}

双向链表

定义:在单链表的每个结点里再增加一个指向其前趋的指针域prior。这样形成的链表中就有两条方向不同的链,称之为双 ( 向) 链表 (Double Linked List)。
双链表的结点结构
**加粗样式**

typedef struct Dnode
{
 ElemType data;
 struct DNode *prior,*next;
}DNode,* DoubleList;

在链表结构中保存当前位置和元素个数
线性表的三种链式存储结构特点:实现较一致,处理亦简单,但效率低下。
原因:许多应用程序是按顺序处理线性表的中数据元素的,也就是
处理完一个数据元素后再处理下一个数据元素,也可能要几次引用
同一个数据元素,比如在访问下一个数据元素之前做GetElem和SetElem操作,对于这类应用程序,前面的链表实现效率低下。
解决:在链表结构中保存当前位置和元素个数。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线性表C++语言实现,包含《数据结构算法与应用-C++语言描述》练习题。 template<class T> class LinearList { public: LinearList(int MaxListSize = 10);//构造函数 LinearList(LinearList<T>& L);//复制构造函数 ~LinearList() { delete[] element; }//析构函数 bool IsEmpty() const { return length == 0; }//判断线性表是否为空 int Length() const { return length; }//返回数组长度 void SetLength(int SetLength) { length = SetLength; }//设置数组长度 int MaxSize_ret() const { return MaxSize; }//返回数组最大长度 int Current_ret(void) const { return current; } bool Find(int k, T& x);//返回第k个元素至x中 int Search(const T& x) const;//返回x所在位置 LinearList<T>& Delete(int k, T& x);//删除第k个元素并将它返回至x中 LinearList<T>& Insert(int k, const T& x);//在第k个元素之后插入x LinearList<T>& Insert(const T& x);//在表尾插入x void Output(ostream& out) const; void Self_adaption_LinearList_Size(void); void Reverse(void); bool Half(void); LinearList<T>& Alternate(LinearList<T>& A, LinearList<T>& B); LinearList<T>& Merge(LinearList<T>& A, LinearList<T>& B); LinearList<T>& Merge_1(LinearList<T>& A, LinearList<T>& B); void Split(LinearList<T>& A, LinearList<T>& B); LinearList<T>& Sort(void); /*关于前移和后移*/ void Reset(void) { current = 1; } //置current为0 T Current(void) { return element[current-1]; }//返回当前元素 bool End(void);//当且仅当当前元素为表的最后一个元素时,返回true bool Front(void);//当且仅当当前元素为表的第一个元素时,返回true void Next(void);//移动current至表中的下一个元素,如果操作失败则引发一个异常 void Previous(void);//移动current至表中的前一个元素,如果操作失败则引发一个异常 private: int length;//线性表长度 int MaxSize;//线性表最大可容纳的长度 int current;//记录线性表当前的位置 T* element;//一维动态数组 };

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值