相对于顺序表这种存放这连续内存地址的而言,每次都需要扩容
然而扩容又有很多缺点:
1、如果扩容扩少了,就需要多次扩容
2、如果扩容扩多了,那又会造成空间浪费
这个时候我们就要想到链表,因为链表和顺序表不一样
链表是由一个个不同的空间组成的,他们在物理结构上是不连续的,但是在逻辑结构上是连续的
由于单项无头结点不循环链表最难,所以我们从这个开始讲起
单链表
typedef struct SLNode
{
typedef peard;
typedef* SLNodenext;
}
结构体内存放的是数据的内容,还有一个指向地址的指针
单链表的图像如下:
这个nextcode会一直指向下一个,直到找到NULL为止停下,如此就完成了链表的构建
链表和顺序表一样,也是有头插尾插,头删尾删的功能,如下代码如此实现:
List.c
#include"SList.h"
void SLTPrint(SLNode* phead)
{
SLNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL\n");
}
SLNode* CreateNode(SLNDataType x)
{
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
return newnode;
}
void SLTPushBack(SLNode** pphead, SLNDataType x)
{
SLNode* newnode = CreateNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
// 找尾
SLNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
SLNode* newnode = CreateNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPopBack(SLNode** pphead)
{
// 温柔的检查
//if (*pphead == NULL)
// return;
// 空
assert(*pphead);
// 1、一个节点
// 2、一个以上节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
// 找尾
/*SLNode* prev = NULL;
SLNode* tail = *pphead;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;*/
SLNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SLTPopFront(SLNode** pphead)
{
// 空
assert(*pphead);
一个以上节点 + 一个
//SLNode* tmp = (*pphead)->next;
//free(*pphead);
//*pphead = tmp;
// 一个以上节点 + 一个
SLNode* tmp = *pphead;
free(tmp);
*pphead = (*pphead)->next;
}
List.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLNDataType;
// Single List
typedef struct SListNode
{
SLNDataType val;
struct SListNode* next;
}SLNode;
void SLTPrint(SLNode* phead);
void SLTPushBack(SLNode** pphead, SLNDataType x);
void SLTPushFront(SLNode** pphead, SLNDataType x);
void SLTPopBack(SLNode** pphead);
void SLTPopFront(SLNode** pphead);
SLNode* SLTFind(SLNode* phead, SLNDataType x);
// posǰ
SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);
// ɾposλ
void SLTErase(SLNode** pphead, SLNode* pos);
void SLTDestroy(SLNode** pphead);
Test.c
#include"SList.h"
void TestSLT1()
{
SLNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
//SLTPopBack(&plist);
//SLTPrint(plist);
}
void TestSLT2()
{
SLNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
//SLNode* pos = SLTFind(plist, 3);
//SLTInsert(&plist, pos, 30);
}
//int main()
//{
// TestSLT2();
//
// return 0;
//}
struct ListNode
{
struct ListNode* next;
int val;
};
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* prev = NULL;
struct ListNode* cur = head;
//while(cur != NULL)
while (cur)
{
if (cur->val == val)
{
struct ListNode* next = cur->next;
free(cur);
if (prev)
prev->next = next;
cur = next;
}
else
{
prev = cur;
cur = cur->next;
}
}
return head;
}
int main()
{
struct ListNode* n1 = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n2 = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n3 = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* n4 = (struct ListNode*)malloc(sizeof(struct ListNode));
n1->val = 7;
n2->val = 7;
n3->val = 7;
n4->val = 7;
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = NULL;
struct ListNode* head = removeElements(n1, 7);
return 0;
}
//void Swap(int* p1, int* p2)
//{
// int tmp = *p1;
// *p1 = *p2;
// *p2 = tmp;
//}
//
改变的是int,传的是int*
//int main()
//{
// int a = 0, b = 1;
// Swap(&a, &b);
//
// return 0;
//}
//void Swap(int** p1, int** p2)
//{
// int tmp = *p1;
// *p1 = *p2;
// *p2 = tmp;
//}
//
改变的是int*,传的是int**
//int main()
//{
// int* px = NULL, * py = 0x01;
// Swap(&px, &py);
//
// return 0;
//}
掌握了单链表之后,再来学习其他链表得心应手多了
双向带头循环链表
接下来介绍的是双向带头循环链表,因为除了单链表之外,生活中最常见的就是这种链表了
当然,接下来就是如何实现链表了
List.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType val;
}LTNode;
LTNode* LTInit();
void LTPrint(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
List.c
#include"List.h"
LTNode* CreateLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
LTNode* LTInit()
{
LTNode* phead = CreateLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
void LTPrint(LTNode* phead)
{
assert(phead);
printf("ڱλ");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<=>", cur->val);
cur = cur->next;
}
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = CreateLTNode(x);
// phead tail newnode
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
void LTPopBack(LTNode* phead)
{
assert(phead);
//
assert(phead->next != phead);
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;
}
Test.c
#include "List.h"
int main()
{
LTNode* plist = LTInit();
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 5);
LTPushBack(plist, 4);
LTPrint(plist);
return 0;
}
其实掌握了这两种链表,在生活中就足以解决大部分的链表问题了