单链表的实现
1.链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链
接次序实现的 。
结构:
注意:
- 从图中可以看出来,链式结构在逻辑上是连续的,但是在物理上不一定连续。
- 链表中的节点一般都是在堆上申请出来的。
2.链表的分类
单项或者双向链表:
带头或者不带头:
循环或者非循环:
3.单链表定义
typedef int listDataType;
//定义节点类型
typedef struct listNode
{
listDataType data;
struct listNode* next;
}listNode;
typedef listNode* list;
4.接口的实现
//函数定义
listNode* buyNode(listDataType val);//动态申请一个链表的节点
void listInit(list* ls);//链表的初始化
void listPushBack(list* ls, listDataType val);//尾插一个数据
void listPopBack(list* ls);//尾删一个数据
void listPushFront(list* ls, listDataType val);//头插一个数据
void listPopFront(list* ls);//头删一个数据
void listInsertAfter(listNode* cur, listDataType val);//在pos位置之后插入一个数据
void listEraserAfter(listNode* cur);//在pos位置之后删除一个数据
listNode* listFind(list ls, listDataType val);//按值查找
void listInsert(list* ls, listDataType val);//按值插入
void listErase(list* ls, listDataType val);//按值删除
void listSort(list* ls);//链表排序
void listReverse(list* ls);//链表逆置
void printfList(list ls);//打印链表
void listDestory(list* ls);//摧毁链表
动态申请一个链表的节点
- 动态申请内存,创建新的节点。
- 将新节点的数据存放到data中。
- 节点的next指针置空。
listNode* buyNode(listDataType val)
{
listNode* newNode = (listNode*)malloc(sizeof(listNode));
assert(newNode);
newNode->data = val;
newNOde->next = NULL;
return newNode;
}
链表的初始化
- 指向第一个节点的指针置空
void listInit(list* ls)
{
*ls = NULL;
}
尾插一个数据
- 申请一个新的节点。
- 判断是否是第一个元素,如果是,则头结点的指针指向新的节点。
- 如果不是第一个元素,则找到最后一个元素后,在最后一个个元素的后面插入新的节点。
void listPushBack(list* ls, listDataType val)
{
listNode* newNode = buyNode(val);
if(*ls == NULL)
{
*ls = newNOde;
return;
}
listNode* p = *ls;
while(p->next != NULL)
{
p=p->nxt;
}
p->next = newNode;
}
尾删一个数据
- 判断链表是否有数据,如果不是空链表,则开始进行删除。
- 创建两个节点类型变量tail和prev,tail初始值等于第一个节点,prev等于空,分别向后每次移动一个位置,保存的是最后一个元素和倒数第二个元素。
- 如果prev为NULL,说明链表只有一个元素,则删除第一个元素。
- 如果prev不等于NULL,prev的next为空,并释放tail的空间。
void listPopBack(list* ls)
{
if(*ls == NULL)
{
return;
}
listNode* tail = *ls;
listNode* prev = NULL;
while(tail->next != NULL)
{
prev = tail;
tail =t ail->next;
}
//删除的是第一个元素
if(prev == NULL)
{
*ls = NULL;
}
//不只有一个元素
else
{
prev->next = NULL;
}
free(tail);
}
头插一个数据
- 创建新的节点。
- 新节点的next等于链表的第一个节点。
- 第一个节点的指针指向新的节点
void listPushFront(list* ls, listDataType val)
{
listNode* node = buyNode(val);
node->next = *ls;
*ls = node;
}
头删一个数据
- 判断链表是否为空。
- 创建一个节点变量p 保存第一个节点的位置。
- 把指向第一个节点的指针指向p的next。
- 释放p的空间。
void listPopFront(list* ls)
{
listNode* p = *ls;
if (*ls == NULL)
{
return;
}
*ls = p->next;
free(p);
}
在pos位置之后插入一个数据
- 创建一个新的节点。
- pos的next等于新的节点
- 新节点的next等于pos的下一个节点。
void listInsertAfter(listNode* cur, listDataType val)
{
listNode* node = buyNode(val);
node->next = cur->next;
cur->next = node;
}
在pos位置之后删除一个数据
- 判断链表是否为空 。
- 创建一个节点类型变量next保存pos位置的下一个位置。
- pos的下一个节点等于next节点的下一个节点。
- 释放next的空间。
void listEraserAfter(listNode* cur)
{
listNode* next = cur->next;
if (next == NULL)
{
return;
}
cur->next = next->next;
free(next);
}
按值查找
- 遍历整个链表,找到与val值相等的节点并返回。
listNode* listFind(list ls, listDataType val)
{
if (ls == NULL )
return NULL;
//从第一个节点开始遍历
listNode* cur = ls;
while (cur!=NULL)
{
if (cur->data == val)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
按值插入
- 创建新的节点。
- 如果是空链表,则指向第一个节点的指针指向新的节点。
- 不是空链表的情况下,如果要插入的数据比第一个节点的数据还小,进行头插。
- 在其他位置进行插入。
void listInsert(list* ls, listDataType val)
{
listNode* newNode = buyNode(val);
//空链表
if(*ls == NULL)
{
*ls = newNode;
return;
}
//在第一个位置进行插入
if(val < (*ls)->data)
{
newNode->next = *ls;
*ls = newNode;
return;
}
//在其他位置进行插入
listNode* p = *ls;
while(p->next != NULL && p->next->data < val)
{
p=p->next;
}
newNode->next = p->next;
p->next = newNode;
}
按值删除
- 判断链表是否有数据,如果不是空链表,则开始进行删除。
- 创建两个节点类型变量tail和prev,tail初始值等于第一个节点,prev等于空,分别向后每次移动一个位置,直到找到val的节点和他前一个节点。
- 如果prev为NULL,说明删除的是第一个元素。
- 如果prev不等于NULL,prev的next指向tail的next,并释放tail的空间。
void ListErase(List *plist, ElemType key)
{
ListNode *tail = *plist;
ListNode *prev = NULL;
if(tail == NULL)
{
return;
}
while(tail != NULL && tail->data != val)
{
prev = tail ;
tail = tail->next;
}
if(prev == NULL)
{
*ls = NULL;
}
else
{
prev->next = tail->next;
}
free(tail);
}
链表排序
- 如果链表为空或者只有一个数据,则不进行排序。
- 断开链表
- 将断开后的链表依次进行按值插入。
void listSort(list* ls)
{
if (*ls == NULL || (*ls)->next == NULL)
{
return;
}
listNode* p = NULL;
//断开链表
p = (*ls)->next;
*ls)->next = NULL;
//按值插入
while(p != NULL)
{
listNode* q = p->next;
//比第一个位置的值还小
if(p->data < (*ls)->data)
{
p->next = (*ls)->next;
*ls = p;
}
//在其他位置插入
else
{
listNode* prev = *ls;
while(prev->next != NULL && prev->next->data < p->data)
{
prev = prev->next;
}
p->next = prev->next;
prev->next = p;
}
p=q;
}
}
链表逆置
- 断开链表。
- 断开后的链表依次进行头插。
void listReverse(list* ls)
{
if ((*ls) == NULL || (*ls)->next == NULL)
{
return;
}
//断开链表
listNode* p = (*ls)->next;
(*ls)->next = NULL;
while(p != NULL)
{
//头插节点
listNode* q = p->next;
p->next = *ls;
*ls = p;
p = q;
}
}
链表打印
void printfList(list ls)
{
listNode* node = ls;
while (node != NULL)
{
printf("%d ", node->data);
node = node->next;
}
printf("\n");
}
链表销毁
void listDestory(list* ls)
{
listNode* p = NULL;
while (*ls != NULL)
{
p = *ls;
*ls = p->next;
free(p);
}
}