数据结构——线性表

线性表——顺序表的定义及其上的操作
线性表——链表的定义及其上的操作

线性结构

线性结构是一个数据元素的有序集

线性结构的特点

在数据元素的非空有限集中:

  1. 集合中必存在唯一的一个第一元素
  2. 集合中必存在唯一的一个最后元素
  3. 除最后元素之外,均有唯一的后继
  4. 除第一元素之外,均有唯一的前驱

线性表是一种最简单的线性结构

线性表

线性表的类型定义

定义:一个线性表是n个数据元素的有限序列
例如:英文字母表

特征:

  • 元素个数n——表长度 n=0时为空表
  • i = 2,3,……,n时,ai的直接前驱是ai-1,ai无直接前驱
  • i = 1,2,3,……,n-1时,ai的直接后继是ai+1,an无直接后继
  • 元素同构,同一个线性表中的数据元素具有相同的特性

线性表上的基本操作

数据结构的运算是定义在逻辑结构层次上的,而运算的具体实现是建立在存储结构上的。下面定义的线性表基本运算仅是逻辑结构层面,具体实现在线性表确定了存储结构之后。

  1. 线性表初始化
InitList( &L )
初始条件:表L不存在 
操作结果:构造一个空的线性表L
  1. 销毁一个线性表
DestroyList( &L ) 
初始条件:线性表L 已存在。 
操作结果:销毁线性表L。
  1. 判断一个线性表是否为空表
ListEmpty( L ) 
初始条件:线性表L已存在。 
操作结果:若L为空表,则返回TRUE,否则 FALSE。
  1. 求线性表的长度
ListLength( L ) 
初始条件:线性表L已存在。 
操作结果:返回L中所含元素个数。
  1. 求数据元素的前驱
PriorElem( L, cur_e, &pre_e )  
初始条件:线性表L已存在。 
操作结果:若cur_e是L的元素,但 不是第一个,则用pre_e 返回它的 前驱,否则操作失败,pre_e无定义。
  1. 求数据元素的后继
NextElem( L, cur_e, &next_e ) 
初始条件:线性表L已存在。 
操作结果:若cur_e是L的元素, 但不是最后一个,则用next_e 返回它的后继,否则操作失败, next_e无定义。
  1. 求线性表中某个数据元素
GetElem( L, i, &e ) 
初始条件:线性表L已存在, 且1≤i≤ListLength(L)。 
操作结果:用e 返回L中第i个元素的值。
  1. 定位函数
★LocateElem( L, e, compare( ) ) 
初始条件:线性表L已存在,e为给定值,compare( )是元素判定函数。 
操作结果:返回L中第1个与e满足关系 compare( )的元素的位序。若这样的元素 不存在,则返回值为0。
  1. 遍历线性表
ListTraverse(L, visit( )) 
初始条件:线性表L已存在,Visit() 为某个访问函数。 
操作结果:依次对L的每个元素调用 函数visit( )。一旦visit( )失败,则操 作失败。
  1. 线性表置空
ClearList( &L ) 
初始条件:线性表L已存在。 
操作结果:将L重置为空表
  1. 插入数据元素
ListInsert( &L, i, e ) 
初始条件:线性表L已存在,插入位置正 确(1≤i≤ListLength(L)+1 )。 
操作结果:在L的第i个元素之前插入新 的元素e,这样使原序号为i , i+1, ... , n 的数据元素的序号变为i+1,i+2, ... , n+1,插入后表长=原表长+1。
  1. 删除数据元素
ListDelete(&L, i, &e) 

初始条件:线性表L已存在且非空, 1≤i≤ListLength(L) 
操作结果:删除L的第i个元素,并用 e返回其值,删除后使序号为i+1, i+2,..., n 的元素变为序号为i, i+1,...,n-1,新表长=原表长-1。

线性表的顺序表示和实现

线性表顺序表示

定义:用一组地址连续的存储单元存放一个线性表
在这里插入图片描述
特点:
实现了逻辑上的相邻——物理地址相邻
实现随机存取
实现线性表——可以用一维数组实现

线性表实现

  • 线性表的顺序结构定义
#define  LIST_INIT_SIZE  100 // 线性表存储空间的初始分配量 
#define  LISTINCREMENT 10 // 线性表存储空间的分配增量 

typedef  struct { 
	ElemType  *elem; // 存储空间基址 
	int       length; // 当前长度 
	int		  listsize;  // 当前分配的存储容量 
						// (以sizeof(ElemType)为单位) 
}SqList; // 俗称顺序表
  • 线性表基本操作在顺序表中的实现

初始化线性表:

Status InitList_Sq( SqList&L ){ 
	// 构造一个空的线性表 
	L.elem = (ElemType*) malloc (LIST_ INIT_SIZE* sizeof (ElemType)); 
	//或L.elem=new ElemType[LIST_INIT_SIZE]; 
	if(!L.elem) exit(OVERFLOW); 
	L.length = 0; 
	L.listsize = LIST_INIT_SIZE 
	return OK; 
}// InitList_Sq  

销毁线性表:

voidDestroyList(SqList&L) { 		
	if(L.elem)
		delete[] L.elem;//释放存储空间 
}

清空线性表:

void ClearList(SqList &L) { 
	L.length=0;     //将线性表的长度置为0 
}

求线性表L的长度:

int GetLength(SqList L) { 
	return (L.length);             
} 

判断线性表L是否为空:

int IsEmpty(SqList L) { 
	if (L.length==0) 
		return 1;      
	else 
		return 0; 
}

取值(根据位置i获取相应位置数据元素的内容):

Status GetElem(SqListL,int i,ElemType& e) { 
	if(i<1||i>L.length)
		return ERROR; //判断i值是否合理,若不合理,返回ERROR 
	e=L.elem[i-1];//第i-1的单元存储着第i个数据 		
	return OK; 
}

查找元素在线性表里是否存在:

int LocateElem_Sq(SqList L, ElemType e, Status(*compare)(ElemType, ElemType)) { 
	//在顺序表中查询第一个满足判定条件的数据元素
	//若存在,则返回它的位序,否则返回0
	i = 1;   //i 的初值为第1 元素的位序 
	p = L.elem; //p 的初值为第1 元素的存储位置 
	while(i <= L.length && !(*compare)(*p++, e))
		++i; 
	if(i <= L.length)  
		return i; 
	else  
		return 0;
}// LocateElem_Sq

插入元素:

Status ListInsert_Sq(sqlist &L,int i,ElemType e){ 
	if (i<1||i>L.length+1)  
		return  ERROR; 
	if (L.length>=L.listsize){ 
		newbase =(ElemType*)realloc(L.elem, (L.listsize+LISTINCREMENT)*sizeof(ElemType)); 
		if (!newbase) 
			exit (overflow); 
		L.elem =newbase; 
		L.listsize+=LISTINCREMENT; 
	} 
	q=&(L.elem[i-1]); // q指示插入位置 
	for  (p=&(L.elem[L.length-1]);p>=q;--p)   
		*(p+1)=*p; 
					// 插入位置及之后的元素右移

	*q=e; // 插入e 
	++L.length; // 表长增1 
	return OK; 
}

删除元素:

Status  listdelete_sq(sqlist &L,int i,ElemType &e){ 
	if ((i<1)||(i>L.length)) 
		return ERROR; 
	p=&(L.elem[i-1]); // p 为被删除元素的位置 
	e=*p; // 被删除元素的值赋给e 
	q=L.elem+L.length-1; // 表尾元素的位置 
	for (++p;p<=q;++p)  
		*(p-1)=*p;// 被删除元素之后的元素左移 
	--L.length; // 表长减1 
	return OK; 
}

顺序存储结构的优缺点

优点:
逻辑相邻 物理相邻
可随机存取任一元素
存储空间使用紧凑

缺点:
插入、删除操作需要移动大量的元素
预先分配的空间按最大空间分配 利用不充分
表容量难以扩充

线性表的链式表示和实现

用一组任意的存储单元存储线性表的数据元素
利用指针实现了永不相邻的存储单元存放逻辑上相邻的元素
每个数据元素除了存储本身的信息外还需要存储其直接后继的信息

结点:
数据域:元素本身信息
指针域:指示直接后继的存储位置
在这里插入图片描述

线性链表的定义(单链表)

定义:结点中只含一个指针域的链表加线性单链表

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

生成一个LNode型新结点:

p=(Linklist)malloc(sizeof(LNode));或p=new LNode; 
系统回收p结点:free(p);或delete p;

单链表的操作

初始化(构造一个空表):

Status InitList_L(LinkList &L){ 
	L=new LNode;                    
	L->next=NULL; 
	return OK; 
}

销毁:

Status DestroyList_L(LinkList &L){ 
	LinkList p; 
	while(L) { 
		p=L;  
		L=L->next; 
		delete p;  
	} 
	return OK; 
}

清空:

Status ClearList(LinkList &L){ 
	// 将L重置为空表 
	LinkList p,q; 
	p=L->next;   //p指向第一个结点 
	while(p)       //没到表尾 
	{  
		q=p->next; 
		delete p;     
		p=q;   
	} 
	L->next=NULL;   //头结点指针域为空 
	return OK; 
}

求表长:

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

判断表是否为空:

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

查找(根据指定数据获取数据所在的位置):

LNode *LocateELem_L (LinkList L,Elemtype e) {
	 //返回L中值为e的数据元素的地址,查找失败返回NULL 	
	 p=L->next; 
	 while(p &&p->data!=e)  
	 	p=p->next;                
	 return p; 
}

int LocateELem_L (LinkList L,Elemtype e) { 
	//返回L中值为e的数据元素的位置序号,查找失败返回0 	
	p=L->next; 
	j=1; 
	while(p &&p->data!=e)  
	{
		p=p->next;  
		j++;
	}          
	if(p) 
		return j; 
	else 
		return 0; 
}

查找第i 个数据元素:

Status GetElem_L(LinkList L, int i, ElemType &e) { 
	// L是带头结点的链表的头指针,以e 返回第i 个元素
	p = L->next;   
	j = 1;  // p指向第一个结点,j为计数器 
	while (p && j<i)  
	{ 
		p = p->next;  
		++j;  
	} //顺指针向后查找,直到p 指向第i 个元素 //或p 为空 	
	if( !p || j>i ) 
		return ERROR;      //  第i 个元素不存在 
	e = p->data;          //  取得第i 个元素 
	return OK
}// GetElem_L


插入元素:

Status ListInsert_L(LinkList &L, int i, ElemType e) { 
	// L 为带头结点的单链表的头指针 
	// 在链表中第i个结点之前插入新的元素e
	p = L;    
	j = 0; 
	while (p && j < i-1) { 
		p = p->next;  
		++j; 
	}   // 注意表达技巧 
	if (!p || j > i-1) 
		return ERROR;      // 两种情况的具体所指 
	s = (LinkList) malloc ( sizeof (LNode)); 
	s->data = e; 
	s->next = p->next;      
	p->next = s; // 插入 
	return OK
}// LinstInsert_L


删除元素:

Status ListDelete_L(LinkList &L, int i, ElemType &e) { 
	// 删除以L 为头指针(带头结点)的单链表中第i 个结点
	p = L;    
	j = 0; 
	while (p->next && j < i-1) 
	{  
		p = p->next;   
		++j; 
	} // 寻找第i 个结点,并令p 指向其前趋 
	if  (!(p->next) || j > i-1) 
		return ERROR;  
		// 删除位置不合理 
	q = p->next;   
	p->next = q->next;  // 删除并释放结点 
	e = q->data;   
	free(q); 
	return OK;
} // ListDelete_L

动态建立单链表算法

逆位序输入n个数据元素的值,建立带头节点的单链表。

操作步骤:

  1. 建立一个空表
  2. 输入数据元素an,建立结点并插入
  3. 输入数据元素an-1,建立结点并插入
  4. 以此类推,直到输入a1为止
void CreateList_L(LinkList &L, int n) { 
	// 逆序输入n 个数据元素,建立带头结点的单链表
	L = (LinkList)malloc(sizeof (LNode)); 
	L->next = NULL;// 先建立一个带头结点的单链表 
	for(i = n; i > 0; --i) 
	{ 
		p = (LinkList)malloc(sizeof (LNode)); 	
		scanf(&p->data);    // 输入元素值 
		p->next = L->next; 
		L->next = p;  // 插入 
	}
}// CreateList_L 

单链表特点

它是一种动态的结构,整个存储空间为多个链表共用
不需要预先分配空间
指针占用额外的存储空间
不能随机存取 查找速度慢

重新定义带头结点的单链表类型

typedef struct LNode {  // 结点类型 
	ElemType  data; 
	struct LNode   *next; 
}*Link, *Position;

typedef struct{  // 链表类型 
	Link  head, tail;// 分别指向头结点和 
				//最后一个结点的指针 
	int   len;	// 指示链表长度 
}LinkList;

循环链表

循环链表是表中最后一个结点的指针指向头结点,使链表构成环状

特点:
从表中任一节点出发均可找到表中其他节点,提高查找效率

操作与单链表基本一致 循环条件不同:

  • 单链表p 或 p->next == NULL
  • 循环链表p 或 p->next == H

双向链表

单链表具有单向性的缺点

双向链表结点定义:
在这里插入图片描述

typedef structDuLNode { 
	ElemType   data;// 数据域 
	struct DuLNode *prior; // 指向前驱的指针域 
	struct DuLNode *next; //指向后继的指针域 
}DuLNode, *DuLinkList; 

双向循环链表

特点:

  1. 查询与单链表相同
  2. 插入和删除时需要同时修改两个方向上的指针

插入操作:

Status ListInsert_Dul(DuLinkList &L,int i,ElemType e){ 
	//在带头结点的双向循环链表L中第i个位置之前插入元素e, 		
	//i的合法值为1≤i≤表长+1 
	if (!(p=GetElemP_Dul(L,i)))     //在L中确定插入位置指针P 
		return ERROR;  //i等于表长+1时,p指向头结点,i大于表长+1时,p=NULL 
	if (!(s=(DuLinkList)malloc(sizeof(DuLNode)))) 
		return ERROR; 
	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){ 
	//删除带头结点的双向循环链表L的第i个元素
	//i的合法值为1≤i≤表长 
	if (!(p=GetElemP_Dul(L,i))) 
	//在L中确定第i个元素的位置指针 
		return ERROR;           
	//p =NULL,即第i个元素不存在 
	e=p->data; 
	p->prior->next=p->next; 
	p->next->prior=p->prior; 
	free(p); 
	return OK; 
}

在这里插入图片描述

线性表的应用——一元多项式的表示及相加

一元多项式
在这里插入图片描述
在计算机中,可以用一个线性表来表示:

P = (p0, p1, …,pn) 

但是对于形如 S(x) = 1 + 3x10000–2x20000 的多项式,上述表示方法比较浪费空间

在这里插入图片描述

单链表的结点定义

typedef   struct Lnode{ 
	int coef,exp; 
	struct Lnode  *next; 
}Lnode,*LinkList;

一元多项式相加

在这里插入图片描述

在这里插入图片描述

void  add_poly(LinkList &pa,LinkList  pb) {
	//一元多项式的表示及相加 
	LinkList  p,q,u,pre; 
	int x; 
	p=pa->next; 
	q=pb->next; 
	pre=pa;//pre总是指向待进入链表的节点的前驱 
	while((p!=NULL) && ((q!=NULL)) {//结束条件 
		if(p->exp<q->exp){//p指数小于q指数 
			pre=p;  
			p=p->next; 
		} 
		else if(p->exp==q->exp){//相等 
			x=p->coef+q->coef; //系数相加 
			if(x!=0){
				p->coef=x;  
				pre=p;
			} //系数非0
	
			else {//为0 
				pre->next=p->next;  
				free(p); 
			} // 删除q 
			p=pre->next; 
			u=q; 
			q=q->next; 
			free(u); 
		} 
		else{//p指数大于q指数 
			u=q->next;
			q->next=p;
			pre->next=q; 
			pre=q;  
			q=u; 
		} 
		if(q!=NULL) 
			pre->next=q;//第二条链表未结束
		 	free(pb); 
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值