赞
踩
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种最基本、最简单、也是最常用的数据结构。
常见的线性表:顺序表、链表、栈、队列、字符串等。
线性表在逻辑上是线性结构,即各元素之间是序偶关系。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
下面仅讨论顺序表和链表。
顺序表使用一段物理地址连续的存储单元依次存储数据元素,常使用数组实现。
顺序表一般可以分为静态顺序表和动态顺序表。
静态顺序表只适用于确定知道需要存多少数据的场景,使用起来有较大的局限性。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小。
根据顺序表的定义可知,顺序表结构体应包含一个指向一段连续内存空间的指针,这个空间的大小是动态开辟的,既然如此,也就还需要一个变量保存空间的容量,一个变量保存已使用的空间大小。
于是定义顺序表结构体如下:
typedef struct SeqList {
DataType *arr;
size_t length;
size_t capacity;
}SeqList;
顺序表使用前肯定是要初始化的。除此之外,为满足使用的需求,顺序表还应该具有增、删、查、改等功能。
顺序表的相关操作可定义如下:
//初始化 SeqList* SeqListInit() { SeqList* sl; sl = (SeqList *)malloc(sizeof(SeqList)); sl->capacity = sl->length = 0; return sl; } //扩容 void SeqListExtent(SeqList *sl) { assert(sl); if (sl->capacity == 0) { sl->arr = (DataType *)malloc(sizeof(DataType)); sl->capacity = 1; } else { sl->arr = (DataType *)realloc(sl->arr, 2 * sl->capacity * sizeof(DataType)); sl->capacity = sl->capacity * 2; } } //销毁 void SeqListDestory(SeqList* sl) { assert(sl); free(sl->arr); free(sl); } //判满 int CapacityIsFull(SeqList* sl) { assert(sl); return sl->length == sl->capacity; } //判空 int IsEmptyList(SeqList *sl) { assert(sl); return sl->length == 0; } //尾插 void SeqListPushBack(SeqList* sl, DataType x) { assert(sl); if (CapacityIsFull(sl)) { SeqListExtent(sl); } sl->arr[sl->length] = x; ++sl->length; } //尾删 DataType SeqListPopBack(SeqList* sl) { assert(sl); if (IsEmptyList(sl)) { printf("表已空!\n"); return -1; } --sl->length; return sl->arr[sl->length]; } void SeqListInsert(SeqList* sl, size_t pos, DataType x); //头插 void SeqListPushFront(SeqList* sl, DataType x) { SeqListInsert(sl, 0, x); } void SeqListErase(SeqList* sl, size_t pos); //头删 void SeqListPopFront(SeqList* sl) { SeqListErase(sl, 0); } //查找元素 int SeqListFind(SeqList* sl, DataType x) { assert(sl); size_t cur = 0; while (cur < sl->length) { if (sl->arr[cur] == x) { break; } ++cur; } if (cur == sl->length) { return -1; } return cur; } //指定位置插入元素 void SeqListInsert(SeqList* sl, size_t pos, DataType x) { assert(sl); if (pos < sl->length) { printf("插入位置非法!\n"); return; } ++sl->length; if (CapacityIsFull(sl)) { SeqListExtent(sl); } size_t cur = sl->length - 1; while (cur > pos) { sl->arr[cur] = sl->arr[cur - 1]; --cur; } sl->arr[cur] = x; } //删除指定位置元素 void SeqListErase(SeqList* sl, size_t pos) { assert(sl); if (IsEmptyList(sl)) { printf("表已空!\n"); return; } if (pos < sl->length) { printf("删除位置非法!\n"); return; } for (size_t cur = pos; cur < sl->length - 1; ++cur) { sl->arr[cur] = sl->arr[cur + 1]; } --sl->length; } //移除某元素x void SeqListRemove(SeqList* sl, DataType x) { int pos = SeqListFind(sl, x); if (pos != -1) SeqListErase(sl, pos); else { printf("找不到该元素.\n"); } } //修改指定位置的元素值 void SeqListModify(SeqList* sl, size_t pos, DataType x) { assert(sl); if (pos < sl->length) { printf("修改位置非法!\n"); return; } sl->arr[pos] = x; } //打印顺序表 void SeqListPrint(SeqList* sl) { assert(sl); for (int i = 0; i < sl->length; ++i) { printf("%d ", sl->arr[i]); } printf("\n"); }
链表是一种逻辑上连续,而物理存储结构上非连续、非顺序的存储结构,链表的各节点通过指针链接起来。
链表常见的结构有以下两种:
除了这两种之外还有带头单向非循环链表,下面我们主要实现带头单向非循环链表和带头双向循环链表。
同顺序表的功能类似,链表也应能完成增删查改等功能。
(1) 单链表:
单链表的结构体定义:
typedef int DataType;
typedef struct SingleListNode{
DataType data;
struct SingleListNode *next;
}SingleListNode;
//头结点
typedef struct SingleListHead{
SingleListNode *head;
}SHead;
单链表的相关操作可定义如下:
//初始化头结点 void InitSListHead(SHead* pl) { assert(pl); pl->head = (SingleListNode *)malloc(sizeof(SingleListNode)); pl->head->data = 0; pl->head->next = NULL; } //销毁链表 void SListDestory(SHead* pl) { assert(pl); SingleListNode *cur = pl->head->next; while (cur && cur->next) { SingleListNode *p = cur->next; free(cur); cur = p; } if (cur) { free(cur); } } //创建链表节点 SingleListNode* CreatSListNode(DataType x) { SingleListNode *node; node = (SingleListNode *)malloc(sizeof(SingleListNode)); node->data = x; node->next = NULL; return node; } //查找节点 SingleListNode* FindSListNode(SHead* pl, DataType x) { assert(pl); SingleListNode *cur = pl->head->next; while (cur) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; } //查找某节点的前驱 SingleListNode* FindSListBefore(SHead *pl, SingleListNode *pos) { assert(pl && pos); SingleListNode *pre = pl->head; while (pre) { if (pre->next == pos) { break; } pre = pre->next; } return pre; } // 在pos的后面进行插入 void SListInsertAfter(SingleListNode *pos, DataType x) { assert(pos); SingleListNode *node = CreatSListNode(x); node->next = pos->next; pos->next = node; } // 在pos的前面进行插入 int SListInsertPre(SHead *pl, SingleListNode *pos, DataType x) { assert(pl && pos); SingleListNode *pre = FindSListBefore(pl, pos); if (pre == NULL) { printf("插入失败!节点不存在.\n"); return 0; } SingleListNode *node = CreatSListNode(x); node->data = x; node->next = pos; pre->next = node; return 1; } //移除pos后面的节点 int SListEraseAfter(SingleListNode* pos) { if (pos == NULL) { return 0; } if (pos->next) { SingleListNode *del = pos->next; pos->next = pos->next->next; free(del); return 1; } printf("节点不存在!\n"); return 0; } //头插 void SListPushFront(SHead* pl, DataType x) { SListInsertAfter(pl->head, x); } //头删 void SListPopFront(SHead* pl) { SListEraseAfter(pl->head); } //修改某节点 int AlterSList(SingleListNode *pos, DataType x) { if (pos == NULL) { printf("修改失败! 节点不存在.\n"); return 0; } pos->data = x; return 1; } //删除某节点 int SListRemove(SHead* pl, DataType x) { assert(pl); SingleListNode *cur = FindSListNode(pl, x); if (cur == NULL) { printf("删除失败! 节点不存在.\n"); return 0; } SingleListNode *pre = FindSListBefore(pl, cur); pre->next = cur->next; free(cur); return 1; } //打印链表 void SListPrint(SHead* pl) { assert(pl); SingleListNode *node = pl->head->next; while (node) { printf("%d ", node->data); node = node->next; } printf("\n"); }
(2)双向链表:
双向链表的结构体定义如下:
typedef int DataType;
typedef struct CircularListNode{
DataType data;
struct CircularListNode *next;
struct CircularListNode *pre;
}CListNode;
typedef struct CircularListHead{
struct CircularListNode *head;
}CListHead;
双向链表的相关操作如下:
//初始化 void InitCListHead(CListHead* pl) { assert(pl); pl->head = (CListNode *)malloc(sizeof(CListNode)); pl->head->data = 0; pl->head->next = pl->head; pl->head->pre = pl->head; } //销毁链表 void CListDestory(CListHead* pl) { assert(pl); if (pl->head->next == pl->head) { printf("链表已空\n"); return; } CListNode *cur = pl->head->next; CListNode *later = pl->head->pre; while (cur != later) { cur = cur->next; free(cur->pre); } free(cur); } //查找节点 CListNode* CListFind(CListHead *pl, DataType x) { assert(pl); CListNode *cur = pl->head->next; while (cur != pl->head) { if (cur->data == x) { return cur; } cur = cur->next; } printf("节点不存在!\n"); return NULL; } // 在pos的前面进行插入 void CListInsert(CListNode *pos, DataType x) { assert(pos); CListNode *node; node = (CListNode *)malloc(sizeof(CListNode)); node->data = x; CListNode *pre = pos->pre; pos->pre = node; node->next = pos; node->pre = pre; pre->next = node; } // 删除pos位置的节点 void CListErase(CListNode *pos) { assert(pos); if (pos->next == pos) { printf("链表已空!\n"); return; } pos->next->pre = pos->pre; pos->pre->next = pos->next; free(pos); } //移除某元素x void CListRemove(CListHead* pl, DataType x) { CListNode *cur = CListFind(pl, x); if (cur == NULL) { printf("删除失败!\n"); return; } CListErase(cur); } //打印链表 void CListPrint(CListHead* pl) { CListNode *cur = pl->head->next; while (cur != pl->head) { printf("%d ", cur->data); cur = cur->next; } printf("\n"); } //尾插 void CListPushBack(CListHead* pl, DataType x) { CListInsert(pl->head, x); } void CListPopBack(CListHead* pl) { CListErase(pl->head->pre); } //头插 void CListPushFront(CListHead* pl, DataType x) { CListInsert(pl->head->next, x); } //头删 void CListPopFront(CListHead* pl) { CListErase(pl->head->next); }
注意,在上述操作中,我并没有对链表的头结点进行删除或修改的操作,因为我定义的链表是带头结点的链表,头结点的作用就是作为该链表的一个标志,因此一旦定义并初始化了一个链表,这个链表的头结点就不会被释放直至程序运行结束。
当然,你也可以定义为带头指针的链表,这样就可以对头结点进行操作了,但是同样的,你不能对头指针进行删除或修改的操作。
(1) 顺序表
优势:
地址空间连续,支持随机访问。
不足:
(2) 链表:
优势:
插入删除操作灵活,扩容时不会出现空间的浪费,也不用担心连续的内存空间不足导致增容失败。
不足:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。