数据结构——线性表

一、ADT

具有相同数据类型的n个数据元素的有限序列。除第一个元素之外每个元素都有唯一的直接前驱,除最后一个元素之外都有唯一的直接后继。

L(a1,a2,...,an)

操作:

InitList(&L)

Length(L)

LocateElem(L,e)

GetElem(L,i)

ListInsert(&L,i,e)

ListDelete(&L,i,&e)

PrintList(L)

Empty(L)

DestoryList(&L)

二、顺序存储

特点:逻辑顺序和物理顺序相同,支持随机访问,存储密度高

优点:

  • 无须为表示表中元素之间的逻辑关系而增加额外的存储空间
  • 可以快速地存取表中任一位置的元素

缺点:

  • 插入和删除操作需要移动大量元素
  • 当线性表长度变化较大时,难以确定存储空间的容量
  • 造成存储空间的“碎片”
#define Maxsize 100

typedef struct {
	int data[Maxsize];	//ElemType data[Maxsize];
	int length;
}SqList;

void InitList(SqList *&L) {
	L = (SqList *)malloc(sizeof(SqList));
	//L = new SqList();
	L->length = 0;
}
int ListLength(SqList *L) {
	return L->length;
}
bool ListInsert(SqList *&L, int i, int e) {
	if (i<0 || i>L->length || L->length>=Maxsize) return false;
	for (int j = L->length; j > i; j--) {
		L->data[j] = L->data[j - 1];
	}
	L->data[i] = e;
	L->length++;
	return true;
}
bool ListDelete(SqList *&L, int i, int &e) {
	if (i<0 || i>L->length - 1) return false;
	e = L->data[i];
	for (int j = i + 1; j < L->length; j++) {
		L->data[j - 1] = L->data[j];
	}
	L->length--;
	return true;
}
int GetElem(SqList *L, int i) {
	if (i<0 || i>L->length - 1) return false;
	return L->data[i];
}
int LocateElem(SqList *L, int e) {
	for (int i = 0; i < L->length; i++) {
		if (L->data[i] == e) {
			return i;
		}
	}
	return -1;
}
void PrintList(SqList *L) {
	for (int i = 0; i < L->length;i++) {
		printf("%d ", L->data[i]);
	}
	printf("\n");
}
bool Empty(SqList *L) {
	if (L->length == 0) return true;
	return false;
}
void DestroyList(SqList *&L) {
	free(L);
}

三、链式存储

对于每个节点通过增加指针来表示数据元素之间的关系。

特点:

  • 解决了顺序表需要大量连续存储单元的缺点,但单链表附加指针域,也存在浪费存储空间的缺点。
  • 非随机存取

1.单链表

增加一个后继指针

typedef struct LNode {
	int data;	//ElemType data;
	struct LNode *next;
}LNode, *LinkList;
void InitList(LinkList &L, int data) {
	L = new LNode();
	L->data = data;
	L->next = nullptr;
}
int ListLength(LinkList L) {
	int i = 0;
	while (L->next != nullptr) {
		i++;
		L = L->next;
	}
	return i;
}
LNode* GetElem(LinkList L, int i) {
	if (i < 0) return NULL;
	LNode *p = L;
	while (p != nullptr && i > 0) {
		p = p->next;
		i--;
	}
	if (i > 0) {
		return NULL;
	}
	return p;
}
bool ListInsert(LinkList L, int i, int e) {
	LNode *p = GetElem(L, i);
	if (p == NULL) return false;
	LNode *q;
	InitList(q, e);
	q->next = p->next;
	p->next = q;
	return true;
}
bool ListDelete(LinkList L, int i, LNode *&e) {
	if (i <= 0) return false;
	LNode *p = GetElem(L, i - 1);
	if (p == NULL) return false;
	e = p->next;
	p->next = e->next;
}
LNode* LocateElem(LinkList L, int e) {
	LNode *p = L->next;
	while (p != NULL&&p->data!=e) {
		p = p->next;
	}
	return p;
}
void PrintList(LinkList L) {
	LNode *p = L->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
void DestroyList(LinkList &L) {
	LNode* p = L->next;
	LNode* q = p->next;
	while (q != NULL) {
		delete(p);
		p = q;
		q = q->next;
	}
	delete(p);
	delete(L);
}

2.双链表

在单链表的基础上增加了一个指向其前驱的prior指针

typedef struct LNode {
	int data;	//ElemType data;
	struct LNode *prior;
	struct LNode *next;
}LNode, *LinkList;
void InitList(LinkList &L, int data) {
	L = new LNode();
	L->data = data;
	L->prior = nullptr;
	L->next = nullptr;
}
int ListLength(LinkList L) {
	int i = 0;
	while (L->next != nullptr) {
		i++;
		L = L->next;
	}
	return i;
}
LNode* GetElem(LinkList L, int i) {
	if (i < 0) return NULL;
	LNode *p = L;
	while (p != nullptr && i > 0) {
		p = p->next;
		i--;
	}
	if (i > 0) {
		return NULL;
	}
	return p;
}
bool ListInsert(LinkList L, int i, int e) {
	LNode *p = GetElem(L, i);
	if (p == NULL) return false;
	LNode *q;
	InitList(q, e);
	if (p->next != NULL) {
		p->next->prior = q;
		q->next = p->next;
		p->next = q;
		q->prior = p;
	}
	else {
		q->prior = p;
		p->next = q;
	}
	return true;
}
bool ListDelete(LinkList L, int i, LNode *&e) {
	if (i <= 0) return false;
	LNode *p = GetElem(L, i - 1);
	if (p == NULL) return false;
	e = p->next;
	if (e->next != NULL) {
		e->next->prior = p;
	}
	p->next = e->next;
}
LNode* LocateElem(LinkList L, int e) {
	LNode *p = L->next;
	while (p != NULL&&p->data!=e) {
		p = p->next;
	}
	return p;
}
void PrintList(LinkList L) {
	LNode *p = L->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
void DestroyList(LinkList &L) {
	LNode* p = L->next;
	LNode* q = p->next;
	while (q != NULL) {
		delete(p);
		p = q;
		q = q->next;
	}
	delete(p);
	delete(L);
}

3.循环链表

4.循环双链表

5.静态链表

#define Maxsize 50
typedef struct{
    ElemType data;
    int next;
} SLinkList[Maxsize];

静态链表依然属于链表,不能随机存取

四、顺序表和链表的比较

 1.存取方式

顺序表可以顺序存取,也可以随机存取,链表只能从表头顺序存取元素

2.逻辑结构和物理结构

采用顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻,而采用链式存储时,逻辑上相邻的元素,物理存储位置不一定相邻

3.查找,插入,删除操作

对于按值查找,顺序表无序时,两者的时间复杂度均为O(n);顺序表有序时,可采用折半查找,此时的时间复杂度为O(log2 n)

对于按序号查找,顺序表O(1),链表O(n)

插入删除,顺序表要移动半个表长的元素.链表只需修改指针域即可。链表的时间复杂度也为O(n),但花销在寻找节点上

4.空间分配

顺序表需要预先分配足够大的存储空间,动态存储分配实际上是数据的大量移动,并非是静态的增加空间,依旧需要连续的足够大的存储空间

  • realloc:如果有足够空间用于扩大mem_address指向的内存块,则分配额外内存,并返回mem_address 
  • realloc是从堆上分配内存的,当扩大一块内存空间时, realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平。也就是说,如果原先的内存大小后面还有足够的空闲空间用来分配,加上原来的空间大小= newsize。那么就ok。得到的是一块连续的内存。 
  • 如果原先的内存大小后面没有足够的空闲空间用来分配,那么从堆中另外找一块newsize大小的内存。并把原来大小内存空间中的内容复制到newsize中。返回新的mem_address指针。(数据被移动了)。 

链表只在需要时申请分配,只要内存有空间就可以分配

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值