链表的概念
1.官方的概念
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
2.我的总结:
链表就是一个可变的数据存储表,可以在他的基础上增删查改。不用像顺序表那样频繁增容,也不会浪费空间,操作更加明确。
如图就是一个简易的链表:
链表的实现
总体思路
首先就是要知道我们实现的链表是单向不带头非循环的链表,我们要建立工程文件,将定义和实现分离。也要知道链可以实现增删查改。首先得定义链表的每个节点:
//我们认为链表的数据类型是int型
typedef int SLTDatatype;
typedef struct SListNode
{
struct SListNode* next;//指向下个节点的地址。
SLTDatatype data;//存储的数据
}STNode;
头文件的主要接口有:
void SListPrint(SLTNode* phead); //链表的打印
void SListPushBack(SLTNode** phead,SLTDatatype x);//尾插
void SListPopBack(SLTNode** phead);//尾删
void SListPushFront(SLTNode** phead, SLTDatatype x);//头插
void SListPopFront(SLTNode** phead);//头删
SLTNode* CreateNewnode(SLTDatatype x);//建立一个新节点
SLTNode* SListFindNode(SLTNode* phead, SLTDatatype x);// 单链表查找,同时具有修改的功能
void SListInsertAfter(SLTNode* pos, SLTDatatype x);// 单链表在pos位置之后插入x
void SListEraseAfter(SLTNode* pos);// 单链表删除pos位置之后的值
void SListDestory(SLTNode** phead);// 单链表的销毁
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDatatype x);//在pos之前插入
为什么要传二级指针呢?因为我们设计的链表是不带头的,如果我们传过来的是一级指针,那我们相当于是传的链表的拷贝,是不起作用的,如果有兴趣,可以看看函数栈帧的调用是怎么样的。这里附上链接:
尾插
思路:因为链表不像是顺序表那样可以进行下标访问,所以我们需要找到尾,从而进行尾插,所以时间复杂度为O(N)。
注意 :
(1) 在尾插之前,我们需要建立一个新节点,我们可以知道,不管是在尾插头插还是在任意位置插入,都需要建立新节点,为了防止代码的冗余,我们可以封装一个函数叫BuySListNode。
(2) 尾插的时候如果链表为空,则直接将新建立的节点给给链表即可。
新节点的建立
代码:
SLTNode* CreateNewnode(SLTDatatype x)//建立一个新节点
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
printf("molloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
代码:
void SListPushBack(SLTNode** phead, SLTDatatype x)//尾插
{
//首先建立一个新节点
assert(phead);
SLTNode* newnode = CreateNewnode(x);
//1.phead是否为空,若为空
if (*phead == NULL)
{
*phead = newnode;
}
else
{
//2.若不为空
//(1).找到尾节点
SLTNode* tail = *phead;
while (tail->next != NULL)
{
tail = tail->next;
}
//(2).新节点
tail->next = newnode;
}
}
尾删
思路:尾删首先得判断链表是否为空,为空则断言暴力解决。第二就是判断链表是否只有一个节点,如果是只有一个节点则需要将链表置为空。如果链表的节点个数大于一,我们需要找到尾节点的前一个,在free最后一个节点以后,要将倒数第二个节点的next指向NULL。
代码:
void SListPopBack(SLTNode** phead)//尾删
{
//判断链表是否为空
assert(*phead != NULL);
//特殊情况特别对待
SLTNode* tail = *phead;
SLTNode* next = (*phead)->next;
if (next == NULL)
{
free(tail);
*phead = NULL;
}
else
{
//删除最后一个节点必须要找到倒数第二个节点
//free最后一个节点之后再把倒数第二个节点next置为空
while (next->next != NULL)
{
tail = tail->next;
next = next->next;
}
free(next);
tail->next = NULL;
}
}
头插
void SListPushFront(SLTNode** phead, SLTDatatype x)//头插
{
assert(phead);
SLTNode* newnode = CreateNewnode(x);
newnode->next = *phead;
*phead = newnode;
}
头删
void SListPopFront(SLTNode** phead)//头删
{
assert(phead && *phead);
SLTNode* tmp = (*phead)->next;
free(*phead);
*phead = tmp;
}
查找
思路:查找就是遍历链表,找到就返回当前节点的地址。
SLTNode* SListFindNode(SLTNode* phead, SLTDatatype x)// 单链表查找
{
assert(phead);
SLTNode* tmp = phead;
while (tmp != NULL)
{
if (tmp->data == x)
{
return tmp;
}
tmp = tmp->next;
}
return NULL;
}
其余接口
void SListInsertAfter(SLTNode* pos, SLTDatatype x)// 单链表在pos位置之后插入x
{
assert(pos);
SLTNode* newnode = CreateNewnode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListEraseAfter(SLTNode* pos)// 单链表删除pos位置之后的值
{
assert(pos && pos->next);
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
}
void SListDestory(SLTNode** phead)// 单链表的销毁
{
assert(phead);
SLTNode* next = *phead;
while (next != NULL)
{
next = next->next;
free(*phead);
*phead = next;
}
*phead = next;
}
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDatatype x)//在pos之前插入
{
assert(phead);
assert(pos);
SLTNode* newnode = CreateNewnode(x);
if (pos == *phead)
{
newnode->next = *phead;
*phead = newnode;
}
else
{
SLTNode* prev = *phead;
SLTNode* tail = (*phead)->next;
while (tail != NULL && tail != pos)
{
prev = prev->next;
tail = tail->next;
}
if (tail == NULL)
{
printf("Do not have pos\n");
return;
}
else
{
prev->next = newnode;
newnode->next = tail;
}
}
}
所有代码
SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDatatype;
typedef struct SListNode
{
SLTDatatype data;
struct SListNode* next;
}SLTNode;
void SListPrint(SLTNode* phead); //链表的打印
void SListPushBack(SLTNode** phead,SLTDatatype x);//尾插
void SListPopBack(SLTNode** phead);//尾删
void SListPushFront(SLTNode** phead, SLTDatatype x);//头插
void SListPopFront(SLTNode** phead);//头删
SLTNode* CreateNewnode(SLTDatatype x);//建立一个新节点
SLTNode* SListFindNode(SLTNode* phead, SLTDatatype x);// 单链表查找,同时具有修改的功能
void SListInsertAfter(SLTNode* pos, SLTDatatype x);// 单链表在pos位置之后插入x
void SListEraseAfter(SLTNode* pos);// 单链表删除pos位置之后的值
void SListDestory(SLTNode** phead);// 单链表的销毁
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDatatype x);//在pos之前插入
SList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
SLTNode* CreateNewnode(SLTDatatype x)//建立一个新节点
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
printf("molloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPrint(SLTNode* phead) //链表的打印
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
void SListPushBack(SLTNode** phead, SLTDatatype x)//尾插
{
//首先建立一个新节点
assert(phead);
SLTNode* newnode = CreateNewnode(x);
//1.phead是否为空,若为空
if (*phead == NULL)
{
*phead = newnode;
}
else
{
//2.若不为空
//(1).找到尾节点
SLTNode* tail = *phead;
while (tail->next != NULL)
{
tail = tail->next;
}
//(2).新节点
tail->next = newnode;
}
}
void SListPopBack(SLTNode** phead)//尾删
{
//判断链表是否为空
assert(*phead != NULL);
//特殊情况特别对待
SLTNode* tail = *phead;
SLTNode* next = (*phead)->next;
if (next == NULL)
{
free(tail);
*phead = NULL;
}
else
{
//删除最后一个节点必须要找到倒数第二个节点
//free最后一个节点之后再把倒数第二个节点next置为空
while (next->next != NULL)
{
tail = tail->next;
next = next->next;
}
free(next);
tail->next = NULL;
}
}
void SListPushFront(SLTNode** phead, SLTDatatype x)//头插
{
assert(phead);
SLTNode* newnode = CreateNewnode(x);
newnode->next = *phead;
*phead = newnode;
}
void SListPopFront(SLTNode** phead)//头删
{
assert(phead && *phead);
SLTNode* tmp = (*phead)->next;
free(*phead);
*phead = tmp;
}
SLTNode* SListFindNode(SLTNode* phead, SLTDatatype x)// 单链表查找
{
assert(phead);
SLTNode* tmp = phead;
while (tmp != NULL)
{
if (tmp->data == x)
{
return tmp;
}
tmp = tmp->next;
}
return NULL;
}
void SListInsertAfter(SLTNode* pos, SLTDatatype x)// 单链表在pos位置之后插入x
{
assert(pos);
SLTNode* newnode = CreateNewnode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListEraseAfter(SLTNode* pos)// 单链表删除pos位置之后的值
{
assert(pos && pos->next);
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
}
void SListDestory(SLTNode** phead)// 单链表的销毁
{
assert(phead);
SLTNode* next = *phead;
while (next != NULL)
{
next = next->next;
free(*phead);
*phead = next;
}
*phead = next;
}
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDatatype x)//在pos之前插入
{
assert(phead);
assert(pos);
SLTNode* newnode = CreateNewnode(x);
if (pos == *phead)
{
newnode->next = *phead;
*phead = newnode;
}
else
{
SLTNode* prev = *phead;
SLTNode* tail = (*phead)->next;
while (tail != NULL && tail != pos)
{
prev = prev->next;
tail = tail->next;
}
if (tail == NULL)
{
printf("Do not have pos\n");
return;
}
else
{
prev->next = newnode;
newnode->next = tail;
}
}
}
谢谢大家!!!