链表
一.什么是链表?
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
二.链表的种类
按照带头结点和不带头结点,
单向还是双向,
循环还是非循环,可以分为八种
三.链表的实现
1.不带头非循环单链表
每个节点有自己的数据,并且有一个指针指向链表的下一个节点,
单链表的最后一个节点指向NULL
1)数据结构的定义
//不带头单向链表
typedef int DataType; //数据元素类型
//链表的节点
typedef struct ListNode
{
DataType val; //存储的数据
struct ListNode* next; //下一个节点的指针
}ListNode;
//链表
typedef struct List
{
//第一个节点的指针
ListNode* head;
}List;
2)核心操作的实现
初始化链表
//初始化链表
void ListInit(List* lst)
{
if (lst == NULL)
return;
lst->head = NULL;
}
创建新节点
//创建新节点
ListNode* createNode(DataType val)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
node->val = val;
node->next = NULL;
return node;
}
销毁单链表
//销毁单链表
void DestroyList(List* lst)
{
if (lst == NULL)
return;
struct ListNode* node = lst->head;
while (node)
{
struct ListNode* nextNode = node->next;
free(node);
node = nextNode;
}
}
插入删除、尾插尾删、头插头删实现见源码
3)源码
LinkList.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
//不带头单向链表
typedef int DataType;
typedef struct ListNode
{
DataType val;
struct ListNode* next;
}ListNode;
typedef struct List
{
//第一个节点的指针
ListNode* head;
}List;
//初始化链表
void ListInit(List* lst);
//创建新节点
ListNode* createNode(DataType val);
//销毁单链表
void DestroyList(List* lst);
//尾插
bool ListPushBack(List* lst, DataType val);
//头插
bool ListPushFront(List* lst, DataType val);
//尾删
bool ListPopBack(List* lst);
//头删
bool ListPopFront(List* lst);
//插入:node的后面进行插入
bool ListInsert(List* lst, ListNode* node, DataType val);
//删除
bool ListErase(List* lst, ListNode* target);
//查找val对应的节点
ListNode* findNode(List* lst, DataType val);
//获取元素个数
int getSize(List* lst);
//判空
bool isEmpty(List* lst);
//获取链表头部元素
ListNode* getListFront(List* lst);
//获取链表尾部元素
ListNode* getListBack(List* lst);
//打印单链表
void PrintList(List* lst);
LinkList.c
#include "LinkList.h"
//初始化链表
void ListInit(List* lst)
{
if (lst == NULL)
return;
lst->head = NULL;
}
//创建新节点
ListNode* createNode(DataType val)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
node->val = val;
node->next = NULL;
return node;
}
//销毁单链表
void DestroyList(List* lst)
{
if (lst == NULL)
return;
struct ListNode* node = lst->head;
while (node)
{
struct ListNode* nextNode = node->next;
free(node);
node = nextNode;
}
}
//尾插
bool ListPushBack(List* lst, DataType val)
{
if (lst == NULL)
return false;
struct ListNode* newNode = createNode(val);
if (lst->head == NULL)
lst->head = newNode;
else
{
//找到尾节点
struct ListNode* tail = lst->head;
while (tail->next != NULL)
{
tail = tail->next;
}
//将新节点链接到尾部
tail->next = newNode;
}
return true;
}
//头插
bool ListPushFront(List* lst, DataType val)
{
if (lst == NULL)
return false;
struct ListNode* newNode = createNode(val);
if (lst->head == NULL)
lst->head = newNode;
else
{
//重新链接
newNode->next = lst->head;
//更新头节点
lst->head = newNode;
}
return true;
}
//尾删
bool ListPopBack(List* lst)
{
if (lst == NULL || lst->head == NULL)
return false;
struct ListNode* prev = NULL;
struct ListNode* node = lst->head;
//找到尾节点的前一个节点
while (node->next)
{
prev = node;
node = node->next;
}
//释放节点
free(node);
//更新指向
//删除的是头节点
if (prev == NULL)
lst->head = NULL;
else
prev->next = NULL;
return true;
}
//头删
bool ListPopFront(List* lst)
{
if (lst == NULL || lst->head == NULL)
return false;
//释放空间+更新头节点
//头节点后面的元素为空时,空节点就是头节点
struct ListNode* newHead = lst->head->next;
free(lst->head);
lst->head = newHead;
return true;
}
//插入:node的后面进行插入
bool ListInsert(List* lst, ListNode* node, DataType val)
{
if (lst == NULL)
return false;
//链表为空
if (lst->head == NULL && node == NULL)
{
lst->head = createNode(val);
return true;
}
//空节点后面插入视为头插
if(node == NULL){
struct ListNode* newNode = createNode(val);
newNode->next = lst->head;
lst->head = newNode;
return true;
}
//查找node节点是否存在
struct ListNode* p = lst->head;
bool flag = false; //表示是否找到了node节点
while (p)
{
if (p == node)
{
flag = true;
break;
}
node = node->next;
}
//节点不存在
if (flag == false)
return false;
//创建新节点
struct ListNode* newNode = createNode(val);
struct ListNode* nextNode = node->next;
node->next = newNode;
newNode->next = nextNode;
return true;
}
//删除target节点
bool ListErase(List* lst, ListNode* target)
{
if (lst == NULL || lst->head == NULL || target == NULL)
return false;
//删除的是头节点
if (lst->head == target)
{
lst->head = lst->head->next;
free(target);
return true;
}
struct ListNode* node = lst->head;
struct ListNode* prev = NULL; //被删除节点的前一个节点
//查找要删除的节点
while (node)
{
if (node->next == target)
{
prev = node;
break;
}
node = node->next;
}
//没找到要删除的节点
if (prev == NULL)
return false;
prev->next = target->next;
free(target);
return true;
}
//查找val对应的节点
ListNode* findNode(List* lst, DataType val)
{
if (lst == NULL || lst->head == NULL)
return NULL;
struct ListNode* node = lst->head;
while (node)
{
if (node->val == val)
return node;
node = node->next;
}
return NULL;
}
//获取元素个数
int getSize(List* lst)
{
if (lst == NULL || lst->head == NULL)
return 0;
int sz = 0;
struct ListNode* node = lst->head;
while (node)
{
sz++;
node = node->next;
}
return sz;
}
//判空
bool isEmpty(List* lst)
{
return lst == NULL || lst->head == NULL;
}
//获取链表头部元素
ListNode* getListFront(List* lst)
{
if (lst == NULL)
return NULL;
return lst->head;
}
//获取链表尾部元素
ListNode* getListBack(List* lst)
{
if (lst == NULL)
return NULL;
struct ListNode* node = lst->head;
while (node->next)
{
node = node->next;
}
return node;
}
//打印单链表
void PrintList(List* lst)
{
if (lst == NULL || lst->head == NULL)
return;
struct ListNode* node = lst->head;
while (node)
{
printf("%d", node->val);
if (node->next != NULL)
printf("-->");
node = node->next;
}
printf("\n");
}
2.带头循环双向链表
链表有一个头节点,每个节点有自己的数据,有两个指针(前驱指针和后继指针),分别指向前一个节点和后一个节点,最后一个节点的后继指针指向头节点,头节点的前驱指针指向最后一个节点
1)数据结构的定义
typedef int DataType; //链表节点元素类型
//链表节点
struct ListNode
{
DataType val; //存储的数据
struct ListNode* prev; //前驱节点
struct ListNode* next; //后继节点
};
typedef struct ListNode ListNode;
//带头结点的双向链表
typedef struct List
{
ListNode* head;
}List;
2)核心操作的实现
新建一个链表节点
//新建一个链表节点
ListNode* createListNode(DataType val)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
node->val = val;
node->prev = NULL;
node->next = NULL;
return node;
}
链表的初始化
//链表初始化
void ListInit(List* lst)
{
if (lst == NULL)
return;
lst->head = createListNode(INT_MIN);
//让头节点指向自身
lst->head->prev = lst->head->next = lst->head;
}
链表的销毁
//链表的销毁
void ListDestroy(List* lst)
{
if (lst == NULL)
return;
struct ListNode* node = lst->head->next;
while (node != lst->head)
{
ListNode* nextNode = node->next;
free(node);
node = nextNode;
}
//释放头节点
free(lst->head);
}
插入节点
//节点之后插入新节点
bool insertAfterNode(List* lst, ListNode* node, DataType val) {
if (lst == NULL) {
return false;
}
//头节点之后插入
if (node == lst->head) {
ListNode* nextNode = node->next;
ListNode* newNode = createNode(val);
newNode->prev = lst->head;
newNode->next = nextNode;
nextNode->prev = newNode;
lst->head->next = newNode;
return true;
}
//查找节点是否存在于链表中
ListNode* curNode = lst->head->next;
bool isExist = false; //是否存在标志位
while (curNode != lst->head) {
if (curNode == node) {
isExist = true;
break;
}
curNode = curNode->next;
}
//节点在链表中,在节点后面插入新节点
if (isExist) {
ListNode* nextNode = node->next;
ListNode* newNode = createNode(val);
node->next = newNode;
newNode->prev = node->prev;
newNode->next = nextNode;
nextNode->prev = newNode;
return true;
}
else {
//目标节点不在链表中,返回
return false;
}
}
删除节点
//删除节点
bool eraseNode(List* lst, ListNode* node) {
if (lst == NULL) {
return false;
}
//查找节点是否在链表中
bool isExist = false; //节点是否在链表中标志位
ListNode* curNode = lst->head;
while (curNode != curNode->prev) {
if (curNode == node) {
curNode->prev->next = curNode->next;
curNode->next->prev = curNode->prev;
free(curNode);
isExist = true;
break;
}
else {
curNode = curNode->next;
}
}
if (!isExist) {
return false;
}
return true;
}
头插、头删、尾插、尾删见下面的源码
3)源码
LinkList.h
#pragma once
//带头双向循环链表
//vs2013编译器无法识别typedef是最困扰我的问题
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h> //INT_MIN
typedef int DataType; //链表节点元素类型
//链表节点
struct ListNode
{
DataType val; //存储的数据
struct ListNode* prev; //前驱节点
struct ListNode* next; //后继节点
};
typedef struct ListNode ListNode;
//带头结点的双向链表
typedef struct List
{
ListNode* head;
}List;
//新建一个链表节点
ListNode* createListNode(DataType val);
//销毁链表节点
void DestroyNode(ListNode* node);
//链表初始化
void ListInit(List* lst);
//链表的销毁
void ListDestroy(List* lst);
//节点之后插入新节点
bool insertAfterNode(List* lst, ListNode* node, DataType val);
//删除节点
bool eraseNode(List* lst, ListNode* node);
//头插
bool pushFront(List* lst, DataType val);
//尾插
bool pushBack(List* lst, DataType val);
//头删
bool popFront(List* lst);
//尾删
bool popBack(List* lst);
//判空
bool IsEmpty(List* lst);
//获取链表节点个数
int getSize(List* lst);
//从头到尾打印链表元素
void PrintList(List* lst);
//根据值查找链表节点
ListNode* findNode(List* lst, DataType val);
LinkList.c
#include "LinkList.h"
//新建一个链表节点
ListNode* createListNode(DataType val)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
node->val = val;
node->prev = NULL;
node->next = NULL;
return node;
}
//销毁链表节点
void DestroyNode(ListNode* node)
{
if (node == NULL)
return;
free(node);
node->prev = node->next = NULL;
}
//链表初始化
void ListInit(List* lst)
{
if (lst == NULL)
return;
lst->head = createListNode(INT_MIN);
//让头节点指向自身
lst->head->prev = lst->head->next = lst->head;
}
//链表的销毁
void ListDestroy(List* lst)
{
if (lst == NULL)
return;
struct ListNode* node = lst->head->next;
while (node != lst->head)
{
ListNode* nextNode = node->next;
free(node);
node = nextNode;
}
//释放头节点
free(lst->head);
}
//节点之后插入新节点
bool insertAfterNode(List* lst, ListNode* node, DataType val) {
if (lst == NULL) {
return false;
}
//头节点之后插入
if (node == lst->head) {
ListNode* nextNode = node->next;
ListNode* newNode = createNode(val);
newNode->prev= lst->head;
newNode->next = nextNode;
nextNode->prev= newNode;
lst->head->next = newNode;
return true;
}
//查找节点是否存在于链表中
ListNode* curNode = lst->head->next;
bool isExist = false; //是否存在标志位
while (curNode != lst->head) {
if (curNode == node) {
isExist = true;
break;
}
curNode = curNode->next;
}
//节点在链表中,在节点后面插入新节点
if (isExist) {
ListNode* nextNode = node->next;
ListNode* newNode = createNode(val);
node->next = newNode;
newNode->prev= node->prev;
newNode->next = nextNode;
nextNode->prev= newNode;
return true;
}
else {
//目标节点不在链表中,返回
return false;
}
}
//删除节点
bool eraseNode(List* lst, ListNode* node) {
if (lst == NULL) {
return false;
}
//查找节点是否在链表中
bool isExist = false; //节点是否在链表中标志位
ListNode* curNode = lst->head;
while (curNode != curNode->prev) {
if (curNode == node) {
curNode->prev->next = curNode->next;
curNode->next->prev = curNode->front;
free(curNode);
isExist = true;
break;
}
else {
curNode = curNode->next;
}
}
if (!isExist) {
return false;
}
return true;
}
//头插
bool pushFront(List* lst, DataType val)
{
if (lst == NULL)
return false;
struct ListNode* newNode = createListNode(val);
struct ListNode* prev = lst->head;
struct ListNode* next = lst->head->next;
//更新连接
newNode->prev = prev;
newNode->next = next;
prev->next = newNode;
next->prev = newNode;
return true;
}
//尾插
bool pushBack(List* lst, DataType val)
{
if (lst == NULL)
return false;
struct ListNode* newNode = createListNode(val);
struct ListNode* tail = lst->head->prev; //尾节点
//插入尾节点的下一个位置
newNode->prev = tail;
newNode->next = lst->head;
lst->head->prev = newNode;
tail->next = newNode;
return true;
}
//头删
bool popFront(List* lst)
{
//头节点不能删
if (lst->head->prev == lst->head && lst->head->next == lst->head)
return false;
struct ListNode* curNode = lst->head->next; //要删除的节点
struct ListNode* nextNode = curNode->next; //要删除节点的下一个节点
free(curNode);
lst->head->next = nextNode;
nextNode->prev = lst->head;
return true;
}
//尾删
bool popBack(List* lst)
{
//头节点不能删
if (lst->head->prev == lst->head && lst->head->next == lst->head)
return false;
struct ListNode* curNode = lst->head->prev; //要删除的节点:尾节点
struct ListNode* prevNode = curNode->prev; //要删除节点的前一个节点
free(curNode);
prevNode->next = lst->head;
lst->head->prev = prevNode;
return true;
}
//判空
bool IsEmpty(List* lst)
{
return lst->head == lst->head->prev && lst->head == lst->head->next;
}
//获取链表节点个数
int getSize(List* lst)
{
if (lst == NULL || (lst->head->next == lst->head && lst->head->prev == lst->head))
return 0;
int sz = 0;
struct ListNode* p = lst->head->next;
while (p != lst->head)
{
++sz;
p = p->next;
}
return sz;
}
//从头到尾打印链表元素
void PrintList(List* lst)
{
if (lst == NULL)
return;
struct ListNode* p = lst->head->next;
while (p != lst->head)
{
printf("%d ", p->val);
p = p->next;
}
printf("\n");
}
//根据值查找链表节点
ListNode* findNode(List* lst, DataType val)
{
if (lst == NULL)
return NULL;
struct ListNode* p = lst->head->next;
while (p != lst->head)
{
if (p->val == val)
{
return p;
}
p = p->next;
}
return NULL;
}
四.链表和顺序表的区别
顺序表的空间是连续的,链表的空间是非连续的;
顺序表支持随机访问,链表不支持随机访问;
顺序表在满了的时候需要增容,链表不需要增容,链表每次插入一个新节点就为这个节点分配空间;
顺序表除了尾部插入和删除的时间复杂度为O(1)外,其它位置插入和删除的时间复杂度都为O(N),链表任何位置插入和删除的时间复杂度都是O(1);