顺序表和链表都是线性表的一种,还有队列和栈,以及字符串等等.
一.顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般用数组来存储.
1.顺序表的分类:
(1)静态顺序表(定长的)
#define N 8
typedef int ElemType
typedef struct SeqList {
ElemType arr[N];//定长数组
size_t size;//数组长度
}SeqList;
(2)动态顺序表(容量不够自动扩容的)
typedef struct SeqList {
ElemType* base;//动态数组
size_t capacity;
size_t size;
}SeqList;
2.顺序表功能实现可以看我之前写的一篇博客.
顺序表的增删改查等操作以及动态扩容_Exy-的博客-CSDN博客
二.链表
链表是一种物理存储结构上非连续,非顺序的存储结构 数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
1.链表的分类
(1)单向和双向
(2)循环非循环
(3)带头结点不带头结点
2.我们常见的一般是
(1)无头单向非循环链表
代码实现各种基础操作:
ListNode* _Buynode(ElemType v)
{
ListNode *s = (ListNode*)malloc(sizeof(ListNode));
assert(s != NULL);
s->data = v;
s->next = NULL;
return s;
}
void ListInit(List *plist)
{
*plist = NULL;
}
//尾插
void ListPushBack(List *plist, ElemType v)
{
//申请节点
ListNode *s = _Buynode(v);
//插入节点
ListNode *p = *plist;
if(p == NULL)
{
*plist = s;
return;
}
while(p->next != NULL)
p = p->next;
p->next = s;
}
//头插
void ListPushFront(List *plist, ElemType v)
{
ListNode *s = _Buynode(v);
s->next = *plist;
*plist = s;
}
//按值插入
void ListInsertByVal(List *plist, ElemType v)
{
ListNode *p = NULL;
ListNode *s = _Buynode(v);
if(*plist == NULL) //空链表
{
*plist = s;
return;
}
if(v < (*plist)->data)
{
s->next = *plist;
*plist = s;
return;
}
p = *plist;
while(p->next!=NULL && v>p->next->data)
p = p->next;
s->next = p->next;
p->next = s;
}
//尾删
void ListPopBack(List *plist)
{
ListNode *p = *plist;
ListNode *prev = NULL;
if(*plist == NULL)
return;
while(p->next != NULL)
{
prev = p;
p = p->next;
}
if(prev == NULL)
*plist = NULL;
else
prev->next = NULL;
free(p);
}
//头删
void ListPopFront(List *plist)
{
ListNode *p = *plist;
if(*plist == NULL)
return;
*plist = p->next;
free(p);
}
//打印
void ListShow(List plist)
{
ListNode *p = plist;
while(p != NULL)
{
printf("%d->", p->data);
p = p->next;
}
printf("Over.\n");
}
size_t ListLength(List plist)
{
ListNode *p = plist;
size_t len = 0;
while(p != NULL)
{
len++;
p = p->next;
}
return len;
}
//按值查找
ListNode* ListFind(List plist, ElemType key)
{
ListNode *p = plist;
while(p!=NULL && p->data!=key)
p = p->next;
return p;
}
//删除结点
void ListErase(List *plist, ElemType key)
{
ListNode *p = *plist;
ListNode *prev = NULL;
if(p == NULL)
return;
while(p!=NULL && p->data!=key)
{
prev = p;
p = p->next;
}
if(p == NULL)
return;
if(prev == NULL)
*plist = p->next;
else
prev->next = p->next;
free(p);
}
//链表清空
void ListClear(List *plist)
{
ListNode *p = NULL;
while(*plist != NULL)
{
p = *plist;
*plist = p->next;
free(p);
}
}
//链表销毁
void ListDestroy(List *plist)
{
ListClear(plist);
}
//链表排序
void ListSort(List *plist)
{
ListNode *p;
if(*plist==NULL || (*plist)->next==NULL)
return;
p = (*plist)->next;
(*plist)->next = NULL; // 断开链表
while(p != NULL)
{
ListNode *q = p->next;
if(p->data < (*plist)->data)
{
p->next = *plist;
*plist = p;
}
else
{
ListNode *prev = *plist;
while(prev->next!=NULL && p->data>prev->next->data)
prev = prev->next;
p->next = prev->next;
prev->next = p;
}
p = q;
}
}
//不带头结点的链表逆置
void ListReverse(List *plist)
{
ListNode *p = NULL;
if(*plist==NULL || (*plist)->next==NULL)
return;
p = (*plist)->next;
(*plist)->next = NULL; // 断开链表
while(p != NULL)
{
ListNode *q = p->next;
//头插节点
p->next = *plist;
*plist = p;
p = q;
}
}
(2)带头双向循环链表
DCListNode* _Buynode(ElemType v)//申请结点
{
DCListNode *s = (DCListNode*)malloc(sizeof(DCListNode));
assert(s != NULL);
s->data = v;
s->next = s->prev = s;
return s;
}
void DCListInit(DCList *plist)
{
plist->head = _Buynode(-1);//头节点为空
}
//尾插
void DCListPushBack(DCList *plist, ElemType v)
{
DCListNode *s = _Buynode(v);
s->next = plist->head;
s->prev = plist->head->prev;
s->next->prev = s;
s->prev->next = s;
}
//头插
void DCListPushFront(DCList *plist, ElemType v)
{
DCListNode *s = _Buynode(v);
s->next = plist->head->next;
s->prev = plist->head;
s->next->prev = s;
s->prev->next = s;
}
//尾删
void DCListPopBack(DCList *plist)
{
DCListNode *p;
if(plist->head->next == plist->head)
return;
//查找最后一个节点
p = plist->head->prev;
//删除节点
p->next->prev = p->prev;
p->prev->next = p->next;
free(p);
}
//头删
void DCListPopFront(DCList *plist)
{
DCListNode *p = plist->head->next;
if(plist->head->next == plist->head)
return;
p->prev->next = p->next;
p->next->prev = p->prev;
free(p);
}
//按值插入
void DCListInsertByVal(DCList *plist, ElemType v)
{
DCListNode *p = plist->head->next;
while(p!=plist->head && v>p->data)
p = p->next;
DCListNode *s = _Buynode(v);
s->next = p;
s->prev = p->prev;
s->next->prev = s;
s->prev->next = s;
}
//按值寻找
DCListNode* DCListFind(DCList *plist, ElemType key)
{
DCListNode *p = plist->head->next;
while(p!=plist->head && key!=p->data)
p = p->next;
if(p == plist->head)
return NULL;
return p;
}
//按值删除
void DCListErase(DCList *plist, ElemType key)
{
DCListNode *p = DCListFind(plist, key);
if(p == NULL)
return;
p->next->prev = p->prev;
p->prev->next = p->next;
free(p);
}
//打印链表
void DCListShow(DCList *plist)
{
DCListNode *p = plist->head->next;
while(p != plist->head)
{
printf("%d->", p->data);
p = p->next;
}
printf("Over.\n");
}
size_t DCListLength(DCList *plist)
{
DCListNode *p = plist->head->next;
size_t len = 0;
while(p != plist->head)
{
len++;
p = p->next;
}
return len;
}
//排序
void DCListSort(DCList *plist)
{
DCListNode *p, *q;
size_t size = DCListLength(plist);
if(size <= 1)
return;
p = plist->head->next;
q = p->next;
p->next = plist->head;
plist->head->prev = p;
while(q != plist->head)
{
p = q;
q = q->next;
//寻找插入的位置
DCListNode *pos = plist->head->next;
while(pos!=plist->head && p->data>pos->data)
pos = pos->next;
//插入节点
p->next = pos;
p->prev = pos->prev;
p->next->prev = p;
p->prev->next = p;
}
}
//逆置
void DCListReverse(DCList *plist)
{
DCListNode *p, *q;
size_t size = DCListLength(plist);
if(size <= 1)
return;
p = plist->head->next;
q = p->next;
//断开链表
p->next = plist->head;
plist->head->prev = p;
while(q != plist->head)
{
p = q;
q = q->next;
p->next = plist->head->next;
p->prev = plist->head;
p->next->prev = p;
p->prev->next = p;
}
}
//清空
void DCListClear(DCList *plist)
{
DCListNode *p = plist->head->next;
while(p != plist->head)
{
p->prev->next = p->next;
p->next->prev = p->prev;
free(p);
p = plist->head->next;
}
}
//销毁
void DCListDestroy(DCList *plist)
{
DCListClear(plist);
free(plist->head);
plist->head = NULL;
}
注意:因为带头结点的销毁需要free头结点,而不带头节点不需要.
当然还有很多链表结构比如双向非循环链表或者单向循环链表,这里就不一一实现了.
三.顺序表和链表的比较
1.如果是访问元素的操作,元素的插入、删除和移动操作极少;
适合使用顺序表。这是因为,顺序表中存储的元素可以使用数组下标直接访问,无需遍历整个表,因此使用顺序表访问元素的时间复杂度为 O(1)
;而在链表中访问数据元素,需要从表头依次遍历,直到找到指定节点,花费的时间复杂度为 O(n)
;
2.如果是涉及元素的插入、删除和移动,访问元素的需求很少;适合使用链表。链表中数据元素之间的逻辑关系靠的是节点之间的指针,当需要在链表中某处插入或删除节点时,只需改变相应节点的指针指向即可,无需大量移动元素,因此链表中插入、删除或移动数据所耗费的时间复杂度为 O(1)
;而顺序表中,插入、删除和移动数据可能会牵涉到大量元素的整体移动,因此时间复杂度至少为 O(n)
;
不同点 | 顺序表 | 链表 |
存储空间 | 物理上一定连续 | 逻辑上连续,物理上不一定 |
随机访问 | 支持O(1) | 不支持O(N) |
任意位置插入或者删除元素 | 可能要移除元素,效率低 | 只需要修改指针指向 |
插入 | 动态顺序表,容量不够需要扩容 | 没有容量的概念 |
应用场景 | 元素高效存储,频繁访问 | 任意位置插入和删除频繁 |
缓存利用率 | 高 | 低 |