一.链表的基本概念:
链表是一种存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的;
二.链表的几种形态:
链表的结构非常多,组合起来有8中结构:
1.单向,双向链表;
2.带头,不带头链表;
3.循环,不循环链表;
在实际中最常用的链表结构有两种:
无头单向不循环链表:
带头双向循环链表:
三.链表的特点:
1.任意位置插入删除的时间复杂度为O(1),没有增容,插一个开辟一个空间;以节点为单位存储,不2.支持随机访问;
3.链表的优点:更适合长度不固定,并且频繁增删的场景
(1)O(1)时间复杂度 插入/删除;
(2)相对来说,空间更高效一点;
四.无头单向不循环链表的基本操作:
Slist.h文件中的代码:
typedef int SlistDataType;
//设置链表中的一个节点;
typedef struct Node{
SlistDataType value; //表示该节点的值;
struct Node *next; //表示该节点的下一个节点;
} Node;
//设置一个链表的结构体;
typedef struct Slist{
Node* first; //表示结构体中的第一个节点;
}Slist;
1.链表头插节点:
(1)先申请一块内存空间Node*node;(2)将V赋给新的节点node;(3)更改链表的关系;
void SlistPushFront(Slist *slist, SlistDataType num){
Node *node=(Node*)malloc(sizeof(Node));
node->value=num;
node->next=slist->first;
slist->first=num;
}
2.链表的尾插节点:
(1)先申请一块内存空间Node* node;(2)判断链表的第一个节点是否为空,如果为空则进行头插;如果不为空则判断链表的最后一个不为空的节点,然后在该节点的下一个结点进行插入;
void SlistPushBack(Slist* slist,SlistDataType num){
Node* node=(Node*)malloc(sizeof(Node));
node->value=num;
if(slist->first==NULL){
slist->first=node;
}
else{
node*c=slist->first;
while(c->next!=NULL){
c=c->next;
}
c->next=node;
}
3.链表的头删节点:
(1)判断链表是否为空;
(2)释放头节点的空间,将第二个节点的变为第一个节点;
void SlistPopFront(Slist *slist){
assert(slist->first!=NULL);
Node* second=slist->first->next;
free(slist->first);
slist->first=second;
}
4.链表的尾删结点;
(1)先申请一块内存空间Node *node;
(2)判断链表的第一个节点是不是为空;如果为空则说明申请的该节点就是链表的第一个节点;如果不为空,找原来链表的最后一个节点,然后将申请的节点放在NULL的前一个节点;
void SlistPopBack(Slist *slist,SLDataType num){
Node* node=(Node *)malloc(sizeof(Node));
node->value=num;
if(slist->first==NULL){
slist->first=node;
}
else{
Node* c=slist->first;
while(c->next!=NULL){
c=c->next;
}
c->next=node;
}
5.在链表中找到某个节点的值num;
在链表中进行遍历查找;
void SlistFind(Slist *slist,SlistDataType num){
for(Node* c=slist->first;c!=NULL;c=c->next){
if(c->value==num){
return c;
}
}
return NULL;
}
6.在一个节点的后面做插入;
(1)申请一块内存作为需要插入的结点的地址 Node * node;
(2)将V放到申请的节点中;
(3)将指定节点的下一个节点与申请到的节点的位置进行替换;
void SlistAfter(Slist*slist,Node* post,SLDataType num){
Node* node=(Node*)malloc(sizeof(Node));
node->value=num;
node->next=post;
post->next=node;
}
7.删除链表中的某个指定节点:
(1)先判断链表的第一个节点是否为空;如果为空,则直接返回return ;
(2)如果链表中的第一个数为要删除的数,则先记录原来第二个节点,释放第一个节点的空间,把第二个节点放升级为第一;
(3)如果链表中的第一数不是要删除的数,则在链表不为NULL的前提下,进行查找;该节点的下一个节点找到了,就把该结点的下下一个节点给下一个节点,释放下一个节点;
void SlistRemove(Slist *slist,SLDataType num){
if(slist->first==NULL){
return 0;
}
if(slist->first->value==num){ //当链表的第一个节点为要删除的节点时;
Node *second=slist->first->next;
free(slist->first);
slist->first=second;
}
else{
Node* c=slist->first;
while(c->next!=NULL){
if(c->value==num){
Node* next=c->next;
c->next=c->next->next;
free(c->next);
}
c=c->next;
}
}
}
8.删除节点中的所有指定的数:
(1)判断链表的第一个结点是否为NULL;
(2)判断链表的第一个结点是否是要删除的数;
(3)在链表不为空的条件下,进行转换;
void SlistRemoveAll (Slist * slist ,SlistDataType num){
if (slist->first==NULL){
return;
}
if (slist->first->value==NULL){
Node * second=slist->first->next;
free (slist->first);
slist->first=second;
}
Node * c=slist->first;
while (c->next !=NULL){
if (c->next->value== num){
Node * next=c->next;
c-next=c->next->next;
free (next);
}
else {
c=c->next;
}
}
}
五.双向带头循环链表基本操作:
//设置链表的结构体:
typedef struct Node {
int value;
struct Node *next;
struct Node *prev; //表示循环后的值
}Node;
//链表的初始化:
void DListInit(Node**p){
Node* node = (Node *)malloc(sizeof(Node));
//node->value没有用
//当只有一个结点的时候;相当于结点的下一个结点还是结点本身,结点循环之后还是结点本身;
node->next = node;
node->prev = node;
*p = node;
}
//结点头插:4个指针要改;
void DListPushFront(Node *head, Node * node){
node->next = head->next;
node->prev = head;
node->next->prev = node;
head->next = node;
}
//结点尾插:
void DListPushback(Node * head, Node * node){
node->next = head;
node->prev = head->prev;
head->prev->next = node;
head->prev = node;
}
//在pos 的后面插入;
void DListInsertAfter(Node *pos, Node * node){
node->next = pos->next;
node->prev = pos;
pos->next->prev = node;
pos->next = node;
}
//在pos的前面进行插入;
void DListInsertBefore(Node *pos, Node* node){
node->next = pos;
node->prev = pos->prev;
pos->prev->next = node;
pos->prev = node;
}