数据结构|线性表|严蔚敏C语言版第二章

线性表

  1. 定义: 一个线性表是n个数据元素有限序列。
    除第一个元素之外,每个元素有且仅有一个直接前驱;除最后一个元素以外,每个元素有且仅有一个直接后继。

  2. 性质:
    个数有限;具有逻辑上的顺序性,表中元素有先后次序;都是数据元素,每个元素都是单个元素;数据类型都相同,每个元素占有相同大小的存储空间;具有抽象性,仅讨论元素间的逻辑关系,而不考虑元素究竟表示什么内容

线性表的顺序表示和实现

  1. 顺序表的定义

用一组地址连续的存储单位依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。

静态分配:
大小和空间都实现固定!!

#define MaxSize 10
typedef struct{
	Elemtype data[MaxSize];
	int length;
}SqList;

动态分配:
存储数据的空间在程序执行的过程中通过动态存储语句分配的,一旦数据空间占满,就另外开辟一块更大的存储空间。

#define InitSize 100
typedef struct{
	ElemType *data;
	int MaxSize,length;
}SeqList;

初始化:

Status InitList_Sq(SqList &L){
	L.elem=(ElemType*)malloc(sizeof(ElemType)*InitSize);
	L.length=0;
	return OK;
}

特点:
1.随机访问,通过首地址和元素序号可在时间 O ( 1 ) O(1) O(1)内找到指定的元素。
2.存储密度高,每个结点只存储数据元素。
3.逻辑上相邻的元素物理上也相邻,插入和删除操作需要移动大量的元素。
4.拓展不方便

  1. 顺序表的基本操作

插入数据元素:
1 ≤ i ≤ L i s t L e n g t h _ S q + 1 1 \leq i \leq ListLength\_Sq+1 1iListLength_Sq+1

Status ListInsert_Sq(SqList &L,int i,ElemType e){
	//在顺序线性表L中第i个位置之前插入新的元素e
	if(i<1||i>L.length+1) //判断i的范围是否有效
		return false;
	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]);
	for(p=L.elem[L.length-1];p>=q;--p)
		L.data[p]=L.data[p-1];
	L.data[p-1]=e;
	L.length++;
	return true;
}

最好情况:在表尾插入 ( i = n + 1 ) (i=n+1) (i=n+1),元素后移语句将不执行,时间复杂度为 O ( 1 ) O(1) O(1)
最坏情况:在表头插入 ( i = 1 ) (i=1) (i=1),元素后移语句将执行n次,时间复杂度为 O ( n ) O(n) O(n)
平均情况:时间复杂度为 O ( n ) O(n) O(n)

删除数据元素:
删除第 i ( 1 ≤ i ≤ n ) i(1\leq i \leq n) i1in个元素时,需将从第 i + 1 i+1 i+1个元素至第 n n n个元素依次向前移动一个位置。

Status ListDelete_Sq(SqList &L, int i,ElemType &e){
	//在顺序线性表L中删除第i个元素,并用e返回其值
	if(i<1||i>L.length)
		return false;
	e=L.data[i-1];
	for(int j=i;j<L.length;j++)
		L.data[j-1]=L.data[j];
	L.length--;
	return true;
}

最好情况:在删除表尾 ( i = n ) (i=n) (i=n),无元素移动,时间复杂度为 O ( 1 ) O(1) O(1)
最坏情况:在删除表头 ( i = 1 ) (i=1) (i=1),元素移动语句执行 n − 1 n-1 n1次,时间复杂度为 O ( n ) O(n) O(n)
平均情况:时间复杂度为 O ( n ) O(n) O(n)

查找数据元素:(顺序查找)
在顺序表 L L L中查找第一个元素值等于e的元素,并返回其位序。

int LocateElem(SqList L,ElemType e){
	int i;
	for(i=0;i<L.length;i++){
		if(L.data[i]==e)
			return i+1;  //下标为i的元素值等于e,返回其位序i+1
	}
	return 0;  //退出循环,说明查找失败
}

最好情况:查找的元素在表头,仅此比较一次,时间复杂度为 O ( 1 ) O(1) O(1)
最坏情况:查找的元素在表尾(或不存在时),需要比较n次,时间复杂度为 O ( n ) O(n) O(n)
平均情况:时间复杂度为 O ( n ) O(n) O(n)

线性表的链式表示和实现

单链表

  1. 定义
    指通过一组任意的存储单元来 存储线性表中的 数据元素。
//线性表的单链表存储结构
typedef struct LNode{   //定义单链表结点类型
	ElemType data;   //数据域
	struct LNode *next;   //指针域
}LNode,*LinkList;

上述的表达等同于:

typedef struct LNode{
	ElemType data;
	struct LNode *next;
}
typedef struct LNode LNode;
typedef struct LNode *LinkList;

返回带有头结点的单链表第 i i i个值的:

Status GetElem(LinkList L,int i,ElemType &e){
	//L为带有头结点的单链表的头指针
	//当第i个存在时,其值赋给e并返回OK,否则返回ERROR
	p=L->next;j=1; //初始化,p为第一个结点,j为计数器
	while(p&&j<i){  //顺时针查找,直到p指向第i个元素,或者为空
		p=p->next;++j;
	}
	if(!p||j>i) return ERROR;
	e=p->data;
	return OK;
}
头结点:

定义:在单链表第一个节点之前附加一个节点,称为头结点
引入后的优点:
1)第一个数据结点的位置被存放在头结点的指针域中,因此在链表中所有系欸但的操作都是一致的,无需进行特殊处理。
2)无论链表是否空,其头指针都指向头结点的非空指针,因此空表和非空表的处理也是一致的。

  1. 基本操作
初始化一个单链表(带头结点)
bool InitList(LinkList &L){
	L=(LNode*)malloc(sizeof(LNode));  //分配头结点
	if(L==NULL)   //内存不足,分配失败
		return false;
	L->next=NULL;  //头结点之后,暂时还没有节点
	return true;
}

判断是否为空:

bool Empty(LinkList L){
	if(L->next==NULL)
		return true;
	else
		return false;
}
初始化一个单链表(不带头结点)
bool InitList(LinkList &L){
	L=NULL;  //空表,暂时还没有节点(防止脏数据)
	return true;
}

判断是否为空:

bool Empty(LinkList L){
	if(L==NULL)
		return true;
	else
		return false;
}

bool Empty(LinkList L){
	return(L==NULL);
}
插入结点(带头结点)
Status ListInsert(LinkList &L,int i,ElemType e){
	//带头结点的单链线性表L中第i个位置之前插入元素e
	p=L;j=0;
	while(p&&j<i-1){
		p=p->next;
		++j;
	}  //寻找第i-1个结点
	if(!p||j>i-1)  //i小于1或者大于表长加1
		return ERROR;
	s=(LinkList)malloc(sizeof(LNode));  //生成新节点
	s->data=e;s->next=p->next; p->next=s; //插入L中
	return OK;
}
删除结点(带头结点)
Status ListDelete(LinkList &L,int i,ElemType &e){
	//带头结点的单链线性表L中,删除第i个元素,并由e返回其值
	p=L;j=0;
	while(p->next&&j<i-1){ //寻找第i个结点,并令p指向其前驱
		p=p->next;
		++j;
	}  
	if(!(p->next)||j>i-1)  //删除位置不合适
		return ERROR;
	q=p->next;p->next=q->next; //删除并释放结点
	e=q->data;free(q);
	return OK;
}

复杂度为 O ( n ) O(n) O(n),因为必须找到第 i − 1 i-1 i1个结点,在删除或插入第 i i i个结点之前。

头插法建立单链表
void CreateListHead(LinkList &L,int n){
	//逆序位输入n个元素的值,建立带表头结点的单链线性表L
	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; //插入到表头	
	}
}
尾插法建立单链表
void CreateListTail(LinkList &L){
	//正向建立单链线性表
	L=(LinkList)malloc(sizeof(LNode));
	LNode *s,*r=L; //r为表尾指针
	scanf(L->data); //输入结点的值
	while((L->data)!=9999){
		s=(LNode*)malloc(sizeof(LNode));   //生成新的结点
		s->data=L->data;r->next=s;
		r=s;     //r指向新的表尾结点
		scanf(L->data); //输入结点的值
	}
	r->next=NULL;   //尾结点指针置空
	return L;
}
求表长

定义:计算单链表中数据结点(不含头结点)的个数。每访问一个结点,计数器加1,直到访问到空结点为止。

双链表

结点定义:

typedef struct DNode{ //定义双链表的结点类型
	ElemType data;
	struct DNode *prior,*next; 
}DNode,*DLinkList;

bool InitDLinkList(DLinkList &L){
	L=(DNode*)malloc(sizeof(DNode));  //分配一个头结点
	if(L==NULL)   //内存不足,分配失败
		return false;
	L->prior=NULL;
	L->next=NULL;
	return true;
}
双链表的插入
//在p结点后插入s结点
bool InsertNextDNode(DNode *p,DNode *s){
	if(p==NULL||s==NULL)
		return false;
	s->next=p->next;  //1
	if(p->next!=NULL){   //如果p结点有后继结点
		p->next->prior=p;  //2
	}
	s->prior=p;  //3
	p->next=s; //4
	return true;
}

第1、2步必须在第4步之前!!!

双链表的删除
//删除p结点的后继结点
bool DeleteNextDNode(DNode *p){
	if(p==NULL)
		return false;
	DNode *q=p->next; //找到p的后继结点q
	if(q==NULL)
		return false;  //p没有后继结点
	p->next=q->next;
	if(q->next!=NULL) //q结点不是最后一个结点
		q->next->prior=p;
	free(q);	
	return true;
}

void DestoryList(DLinklist &L){
	//循环释放各个数据结点
	while(L->next!=NULL)
		DeleteNextDNode(L);
	free(L);  //释放头结点
	L=NULL;  //头指针指向NULL
}
双链表的遍历
//后向遍历
while(p!=NULL){
	p=p->next;
}
//前向遍历
while(p!=NULL){
	p=p->prior;
}
//前向遍历(跳过头结点)
while(p->prior!=NULL){
	p=p->prior;
}

循环链表

循环单链表

判断是否是空的条件:头结点的指针是否等于头指针。

循环单链表通常仅设尾指针,从而使得效率更高。因为设r是尾指针,那么 r − > n e x t r->next r>next即为头指针,对表头和表尾都仅需要 O ( 1 ) O(1) O1的时间复杂度。

循环双链表
//初始为空的循环双链表
bool InitDLinkList(Dlinklist &L){
	L=(DnOde *)malloc(sizeof(DNode));
	if(L==NULL)
		return false;
	L->prior=p;
	L->next=p;
	return true;
}

//判断循环双链表是否为空
bool Empty(DLinlist L){
	if(L->next==L)
		return true;
	else 
		return flase;
}

//判断结点p是否为循环双链表的表尾结点
bool isTail(DLinkList L,DNode *p){
	if(p->next==L)
		return true;
	else
		return false;
}

静态链表

定义:借助数组来描述线性表的链式存储结构。指针域是节点的呃相对位置(数组下标),又称游标。

#define MaxSize 50
typedef struct{
	Elemtype data;
	int next;
}SLinkList[MaxSize];

当next=-1,作为结束的标志。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值