一.顺序表
1.顺序表的概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。优点:物理空间连续,下标随机访问。缺点:空间不够,需要扩容。扩容有一定性能的消耗,且存在一些空间浪费;头部或者中间位置插入删除效率低下。
2.静态顺序表:使用定长数组存储元素
//顺序表的静态存储
#defiene N 8
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N];//定长数组
size_t size;//有效数据的个数
}SeqList;
形参的改变不会影响实参
void TestSeqList()
{
SL sl;
SLInit(s1);
}
void SLInit(Sl s)
{
s.a = NULL;
s.size = s.caoacity = 0;
}
结构体sl里面的变量不会改变
3.动态顺序表:使用动态开辟的数组存储
//顺序表的动态存储
typedef struct SeqList
{
SLDataType* array;//指向动态开辟的数组
size_t size;//有效数据的个数
size_t capicity;//容量空间的大小
}SeqList;
3.顺序表的增删查改
0.0初始化
void SLInit(SL* ps) {
ps->a = NULL;
ps->size = ps->capacity = 0;
}
0.1扩容
检查容量空间,满了扩容
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity) {
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SL));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
}
1.头插
//头插
void SLPushFront(SL* ps, SLDataType x)
{
SLCheckCapacity(ps);
//挪动数据
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
2.头删
//头删
void SLPopFront(SL* ps) {
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
assert(ps->size > 0);
}
3.尾删
//尾删
void SLPopBack(SL* ps)
{
//温柔检查
/*if (ps->size == 0)
{
printf("SeqList is empty\n");
return;
}*/
//暴力检查
assert(ps->size > 0);//若ps->size<0直接让程序报错
ps->size--;
}
4.尾插
//尾插
void SLPushBack(SL* ps, SLDataType x) {
SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
5.查找
//查找
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)return i;
}
return -1;
}
6.修改
//修改
void SLModify(SL* ps,int pos, SLDataType x) {
assert(ps);
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
7.打印
void SLPrint(SL* ps)
{
for (int i = 0; i < ps->size; i++)
printf("%d ", ps->a[i]);
printf("\n");
}
8.销毁
void SLDestroy(SL* ps) {
if (ps->a) {
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
}
9.插入数据
//插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos > 0 && pos < ps->size);
SLCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
10.删除数据
//删除数据
void SLErase(SL* ps, int pos) {
assert(ps);
assert(pos >= 0 && pos < ps->size);
/*int begin = pos;
while (begin < ps->size - 1)
{
ps->a[begin] = ps->a[begin + 1];
++begin;
}
ps->size--;
*/
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
}
二.单向链表
1.单向链表的概念及结构
单向链表是一种常见的数据结构,由一系列节点组成,每个节点包含两部分:数据和指向下一个节点的指针。每个节点只有一个指针域,指向其后继节点,而没有指向前驱节点的指针。
2.单向链表的增删改查
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
1.创建结点
//创建结点
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->next = NULL;
newnode->data = x;
return newnode;
}
2.打印
void SListPrint(SLTNode* phead) {
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
3.尾插
//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuySListNode(x);
if (*pphead == NULL)//当要尾插的链表是空的,直接将新节点赋值给头节点
{
*pphead = newnode;
}
else {
SLTNode* tail = *pphead;
while (tail->next != NULL)//找到尾部的关键是尾部的下一个指针域是空的
{
tail = tail->next;
}
tail->next = newnode;
}
}
4.头插
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
5.尾删
//尾删
void SListPopBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
assert(*pphead);
//1.一个结点
//2.多个结点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else {
/*SLTNode* tailPrev = NULL;
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tailPrev = tail;
tail = tail->next;
}
free(tail);
tailPrev->next = NULL;*/
SLTNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
6.头删
//头删
void SListPopFront(SLTNode** pphead) {
assert(pphead);
assert(*pphead != NULL);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
7.查找
//查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)return cur;
cur = cur->next;
}
return NULL;
}
8.在pos位置之前插入
//插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pos);//检查,防止用户传错参数
assert(pphead);
if (pos == *pphead)
{
SListPushFront(*pphead, x);
}
else {
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
9.在pos位置之后插入
//在pos位置之后插入
void SListInsertAfter(SLTNode* pos, SLTDataType x) {
assert(pos);
/*SLTNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;*/
//不在乎链接的顺序
SLTNode* newnode = BuySListNode(x);
SLTNode* next = pos->next;
//post newnode next
pos->next = newnode;
newnode->next = next;
}
10.删除pos位置之前的值
//删除pos位置之前
void SListErase(SLTNode** pphead, SLTNode* pos) {
assert(pphead);
assert(pos);
if (*pphead == pos) {
SListPopFront(pphead);
}
else {
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
11.删除pos位置之后的值
void SListEraseAfter(SLTNode** pphead, SLTNode* pos)
{
assert(pos);
if (pos->next == NULL)return;
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
12.合并两个单向链表
SLTNode* mergeTwoLists(SLTNode* list1, SLTNode* list2) {
SLTNode* l1 = list1;
SLTNode* l2 = list2;
SLTNode* head = NULL;
SLTNode* tail = NULL;
while (l1 && l2)
{
if (l1->data < l2->data)
{
if (tail == NULL)
{
tail = head = l1;
}
else {
tail->next = l1;
tail = tail -> next;
}
l1 = l1->next;
}
else {
if (tail == NULL)
{
tail= head = l2;
}
else {
tail->next = l2;
tail = tail->next;
}
l2 = l2->next;
}
}
if (l1 == NULL)
{
while (l1)
{
head->next = l1;
l1->next;
}
}
else {
while (l2)
{
head->next = l2;
l2->next;
}
}
return head;
}
三.双向链表
1.双向链表的概念及结构
双向链表是一种常见的数据结构,与单向链表不同,每个节点除了包含数据外,还有两个指针域,分别指向前驱节点和后继节点。这使得双向链表具有双向遍历的能力。
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
2.双向链表的增删改查
1.创建结点
//创建结点
LTNode* BuyListNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail");
exit(-1);
}
node->data = x;
node->next = NULL;
node->prev = NULL;
return node;
}
1.1.初始化
LTNode* ListInit()
{
LTNode* phead = BuyListNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
2.打印
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d", cur->data);
cur = cur->next;
}
printf("\n");
}
3.尾插
//尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
4.头插
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* next = phead->next;
/*phead->next = newnode;
newnode->next = next;
newnode->prev = phead;
next->prev = newnode;*/
newnode->next = phead->next;
phead->next->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
}
5.尾删
//尾删
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(!ListEmpty(phead));
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;
}
6.头删
//头删
void SListPopFront(SLTNode** pphead) {
assert(pphead);
assert(*pphead != NULL);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
7.检查链表是否为空
bool ListEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
8.在pos位置之前插入
//在pos位置之前插入x
void ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* newnode = BuyListNode(x);
newnode->next = pos;
pos->prev = newnode;
prev->next = newnode;
newnode->prev = prev;
}
9.删除pos位置的值
//删除pos位置的结点
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* next = pos->next;
LTNode* prev = pos->prev;
next->prev = prev;
prev->next = next;
free(pos);
}
10.销毁链表
void ListDestory(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
int size = 0;
while (cur != phead)
{
LTNode* next = cur->next;
ListErase(cur);
cur = next;
}
free(phead);
}