数据结构之单链表(C语言实现)
本次介绍三种单链表:普通单链表、循环链表和双向链表,后面的博客会继续介绍后两种链表
首先介绍单链表的特点:
1.链表是由一个个内存地址不连续的节点组成
2.每个节点最多只有一个前驱,一个后记(第一个节点只有后继没有前驱,最后一个节点只有前驱没有后继)
3.链表不支持随机存取(意思就是不能通过下标来进行找某个节点,只能从头到尾进行遍历)
4.链表的查找只能从头到尾对链表进行遍历,时间复杂度为O(n);而顺序表查找元素只要给一个下标即可,时间复杂度为O(1)
5.删除链表的某个节点时只需改变节点的指针,不需要移动大量元素
然后从三个方面来介绍三种链表的通用操作:
1.链表的初始化
2.申请一个链表节点
3.链表的头插法
4.链表的尾插法
5.获取链表长度
6.删除链表节点
7.查找指定值的节点
8.销毁链表(释放链表所有节点的内存空间)
9.输出单链表(输出单链表所有节点的数据域)
说明:以下单链表的实现,是数据域以整型为例,而且带有头结点。
一、普通单链表
1.单链表的结构
typedef struct _Node
{
int data;//数据域
struct _Node* next;//指针域
}Node, *List;
2.链表的操作
(1)链表的初始化(带头结点)
这里的初始化只要是指初始化头结点的指针域
void InitList(List plist)
{
if (NULL == plist)
return;
plist->next = NULL;
}
(2).申请一个链表节点
从堆中申请一个节点,注意这里是从堆中申请的内存,只能通过free(p)显式释放内存。即使是局部变量,该内存也不会随着函数调用完成而释放该内存。
static Node* BuyNode(int val)
{
Node* pTmp = (Node*)malloc(sizeof(Node));
pTmp->next = NULL;
return pTmp;
}
(3).链表头插法
这里的链表是带有头结点的,所以每次新插入的节点应插入头结点后面。
void InsertHead(List plist, int val)
{
Node *pTmp = BuyNode(val); //申请一个节点,数据域为val
pTmp->next = plist->next;
plist->next = pTmp;
}
(4).链表尾接法
每次将新插入的节点插入到最后一个节点后面,所以采用尾接法插入节点时首先要找到尾节点
void InsertTail(List plist, int val)
{
Node *pTmp = BuyNode(val);
Node *pCur;
for (pCur = plist; NULL != pCur; pCur = pCur->next) //查找尾节点
{
; //空语句
}
pCur->next = pTmp; //将新节点插入到尾节点之后
}
(5).获取链表长度
对链表进行遍历,每遍历一个节点,计数器加一。
int GetListLen(List plist)
{
Node *pTmp = plist->next;
int iCount = 0;
while (NULL != pTmp)
{
++iCount;
pTmp = pTmp->next;
}
return iCount;
}
(6).删除链表节点
删除指定值的链表节点时,需要遍历该链表,找到对应节点后想要删除该节点必须要知道该节点的前驱节点,这样才能正确删除该节点。
bool Delete(List plist, int val)
{
Node* pPre = plist; //指向前驱节点
Node* pCur = plist->next; //指向当前比较的节点
while (NULL != pCur) //当链表没找完就继续找
{
if (pCur->data != val) //链表没找到就更新当前节点和它的前驱节点
{
pPre = pCur; //更新前驱节点为当前节点
pCur = pCur->next; //更新当前节点为下一个节点
}
else
{
pPre->next = pCur->next;
free(pCur); //释放待删节点的内存
return true; //找到该节点返回true
}
}
return false;
}
(7).查找指定值的节点
查找指定值的节点也需要从头到尾遍历链表,若找到则返回该节点,没找到则返回NULL。
Node* Search(List plist, int val)
{
Node *pCur = plist->next;
while (NULL != pCur)
{
if (pCur->data == val)
{
return pCur;
}
pCur = pCur->next;
}
return NULL;
}
(8).销毁链表
销毁链表就是释放链表中所有节点的内存。
void Destroy(List plist)
{
Node* pCur = plist->next; //注意销毁链表后头结点的内存空间还是存在的,即空链表就是只有一个头结点的单链表
while (NULL != pCur)
{
plist = pCur->next;
free(pCur);
pCur = plist->next;
}
}
(9).输出单链表
输出单链表的操作也比较简单,从头到尾遍历单链表,每遍历一个节点就输出该节点的指针域
void Show(List plist)
{
Node* pCur = plist->next;
while (NULL != pCur)
{
printf("%5d", pCur->data);
pCur = pCur->next;
}
printf("\n");
}
最后附上完整代码和运行结果:
//Link.h
#include<stdio.h>
#include<stdlib.h>
typedef struct _Node
{
int data;
struct _Node* next;
}Node, *List;
void InitList(List plist);
void InsertHead(List plist, int val);
void InsertTail(List plist, int val);
bool Delete(List plist, int val);
Node* Search(List plist, int val);
int GetListLen(List plist);
void Destroy(List plist);
static Node* BuyNode(int val);
void Show(List plist);
//Link.c
#include "test.h"
int main()
{
Node head;
InitList(&head);
for (int i = 0; i < 13; ++i)
{
InsertTail(&head, i); //尾插法
}
ShowList(&head);
printf("链表长度:%d\n", GetListLen(&head));
for (int i = 0; i < 12; ++i)
{
InsertHead(&head, i); //头插法
}
ShowList(&head);
printf("链表长度:%d\n", GetListLen(&head));
printf("search 12:\n");
Node *p = Search(&head, 12); //查找节点
if (p != NULL)
{
printf("%d\n", p->data);
}
else
{
printf("Not Found\n");
}
printf("删除节点12:\n"); //删除节点
if (Delete(&head, 12))
{
ShowList(&head);
}
else
{
printf("链表中无此节点\n");
}
Destroy(&head); //销毁链表
return 0;
}
void InitList(List plist)
{
if (NULL == plist)
return;
plist->next = NULL;
}
void InsertHead(List plist, int val)
{
Node *pTmp = BuyNode(val);
pTmp->next = plist->next;
plist->next = pTmp;
}
void InsertTail(List plist, int val)
{
Node *pTmp = BuyNode(val);
Node *pCur;
for (pCur = plist; NULL != pCur->next; pCur = pCur->next)
{
; //空语句
}
pCur->next = pTmp;
}
bool Delete(List plist, int val)
{
Node* pPre = plist;
Node* pCur = plist->next;
while (NULL != pCur)
{
if (pCur->data != val)
{
pPre = pCur;
pCur = pCur->next;
}
else
{
pPre->next = pCur->next;
free(pCur);
return true;
}
}
return false;
}
Node* Search(List plist, int val)
{
Node *pCur = plist->next;
while (NULL != pCur)
{
if (pCur->data == val)
{
return pCur;
}
pCur = pCur->next;
}
return NULL;
}
int GetListLen(List plist)
{
Node *pTmp = plist->next;
int iCount = 0;
while (NULL != pTmp)
{
++iCount;
pTmp = pTmp->next;
}
return iCount;
}
void Destroy(List plist)
{
Node* pCur = plist->next; //注意销毁链表后头结点的内存空间还是存在的,即空链表就是只有一个头结点的单链表
while (NULL != pCur)
{
plist = pCur->next;
free(pCur);
pCur = plist->next;
}
}
static Node* BuyNode(int val)
{
Node* pTmp = (Node*)malloc(sizeof(Node));
pTmp->next = NULL;
pTmp->data = val;
return pTmp;
}
void ShowList(List plist)
{
Node* pCur = plist->next;
while (NULL != pCur)
{
printf("%5d", pCur->data);
pCur = pCur->next;
}
printf("\n");
}