线性表基本总结

线性表

由n(n>=0)个数据特性相同的元素构成的有限序列称为 线性表;(n=0,是空表)
相邻元素之间存在着序偶关系;同一线性表中的元素必定具有相同的特性;

顺序表

(1)线性表的顺序表示是指用一组 地址连续的存储单元 依次存储线性表的数据元素
这种表示称为线性表的顺序存储结构或顺序映像 ,称这种存储结构的线性表为顺序表(SequentialList)
(2)线性表的顺序存储结构是 一种随机存取 结构
// …顺序表的存取结构…

#define  MAXSIZE 100   
typedefy struct 
{
	ElemType *elem;   //存储空间的基地址  (用指针的方式定义创建了数组  这是基地址)
	int  length ;          //说明一下长度 ,表示当前数据元素的个数;
}sqList;                // 顺序表的结构类型为 SqList  (数据类型)

说明: 因为C 语言中数组的下标是从0 开始的而位置序号是从1 开始的
数据元素 a1 a2 a3 , an 依次存放的数组的
elem[0] elem[1] elem[2] elem[length-1] 中

eg : (一元多项式的顺序存储结构)
#define MAXSIZE 100 //定义一个表长可能达到的最大长度
typedef struct
{
float coef ; // 系数
int expn ; // 指数
}Polynomial ; 多项式
typedef strcut
{
Polynomial *elem; //结构体类型的指针
int length ;
}Sqlist ;

(3)顺序表的基本操作

初始化

思想:就是构造一个空的 顺序表
算法步骤 :

  1. 先为顺序表L动态分配一个 预定义大小的数组空间,使elem 指向 这段空间的基地址
  2. 将表的当前长度设为0;
status InitList(SqList  &L)
{
	L.elem = new ElemType[MAXSIZE];
	if (! L.elem) exit (OVERFLOW);    //分配地址失败 返回 -2;
	L.length = 0;   //当前的表长为0 空表
	return OK;
}
取值

思想:根据顺序表的存储结构 直接定位 elem[i-1] 单元存储的地 i 个数据元素
算法步骤:

  1. 判断 给的位置是否合理 (1<= i <=length),如不合理 返回 ERROR;
  2. 若i 合理则将第 i 数据元素L。elem[i-1] 赋给参数 e
status GetElem(SqList L ,int i ,ElemType &e)
{
	if (i<1 || i> length) return ERROR;
	e = L .elem[i-1];
	return OK;
}
查找

思想:查找第一个 与 e 相等的元素 返回元素的位置序号 若失败则返回0 ;
算法步骤:

  1. 从第一个元素起 ,依次和e 比较 若找到与 e 相等的元素L.elem[i] ,返回 i +1;
  2. 若查找这个顺序表都没找到 则返回0;
int LocateElem(SqList  L,ElemType e)
 {
 	for( i=0 ; i < Length; i ++)
 		if (L.elem[i] == e )   return i+1;
 	
 	return 0;
}
插入

思想:在第i 个位置插入一个元素时 需要从最后一个元素即第n 个元素开始,依次向后移动一个位置,
直至第 i 个元素(共 n-i+1 个)

算法步骤:

  1. 判断 i 是否合法,(i值合法 1 <=i<= n-+1)
  2. 判断顺序表的存储空间 是否已满,若满足则返回ERROR.
  3. 将第 n 个元素至 第 i 个元素依次向后移动一个位置 空出第 i 个位置(i= n+1 时无需移动)
  4. 将要插入的元素 e 加入到第 i 个位置
  5. 表长加一
 status ListInsert(Sqlist &L ,int  i, Elemtype e)
 {
 	if (i<1 || i>length +1)  return ERROR;
 	if (L.length == MAXSIZE) return ERROR ;
 	for (j=L.length-1; j>=i-1; j --// 注意一下下标与位置的关系
		L.elem[j+1] = L.elem[j];
	L.elem[i-1] = e;
	++ L.length;
	return OK;
}
删除

思想: 一般情况下,删除第 i (1<=i <=n)个元素 时需要将第 i+1 个至第n 个元素( 共n-i 个元素)
依次向前移动 一个位置 (i=n 时无需移动)
算法步骤:

  1. 判断 i 是否合法
  2. 移动第 i 至第 n 个元素依次向前移动 一个位置( i= n 时无需移动)
  3. 表长减一
Status ListDelete (SqList &L, int i)
{
	if (i <1 || i> L.length )return ERROR ;
	for  (j=i ;j <= L.length -1;j ++)
		L.elem[j-1] = L.elem [j];
	-- L.length;
	return OK;
}
 

链表

一组任意的存储单元 存储线性表中的数据元素;(这组存储单元可以是连续的,也可以是不连续的)
为了表示每一个数据元素 ai 和其直接后继数据元素 a i+1 的关系的逻辑关系;
对于数据 ai 来说,除了存储器本身的信息外,还需存储一个能指示下一个位置的信息,
这两个部分组成数据元素的ai 的存储映像, 称为结点。
存储数据元素的信息的称为数据域 ;存储直接后继存储位置的域为指针域
每个结点只包含一个指针域,故 称为 线性链表或单链表
线性表中元素逻辑顺序 ,而不是它在存储器中的实际位置;

// 单链表的存储结构
typedef struct LNode
{
ElemType date ;
struct LNode *next ;
}LNode ,*LinkList ; //LinkList 为指向结构体LNode 的指针类型;

在这里LinkList 和 LNode * 是一样的
在C++ 中引用时 LinkList & 方便使用 不用想c 语言中的指针 害的在 用指针引用内容;

  1. 区分指针变量和结点变量两个不同的概念,若定义 LinkList p 或 LNode * p ;
    则 p 是指向某结点的指针变量,表示该结点的地址,而 *p 为对应的结点变量 表示该结点的名称,

  2. 增加头结点的作用
    (1)便于首元结点的处理 使其没有特殊性
    (2)便于空表和非空表的统一处理
    (当链表不设头结点的时假设 L 为单链表的头指针,指向首元结点当单链表的长度n =0 为空表时,L指针为空,可记为 L==NULL);
    增加头结点后无论链表是否为空,头指针都是指向头结点的非空指针 若为空表则头结点的指针域为非空指针可设为L->next ==NULL;

  3. 要想取得第i 个数据元素必须从头结点出发顺链去找也称顺序存储结构

单链表的初始化

思想:构建一个带有头结点的空表
算法步骤:

  1. 生成新节点作为头结点,用头指针指向头结点;
  2. 头结点的指针域置空
Status InitList (LinkList  &L)
{
	L = new LNode;
	L->next = NULL;
	return OK;
}
取值

给定的结点位置序号i 从链表的首元结点出发顺链去找 逐个结点访问
算法步骤

  1. 用指针 p 指向首元结点,用 J 来做计数器初值为 1 ( j 和 i 比较 )
  2. 从首元结点开始,只要指向当前的的结点指针 p 不为空并且没有达到序号为 i 的结点 ,则进行以下操作
    (1)p 指向下一个结点
    (2)计数器加1 ;
status GetElem( LinkList  L,int i ELemType &e)
{
	p = L->next ;j=1while (p && j < i)
	{
		p = p->next ;
		++j;
	}
	if ( !p || j > i) return ERROR;  // i >n 或 i <=0
	e =  p->date ;
	return OK;
}
查找

从链表的首元结点出发,依次比较结点值和给定值,返回结果;
算法步骤:

  1. 用指针 P 指向首元结点
  2. 只要当前结点 的指针不为空,如果和 e 不相等 p 指向下一个结点;
  3. 若成功 返回p 地址值 否则 返回 p 的值为 NULL;
LNode *LocateElem ( LinkList L ,ElemType e )
{
	p = L->next ;
	while( p && p->date!=e)
		p =p->next;
	return p;
}
插入

将值为e 的新结点 插入到标的第 i 个结点位置上,及插入到 ai-1 和 ai 之间;;
算法步骤;

  1. 查找到 结点ai-1 并有指针 p 指向该结点;
  2. 生成一个新结点 *s ;
  3. 将新结点*s 的数据域置为 e ;
  4. 将新结点的指针域指向 ai;
  5. 将结点* p 的指针域指向新结点;
Status ListInsert(Linklist &L ,int i, ElemType e)
{
	p = L , j =0;   
	while (p && (j< i-1){
		p = p-> next ; 
		j++;
	}
	if ( !p || j<i-1)  return ERROR;   //  i  > n+1 或者 i<1;
	 s= new LNode ;        // 生成 新结点 *s
	 s->date =  e;     // 将**结点 *s** 的数据域 置为 e;
	 s -> next = p ->next ;  
	 p ->next = s;
	 return OK;
}

总结 : 如果表中有 n 个结点,则合法的操作中 合法的位置 有 n +1 个,1 <=i <=n+1; n+1 时为链表尾部

删除

给定位置 i 顺链寻找 首先找到该位置的首元结点;
算法步骤:

  1. 查找 结点ai-1 并由指针 p 指向该结点;
  2. 临时保存待删结点ai 的地址 在 q 中已备释放
  3. 将结点*p 的指针域 指向ai 的直接后继结点
  4. 释放结点ai 的空间;
status ListDelete (LinkList &L, int  i)
{
	p= L; j=0;
	while( (P->next) &&( j < i-1)
	{
		p=p->next;j++;
	}
	if (! (p->next )||(j>i-1) )   return ERROR; //当 i > n或 i<1时,删除位置不合理;
	q =p->next ;
	p->next = q->nxet ;
	delete q;
	return OK;
}
单链表的创建

(1)用前插法创建单链表;
这里从(a1 a2 a3 a4 a5 …an )插入;
结果是( an,an-1,…a3, a2 a1)输出
思想: 是将新结点逐个插入链表的头部(头结点之后)每次申请一个新结点,
读入相应的数据元素,然后将新结点插入到头结点之后
算法步骤:

  1. 创建一个只有头结点的空表;
  2. 输入元素的赋给新节点*p 的数据域;
  3. 将新结点* p 插入到头结点之后
void CreatList_H (LinkList &L ,int n)
{
	L =new LNode;
	L->next = NULL;
	for (i =0 ;i<n;i++)
	{
		p =new LNode;
		cin >> p->date;
		p ->next =L->next ;  //将新结点* p 插入到结点之后
		L->next =p; // L 指向头结点 一直不动作为一个转接点
	}
}

(2)用尾插法创建链表
这个是顺序的
这里从(a1,a2,a3…an)
输出 (a1,a2 a3…an)
思想: 为了使新节点能插入到表尾需要增加一个尾指针 r 指向链表的尾结点
算法步骤:

  1. 创建一个只有头结点的空链表;
  2. 尾指针初始化,指向头结点
  3. 输入n 个元素,循环 n 次
    (1) 生成一个新结点 *p;
    (2)输入元素赋值给新结点 *p;
    (3)将新结点 *p 插入到尾结点 *r 之后
    (4)尾指针 r 指向新的尾结点 *p
void CreateList_R(LinkList & L ,int n)
{
	L= new LNode;
	L->next =NULL;
	 r= L;
	 for ( i=0;i<n;i++)
	 {
	 	p= new LNode;
	 	cin >> P->date;
	 	p ->next = NULL;
	 	r->next =p;
	 	r=p;
	 }
}

循环链表

它的特点是,表中的最后一个结点的指针域指向头结点,整个链表形成一个环,
从表中任意一个元素出发可以找到表中其他结点。
在这里插入图片描述

循环链表与单链表的基本操作一致,差别当链表遍历时,判别当前指针 p 是否指向表尾结点的终止条件不同,
(1)单链表中 : 判别条件是 p!= NULL 或 p->next != NULL;
(2)循环链表中判别条件: p!= L 或 p-> next != L;

循环条件:p!=NULL =》 p!=L
p->next!=NULL =》 p->next!=L
在某些情况下,循环链表中设尾指针而不设头指针 例如: 将两个线性表合并,仅需将第一个表的尾指针指向第二个表的第一个结点,第二个表的尾指针指向第一个表的头结点,然后释放第二个表的头结点,
在这里插入图片描述

在这里插入图片描述

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

上述时间复杂度为O(1);

顺序表和单链表的比较

在这里插入图片描述

双向链表

这里 的双向即是 存在两个指针域,在单链表中,查找直接后继
结点的执行时间为O(1), 而查找直接前驱的执行时间为O(n)
为了克服单链表的这种单向性的缺点,可利用双向链表;

(1)在***C 语言上***的算法定义,可描述为
// 双向链表的存储结构
Typedef struct DuLNode
{
ElemType date;
struct DuLNode *P;
struct DuLNode * next;
} DuLNode ,*DuLinkList;

(2)和单链表相似,双向链表也可以有循环链表,
在这里插入图片描述在这里插入图片描述
(3)在循环链表中若d 为指向表中的某一节点的指针;(d 为DuLinkList型的变量)
显然有:
d ->next ->prior = d->prior ->next =d;

双向链表的插入

在插入结点时,需要修改四个指针,
在这里插入图片描述

Status ListInsert_DuL(DuLinkList &L,int i,ElemType e)
{   //在带头结点的双向链表L中第i 个位置插入元素 e 
   if  ( !(p=GetElemP_DuL(L,i)) )  // 找到第i 个元素的位置指针 P
   	return ERROR;      
   s=new DuLNode;    
   s->data=e;
   //进行四步的指针操作
   s->prior=p->prior;  //对应第一步
   p->prior->next=s;    //对应 第二步
   s->next=p;         //第三步
   p->prior=s;        //第四步 ;
   return OK;
}
双向链表的删除

只需要修改两个指针;
在这里插入图片描述

Status ListDelete_DuL(DuLinkList &L,int i,ElemType &e)
{
   if   (!(p=GetElemP_DuL(L,i)))    
    return ERROR;
   e=p->data;
   p->prior->next=p->next;
   p->next->prior=p->prior;
   
   delete p; 
   return OK;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include"iostream" #include"string" #include<iomanip> #include<fstream> using namespace std; typedef int Status; typedef int ElemType; #define OK 1 #define ERROR 0 typedef struct LNode//定义 { ElemType data;//结点的数据域 struct LNode *next;//结点指针域 }LNode,*LinkList; //LinkList为结构体LNode的指针类型 Status InitList(LinkList &L)//初始 { L=new LNode; L->next=NULL; return OK; } void GreateList_L(LinkList &L,int n)//后插法 输入元素 { /*L=new LNode;//生成新的结点作为头结点,用头指针L指向头结点 L->next=NULL;//头结点指针域为空*/ LNode *r; LNode *p; r=L; //!!!!错误,定义指针 for(int i=0;i<n;++i) { p=new LNode; cin>>p->data; p->next=NULL; r->next=p; r=p; } } /*void CreateList_L(LinkList &L,int n)//前插法 { cout<<"输入录取数据个数 "; cin>>n; cout<<"输入数据"; L=new LNode;//生成新的结点作为头结点,用头指针L指向头结点 L->next=NULL;//头结点指针域为空 for(int i=0;i<n;++i) { p=new LNode; cin>>p->data; p->next=L->next; L->next=p; } }*/ Status GetElem(LinkList L,int i,int &e)//取值 { LNode *p; p=L->next;int j=1; while(p&&j<i) { p=p->next; ++j; } if(!p||j>i) return ERROR; e=p->data; return OK; } LNode *LocateElem(LinkList L,ElemType e)//查找 { int j=1; LNode *p; p=L->next; while(p&&p->data!=e) { p=p->next; } return p; } Status LisInsert(LinkList &L,int i,ElemType e)//插入 { LNode *p; p=L->next;int j=0; while(p&&(j<i-1)) { p=p->next; j++; } if(!p&&(j>i-1)) return ERROR; LNode *s; s=new LNode; s->data=e; s->next=p->next; p->next=s; return OK; } Status ListDelete(LinkList &L,int i)//删除 { LNode *p; p=L->next;int j=0; while(p&&(j<i-1)) { p=p->next; j++; } if(!(p->next)&&(j>i-1)) return ERROR; LNode *q; q=p->next; p->next=q->next; delete q; return OK; } int main() { LNode *L; int i,temp,n,choose=-1; int e; cout << "1. 创建信息表\n"; cout << "2. 查找元素\n"; cout << "3. 插入元素\n"; cout << "4. 删除元素\n"; cout << "0. 退出程序\n\n"; while(choose!=0) { cout << "请选择:"; cin >> choose; switch (choose) { case 1: cout<<"请输入元素个数:"; cin>>n; cout<<"\n"<<"请输入元素"; GreateList_L(L,n); cout<<"\n"; break; case 2:cout<<"请输入查找的元素:"; cin>>e; LNode *s; s=LocateElem(L,e); cout<<"这个元素的地址是"<<s<<"\n\n"; break; case 3:cout<<"请输入需要插入的数:"; cin>>e; cout<<"\n"<<"请输入需要插入的位置"; cin>>i; if(LisInsert(L,i,e)) cout<<"插入成功\n\n"; else cout<<"插入失败\n\n"; break; case 4: cout<<"请输入删除的位置:"; cin>>i; if(ListDelete(L,i)) cout<<"删除成功\n\n"; else cout<<"删除失败\n\n"; break; } } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值