表ADT(抽象数据类型)

1.抽象数据类型

抽象数据类型是指一系列操作的集合。抽象数据类型是数学上的抽象,在ADT的定义中并没有指出这些操作的具体实现,只给出了操作的功能。当需要使用ADT时,由程序员根据所需要的操作来进行编写,需要进行ADT中的操作时只需要调用已经写好的函数即可。

2.表ADT

一般处理的是形如 A1,A2...An 的表,这个表的大小为n,大小为0的表称为空表。对除空表外的表,称 Ai+1 为 Ai 的后继,称 Ai-1 为 Ai 的前驱,不定义 A1 的前驱,也不定义 An 的后继。表ADT中有:PrintList(打印表中元素)、Insert(向表中插入元素)、Delete(删除表中元素)、Find(找到表中对应元素)、FindKth(找到表中对应位置处的元素)、MakeEmpty(将表置空)等。这些操作可增可删,并且其具体实现应该由程序员自己决定。

3.表的简单数组实现

#include <stdio.h>
#include <stdlib.h>

typedef struct arrList {
	int arr[100];//这个数组也可以使用动态内存的形式
	int num;
}al;//定义结构体,包含一个数组和记录表中元素个数的变量num

al* CreatListHead() {
	al* p = (al*)malloc(sizeof(al));
	p->num = 0;
	return p;
}//创建一个结构体,并对其初始化

int IsEmpty(al* L) {
	if (L->num == 0)
		return 1;
	return 0;
}//判断表是否为空,时间复杂度为常数

void InsertAhead(al* L, int i) {
	al* p = L;
	for (int j = L->num; j > 0; j--) {
		p->arr[j] = p->arr[j - 1];
	}
	p->arr[0] = i;
	p->num++;
}//在表头部插入一个元素,时间复杂度为O(N)

void PrintList(al* L) {
	for (int i = 0; i < L->num; i++) {
		printf("%d ", L->arr[i]);
	}
	printf("\n----------------------------\n");
}//打印表中的元素,时间复杂度为O(N)

void InsertBack(al* p, int i) {
	p->arr[p->num] = i;
	p->num++;
}//在表尾部插入一个元素,时间复杂度为常数

void InsertMid(al* p, int pos, int x) {
	for (int i = p->num; i > pos; i--) {
		p->arr[i] = p->arr[i - 1];
	}
	p->arr[pos] = x;
	p->num++;
}//在表中间插入一个元素,最坏时间复杂度为O(N)

void DeleteList(al* p, int x) {
	for (int i = 0; i < p->num; i++) {
		if (p->arr[i] == x) {
			for (int j = i; j < p->num - 1; j++) {
				p->arr[j] = p->arr[j + 1];
			}
		}
	}
	p->num--;
}//删除表中的一个元素,时间复杂度为O(N)

void MakeEmpty(al* p) {
	free(p);
}//释放表的内存



int main() {
	al* List = CreatListHead();
	if (IsEmpty(List)) {
		printf("The List is empty!\n");
		printf("-------------------\n");
	}

	//从前面插入元素
	for (int i = 9; i >= 5; i--) {
		InsertAhead(List, i);
	}
	PrintList(List);

	//从后面插入元素
	for (int i = 4; i >= 0; i--) {
		InsertBack(List, i);
	}
	PrintList(List);

	//从中间插入元素
	InsertMid(List, 5, 99);
	PrintList(List);

	//删除一个元素
	DeleteList(List, 7);
	PrintList(List);

	//查找某个位置的元素直接利用下标返回就行

	//删除表
	MakeEmpty(List);

	return 0;
}

Find所花费的最坏的时间复杂度为O(N),最好的结果是要查找的元素在第一个,所以平均可能花费O(N/2)的时间,而按位置查找则花费常数时间。如果要用插入的方式来建立一个表,时间复杂度为O(N^2)(从头部插入,尾部应该为O(N))。由于数组实现的表,插入和删除所花费的时间过长,所以一般的表(没有限制)一般不使用数组。

4.表的链表实现

由于数组中的元素是在内存中连续排列的,所以使用数组实现表时,不可避免地要对其中的元素进行移动,这就很明显的增加了不必要的开销。如果使用链表来实现,表中的元素在内存中并不是按照顺序存储的,那么对表进行插入和删除时,就不必再移动表中的元素,而只需要改变指针指向的地址即可。

但使用链表实现表ADT时,如果出现以下情况:

①在表的头部插入一个元素时

②删除表头的元素时

③在一般删除时,需要记住被删除元素之前的元素

①、②都有可能因为疏忽而丢失了表的位置,从而造成错误。

使用表头能够很好的解决上面的问题,添加一个表头(哑节点)来记住表的位置,就可以很大程度上避免一些错误。(表头的地址是不会改变的)

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct Lnode {
	int value;
	struct Lnode* next;
}Lnode;

typedef struct List {
	Lnode* head;
	int numofele;
}List;

List* CreatList() {
	List* ret = (List*)malloc(sizeof(List));
	if (ret == NULL) {
		perror("malloc:");
		return NULL;
	}
	ret->head = (Lnode*)malloc(sizeof(Lnode));
	if (ret->head == NULL) {
		perror("malloc:");
		return NULL;
	}
	ret->head->next = NULL;
	ret->numofele = 0;
	return ret;
}

void DestroyList(List* l) {
	if (l == NULL) {
		printf("表不存在\n");
		return;
	}
	Lnode* tmp = l->head;
	while (tmp) {
		tmp = tmp->next;
		free(l->head);
		l->head = tmp;
	}
	free(l);
	l = NULL;
}

List* ClearList(List* l) {
	if (l == NULL) {
		printf("表不存在\n");
		return NULL;
	}
	l->numofele = 0;
	Lnode* tmp = l->head->next;
	Lnode* tmpp = l->head->next;
	while (tmp) {
		tmp = tmp->next;
		free(tmpp);
		tmpp = tmp;
	}
	l->head->next = NULL;
	return l;
}

bool IsEmpty(List* l) {
	if (l == NULL) {
		printf("表不存在\n");
		return false;
	}
	return l->head->next == NULL;
}

int GetListLength(List* l) {
	if (l == NULL) {
		printf("表不存在\n");
		return -1;
	}
	return l->numofele;
}

int GetElem(List* l, int pos) {//获得表中第pos个元素
	if (l == NULL) {
		printf("表不存在\n");
		return -1;
	}
	if (IsEmpty(l)) {
		printf("表为空\n");
		return -1;
	}
	if (pos < 1) {
		printf("输入位置不合法\n");
		return -1;
	}
	else {
		Lnode* tmp = l->head;
		while (tmp && pos--) {
			tmp = tmp->next;
		}
		if (tmp == NULL) {
			printf("不存在第 %d 个元素\n", pos);
			return -1;
		}
		else {
			return tmp->value;
		}
	}
}

Lnode* GetElemPos(List* l, int k) {
	if (l == NULL) {
		printf("表不存在\n");
		return NULL;
	}
	if (IsEmpty(l)) {
		printf("表为空\n");
		return NULL;
	}
	Lnode* tmp = l->head->next;
	while (tmp) {
		if (tmp->value == k)
			return tmp;
		tmp = tmp->next;
	}
	printf("不存在元素 %d\n", k);
	return NULL;
}

Lnode* GetPre(List* l, int k) {
	if (l == NULL) {
		printf("表不存在\n");
		return NULL;
	}
	Lnode* tmp = l->head;
	while (tmp->next) {
		if (tmp->next->value == k)
			return tmp;
		tmp = tmp->next;
	}
	printf("元素 %d 不存在,所以它在表中没有前驱\n", k);
	return NULL;
}

//在指定结点处后插
void BackInsert(List* l, Lnode* object, int k) {

	Lnode* tmp = (Lnode*)malloc(sizeof(Lnode));
	if (tmp == NULL) {
		perror("malloc");
		return;
	}
	tmp->value = k;
	tmp->next = object->next;
	object->next = tmp;

}

//在指定结点前插
void PreInsert(List* l, Lnode* object, int k) {

	Lnode* tmp = (Lnode*)malloc(sizeof(Lnode));
	if (tmp == NULL) {
		perror("malloc");
		return;
	}
	tmp->next = object->next;
	object->next = tmp;
	tmp->value = object->value;
	object->value = k;

}

//在指定位置的插入
void Insert(List* l, int k, int pos) {
	if (l == NULL) {
		printf("表不存在\n");
		return;
	}
	if (pos < 1 || pos > l->numofele + 1) {
		printf("插入位置不合法\n");
		return;
	}
	else {
		Lnode* tmp = l->head;
		int count = pos - 1;
		while (count--) {
			tmp = tmp->next;
		}
		Lnode* p = (Lnode*)malloc(sizeof(Lnode));
		if (p == NULL) {
			perror("malloc:");
			return;
		}
		p->value = k;
		p->next = tmp->next;
		tmp->next = p;
		l->numofele++;
	}
}

//删除指定结点
int ObjectDelete(List* l, Lnode* object) {

	int ret = object->value;
	if (object->next == NULL) {
		Lnode* tmp = l->head->next;
		while (tmp && tmp->next != object)
			tmp = tmp->next;
		if (tmp) {
			tmp->next = NULL;
			free(object);
			object = NULL;
		}
		else {
			printf("该节点不存在\n");
			return -1;
		}
	}
	else {
		Lnode* tmp = object->next;
		object->next = tmp->next;
		object->value = tmp->value;
		free(tmp);
		tmp = NULL;
	}
	return ret;
}

//删除某个位置的结点
int Delete(List* l, int i) {
	if (l == NULL) {
		printf("表不存在\n");
		return -1;
	}
	if (IsEmpty(l)) {
		printf("表为空,无法删除\n");
		return -1;
	}
	if (i<1 || i>l->numofele) {
		printf("该位置不合法\n");
		return -1;
	}
	Lnode* tmp = l->head;
	i--;
	while (i--) {
		tmp = tmp->next;
	}
	Lnode* de = tmp->next;
	int ret = de->value;
	tmp->next = de->next;
	free(de);
	de = NULL;
	return ret;

}

void Print(List* l) {
	if (l == NULL) {
		printf("表不存在\n");
		return;
	}
	if (IsEmpty(l)) {
		printf("表为空\n");
		return;
	}
	Lnode* tmp = l->head->next;
	while (tmp) {
		printf("%d ", tmp->value);
		tmp = tmp->next;
	}
	printf("\n");
}


int main() {

	List* l = CreatList();
	for (int i = 0; i <= 10; i++) {
		Insert(l, i, i + 1);
	}
	Print(l);

	Insert(l, 123, 6);
	Print(l);

	Delete(l, 12);
	Print(l);


	return 0;
}

5.数组和链表的对比

①数组需要在使用前指定大小,而链表可以动态开辟空间

②数组的元素是在内存中连续存储的,而链表不是

③插入和删除链表的时间复杂度都小于数组

④数组FindKth的时间复杂度为O(1),而链表最坏为O(N)

⑤对于已知的相同数量的元素,链表所占用的空间要大于数组,因为链表中的每个结构体打都需要多存储一个指针,用来指向下一个元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值