一.线性表
线性表是n个具有相同特性的数据元素的有限序列。线性表是一种广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串......
线性表在逻辑上是线性结构,但是在物理结构上并不一定是连续的,线性表在物理存储时,通常以数组和链式结构的形式存储。
二.顺序表
1.顺序表的概念及结构
顺序表时用一段物理地址连续存储的存储单元依次存储数据元素的线性结构,一般采用数组存储,在数组上完成数据的增删查改。
顺序表可分为静态顺序表(使用定长数组存储)和动态顺序表(使用动态开辟的数组存储)
// 顺序表的静态存储
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N]; // 定长数组
size_t size; // 有效数据的个数
}SeqList;
// 顺序表的动态存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
2.接口实现
静态顺序表只适用于确定知道数据容量的场景。一般都使用动态顺序表,根据需要分配空间大小。
#include<stdio.h>
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
// 基本增删查改接口
void SeqListInit(SeqList* psl, size_t capacity);
void SeqListDestory(SeqList* psl);
void CheckCapacity(SeqList* psl);
void SeqListPushBack(SeqList* psl, SLDataType x);
void SeqListPopBack(SeqList* psl);
void SeqListPushFront(SeqList* psl, SLDataType x);
void SeqListPopFront(SeqList* psl);
int SeqListFind(SeqList* psl, SLDataType x);
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
void SeqListErase(SeqList* psl, size_t pos);
void SeqListRemove(SeqList* psl, SLDataType x);
void SeqListModify(SeqList* psl, size_t pos, SLDataType x);
void SeqListPrint(SeqList* psl);
三.链表
1.链表的概念及结构
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
链表有这几种结构:单向、双向、带头,不带头,循环、非循环。
常用的是:
(1)无头单向非循环链表
无头单向非循环链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
(2)带头双向循环链表
带头双向循环链表结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单。
2.链表的实现
//无头单向非循环链表
SListNode* BuySListNode(SLTDataType x)//申请一个新节点
{
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
newNode->data = x;
newNode->next = NULL;
return newNode;
}
void SListPrint(SListNode* pList)//打印
{
SListNode* cur = pList;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
void SListPushBack(SListNode** ppList, SLTDataType x)//尾插
{
SListNode* newNode = BuySListNode(x);
if (*ppList == NULL)
{
*ppList = newNode;
}
else
{
SListNode* cur = *ppList;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = newNode;
}
}
void SListPushFront(SListNode** ppList, SLTDataType x)//头插
{
SListNode* newNode = BuySListNode(x);
newNode->next = *ppList;
*ppList = newNode;
}
void SListPopBack(SListNode** ppList)//尾删
{
//1.链表为空
if (*ppList == NULL)
{
return;
}
//2.链表只有一个节点
else if ((*ppList)->next==NULL)
{
free(*ppList);
*ppList = NULL;
}
//3.链表有多个节点
else
{
SListNode* cur = *ppList;
SListNode* prev = NULL;
while (cur->next != NULL)
{
prev = cur;
cur = cur->next;
}
free(cur);
prev->next = NULL;
}
}
void SListPopFront(SListNode** ppList)//头删
{
SListNode* node = (*ppList)->next;
free(*ppList);
*ppList = node;
}
SListNode* SListFind(SListNode* pList, SLTDataType x)//查找
{
SListNode* cur = pList;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur -> next;
}
}
return NULL;
}
void SListInsertAfter(SListNode* pos, SLTDataType x)//在当前位置后插入
{
/*SListNode* newnode = BuySListNode(x);
SListNode* next = pos->next;
pos->next = newnode;
newnode->next = next;*/
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListEraseAfter(SListNode* pos)//在当前位置后删除
{
SListNode* next = pos->next;
if (next != NULL)
{
pos->next = next->next;
free(next);
}
}
void SListDestory(SListNode** ppList)//销毁
{
SListNode* cur = *ppList;
while (cur)
{
SListNode* next = cur->next;
free(cur);
cur = next;
}
*ppList = NULL;
}
//带头双向循环链表
ListNode* ListCreate()//创建返回链表的头节点
{
ListNode* head = (ListNode*)malloc(sizeof(ListNode));
head->next = head;
head->prev = head;
return head;
}
void ListDestory(ListNode* phead)//销毁
{
ListNode* cur = phead->next;
while (cur != phead)
{
ListNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
void ListPrint(ListNode* phead)//打印
{
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
void ListPushBack(ListNode* phead, LTDataType x)//尾插
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
ListNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
void ListPushFront(ListNode* phead, LTDataType x)//头插
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
ListNode* first = phead->next;
newnode->data = x;
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
void ListPopBack(ListNode* phead)//尾删
{
ListNode* tail = phead->prev;
ListNode* prev = tail->prev;
free(tail);
prev->next = phead;
phead->prev = prev;
}
void ListPopFront(ListNode* phead)//头删
{
assert(phead->next != phead);
ListNode* node = phead->next;
ListNode* next = node->next;
free(node);
phead->next = next;
next->prev = phead;
}
ListNode* ListFind(ListNode* phead, LTDataType x)//查找
{
ListNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
void ListInsert(ListNode* pos, LTDataType x)//在pos位置前面插入
{
ListNode* prev = pos->prev;
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
newnode->data = x;
newnode->prev = prev;
prev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
}
void ListErase(ListNode* pos)//删除pos位置的节点
{
ListNode* prev = pos->prev;
ListNode* next = pos->next;
free(pos);
prev->next = next;
next->prev = prev;
}
四.顺序表和链表的区别
(1)顺序表:
优点:空间连续,支持随机访问;
缺点:中间或前面部分的插入删除时间复杂度O(N)。增容的代价比较大。
(2)链表:
优点:任意位置插入删除时间复杂度为O(1)。没有增容问题,插入一个开辟一个空间。
缺点:以节点为单位存储,不支持随机访问。