简介
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域,由于不必按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
分类
链表分为带头、不带头,单向、双向,循环、非循环,所以共有 8 种情况,但并不是 8 种情况都常用,我们挑出两种极端的情况进行演示,即无头单向非循环链表和带头双向循环链表。
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
带头双向循环链表:结构最复杂,一般用在单独存储数据。 实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。
具体实现
无头单向非循环链表:
LinkList.h
#pragma once
#include<stdio.h>
#include<malloc.h>
#include<assert.h>
//无头单向非循环链表增删查改实现
typedef size_t LDataType;
//链表的单个节点
typedef struct ListNode
{
LDataType data;
struct ListNode* next;
}ListNode;
//链表
typedef struct List
{
ListNode* head;
}List;
void ListInit(List* plist);
void ListDestory(List* plist);
ListNode* BuyListNode(LDataType data);
void ListPrint(List* plist);
void ListPushFront(List* plist, LDataType data);
void ListPopFront(List* plist);
void ListPushBack(List* plist, LDataType data);
void ListPopBack(List* plist);
ListNode* ListFind(List* plist, LDataType data);
void ListInsertAfter(ListNode* pos, LDataType data); // 在pos的后面进行插入
void ListErase(List* plist, ListNode* pos);
void ListEraseAfter(ListNode* pos);
void ListRemove(List* plist, LDataType data);
LinkList.c
#include"LinkList.h"
void ListInit(List* plist)
{
assert(plist);
plist->head = NULL;
}
void ListDestory(List* plist)
{
assert(plist);
for (ListNode* cur = plist->head; cur != NULL; cur = cur->next)
{
ListNode* next = cur->next;
free(cur);
}
plist->head = NULL;
}
ListNode* BuyListNode(LDataType data)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
assert(node);
node->data = data;
node->next = NULL;
return node;
}
void ListPrint(List* plist)
{
assert(plist);
for (ListNode *cur = plist->head; cur != NULL; cur = cur->next)
{
printf("%d->", cur->data);
}
printf("NULL\n");
}
void ListPushFront(List* plist, LDataType data)
{
assert(plist);
ListNode* node = BuyListNode(data);
assert(node);
node->next = plist->head;
plist->head = node;
}
void ListPopFront(List* plist)
{
assert(plist != NULL);//保证链表存在
assert(plist->head);//保证链表不为空
ListNode* tmp = plist->head;
plist->head = tmp->next;
free(tmp);
}
void ListPushBack(List* plist, LDataType data)
{
assert(plist);
if (plist->head == NULL) {
ListPushFront(plist, data);
return;
}
ListNode *node = BuyListNode(data);
assert(node);
ListNode* cur = plist->head;
for (; cur->next != NULL; cur = cur->next)
{ }
cur->next = node;
}
void ListPopBack(List* plist)
{
assert(plist);
assert(plist->head);
ListNode* cur = plist->head;
if (cur->next == NULL) {
ListPopFront(plist);
return;
}
ListNode* pos = plist->head;
for (; cur->next != NULL; cur = cur->next)
{
pos = cur;
}
free(cur);
pos->next = NULL;
}
//查找给定数据在链表中的位置
ListNode* ListFind(List* plist, LDataType data)
{
assert(plist);
assert(plist->head);
ListNode* cur = plist->head;
while (cur!=NULL)
{
if (cur->data == data)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 在pos的后面进行插入
void ListInsertAfter(ListNode* pos, LDataType data)
{
ListNode* node = BuyListNode(data);
node->next = pos->next;
pos->next = node;
}
//根据位置删除元素
void ListErase(List* plist, ListNode* pos)
{
assert(plist);
if (plist->head == pos)
{
ListPopFront(plist);
}
else if (pos->next == NULL)
{
ListPopBack(plist);
}
else
{
ListNode* cur = plist->head;
for (; cur != NULL; cur = cur->next)
{
}
cur->next = pos->next;
free(pos);
}
}
//删除给定位置后面的元素
void ListEraseAfter(ListNode* pos)
{
ListNode* next = pos->next;
pos->next = next->next;
free(next);
}
//根据数据删除元素
void ListRemove(List* plist, LDataType data)
{
assert(plist);
ListNode* cur = plist->head;
ListNode* temp = NULL;
if (cur->data == data) //当该数据是第一个节点时,直接头删
{
ListPopFront(plist);
return;
}
else
{
while (cur) //头节点不为空
{
temp = cur;
cur = cur->next;
//这里的if...else语句顺序不能交换,需要先排除掉特殊情况,即要删除的数据不存在
//如果交换,当要删除的数据不存在时,会陷入死循环
if (NULL == cur)
{
printf("Data is not exist!\n");
return;
}
else if (cur->data == data)
{
temp->next = cur->next;
free(cur);
break;
}
}
}
}
test.c
#include"LinkList.h"
void test()
{
List plist;
ListInit(&plist);
ListPushBack(&plist, 1);
ListPushBack(&plist, 2);
ListPushBack(&plist, 3);
ListPushBack(&plist, 4);
ListPushBack(&plist, 5);
ListPushBack(&plist, 6);
ListPrint(&plist);
ListPopFront(&plist);
ListPrint(&plist);
//ListNode* pos = ListFind(&plist, 2);
//ListErase(&plist, pos);
//ListPrint(&plist);
ListRemove(&plist, 8);
ListNode* pos = ListFind(&plist, 3);
ListInsertAfter(pos, 9);
ListPrint(&plist);
ListDestory(&plist);
}