数据结构-单链表基本操作带图完整详解
作者:互联网
1.什么是链表
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指 针链接次序实现的。
2.为什么要使用链表
在未学习链表时,我们常用的存储数据的方式无非就是数组。使用数组存储数据的好处就是查询快,但是它的弊端也很明显:
1.使用前需声明数组的长度,一旦声明长度就不能更改
2.插入和删除操作需要移动大量的数组元素,效率慢
3. 只能存储一种类型的数据
顺序表就是在计算机中内存以数组的形式保存的线性表,因此这些弊端同理
而链表则可以实现以上这些数组所不具备的功能,此时引入了结构体来实现创建链表的操作
- n个节点离散分配
- 每一个节点之间通过指针相连
- 每一个节点有一个前驱节点和一个后继节点
- 首节点没有前驱节点,尾节点没有后继节点
创建单链表
//带头结点单链表,逻辑相邻,物理不一定相邻,为了找到下一个结点,就必须增加下一个结点的地址
//尾结点;最后一个结点,在单链表中,尾结点的next为NULL,NULL是单链表的结尾标志
//头结点;其数据域不使用或者存放链表长度,其作用相当于标杆,简化整个操作
typedef int ElemType;
typedef struct Node
{
ElemType data;//数据
struct Node* next;//后继指针
}Node,*List;
//typedef Node* List;//List==Node *
//List本质是Node*,但含义不同,List表示一条链表,Node*表示一个节点的地址
首节点:存放第一个有效数据的节点
头节点:在单链表的第一个结点之前附设一个结点,它没有直接前驱,称之为头结点,头结点的数据域可以不存储任何信息,指针域指向第一个节点(首节点)的地址。头结点的作用是使所有链表(包括空表)的头指针非空
头指针:指向头节点的指针
尾节点:存放最后一个有效数据的节点
尾指针:指向尾节点的指针
基本函数:
//初始化plist
void InitList(List plist);
//头插 往plist中头部插入数字val
bool Insert_head(List plist, ElemType val);
//尾插 往plist中尾部插入数字val
bool Insert_tail(List plist, ElemType val);
//删除plist中第一个val
bool DeleteVal(List plist, ElemType val);
//在plist中查找val值找到返回该结点地址,失败返回NULL
Node* Search(List plist, ElemType val);
//判断plist是否为空链表(没有数据结点)
bool IsEmpty(List plist);
//获取plist长度,数据结点的个数
int GetLength(List plist);
//获取plist链表的pos位置(下标)的值
bool GetElem(List plist, int pos, int* rtval);//输出参数rtval
//获取val前驱
Node* Prior(List plist, ElemType val);
//获取val后继
Node* Next(List plist, ElemType val);
//打印
void Show(List plist);
//清空数据
void Clear(List plist);
//销毁
void Destroy(List plist);
3.基本操作
初始化单链表plist(带头结点):
//初始化plist
void InitList(List plist)
{
assert(plist != NULL);
//头结点的数据不使用plist->data,可以不处理
plist->next = NULL;
}
//另一种写法(会出现二级指针不推荐)
void InitList(List* pplist)
{
assert(pplist != NULL);
*pplist = (Node*)malloc(sizeof(Node));//动态创建头结点
assert(*pplist != NULL);
if (*pplist == NULL)
return;
(*pplist)->next = NULL;
}
插入操作:
1.头插
//头插 往plist中头部插入数字val //时间复杂度O(1)
bool Insert_head(List plist, ElemType val)
{
//1.动态创建一个新结点
Node* p = (Node*)malloc(sizeof(Node));
assert(p != NULL);
if (p == NULL)
return false;
//2.把数据val存放到新结点
p->data = val;
//3.把新结点插入在头结点的后面
p->next = plist->next;
plist->next = p;
return true;
}
2.尾插
//尾插 往plist中尾部插入数字val
bool Insert_tail(List plist, ElemType val)//时间复杂度O(n)
{
//1.动态创建新结点
Node* p = (Node*)malloc(sizeof(Node));
assert(p != NULL);
if (p == NULL)
return false;
//2.把值存放到新结点
p->data = val;
//3.找到链表尾巴
Node* q;
for (q = plist; q->next != NULL; q = q->next);
//4.把新结点插在尾结点后面
p->next=q->next;//p->next=NULL
q->next = p;
return true;
}
打印函数:
//打印
void Show(List plist)
{
for (Node* p = plist->next; p != NULL; p = p->next) //遍历所有的数据结点
{
printf("%d ", p->data);
}
printf("\n");
}
上面三个函数测试用例:
int main()
{
Node head;//创建头结点
InitList(&head);
//for (int i = 0; i < 10; i++) //....4 3 2 1 0头插
//{
// Insert_head(&head, i);
//}
for (int i = 0; i < 10; i++) //0 1 2 3 4....尾插
{
Insert_tail(&head, i);
}
Show(&head);
}
查找函数(在plist中查找val值,找到返回该结点地址,失败返回NULL):
//在plist中查找val值,找到返回该结点地址,失败返回NULL
Node* Search(List plist, ElemType val)
{
for (Node* p = plist->next; p != NULL; p = p->next)
{
if (p->data ==val)//找到了
return p;
}
return NULL;//没找到
}
获取任意值val的前期:
//获取val前驱
Node* Prior(List plist, ElemType val)
{
for (Node* p = plist->next; p ->next!= NULL; p = p->next)
{
if (p->next->data == val)
return p; //找到了
}
return NULL;//失败了
}
获取任意值val的后继:
//获取val后继
Node* Next(List plist, ElemType val)
{
for (Node* p = plist->next; p != NULL; p = p->next)
{
if (p->data == val)
return p->next;
}
return NULL;
}
删除操作(删除plist中第一个val值):
//删除plist中第一个val
bool DeleteVal(List plist, ElemType val)
{
//1.找到需要删除的结点前驱
Node* p= Prior(plist,val);//指向前驱结点 调用找前驱函数
if (p == NULL)//没有val
return false;
Node* q = p->next;//标记需要删除的结点
p->next = q->next;//将q从链表中剔除
free(q); //释放结点
return true;
}
判空操作:
//判断plist是否为空链表(没有数据结点)
bool IsEmpty(List plist)
{
return plist->next == NULL;
//等同于
/*if (plist->next == NULL)
return true;
else
return false;*/
}
获取长度(获取plist长度,数据结点的个数):
//获取plist长度,数据结点的个数
int GetLength(List plist)
{
int count = 0;
for (Node* p = plist->next; p != NULL; p = p->next)
{
count++;
}
return count;
}
获取链表下标位置的值(获取plist链表的pos位置(下标)的值):
//获取plist链表的pos位置(下标)的值
bool GetElem(List plist, int pos, int* rtval)//输出参数rtval
{
if (pos < 0 || pos >= GetLength(plist))//下标不存在
return false;
Node* p=plist->next;
for (int i=0;i < pos; p = p->next, i++)
{
;
}
*rtval= p->data;
return true;
}
销毁操作:
//销毁
void Destroy(List plist)
{
/*第一种写法
if (plist == NULL || plist->next == NULL)
return;
Node* p = plist->next;
Node* q;
plist->next = NULL;
while (p != NULL)
{
q = p->next;
free(p);
p = q;
}
*/
//第二种写法
Node* p;//指向第一个数据结点
while (plist->next != NULL)//还有数据结点
{
p = plist->next;
plist->next = p->next;//剔除p
free(p);
}
}
清空数据:
//清空数据 把所有的数据删除
void Clear(List plist)
{
Destroy(plist);
}
完整代码:
#pragma once
typedef int ElemType;
typedef struct Node
{
ElemType data;//数据
struct Node* next;//后继指针
}Node,*List;
//typedef Node* List;//List==Node *
//List本质是Node*,但含义不同,List表示一条链表,Node*表示一个节点的地址
//初始化plist
void InitList(List plist);
//头插 往plist中头部插入数字val
bool Insert_head(List plist, ElemType val);
//尾插 往plist中尾部插入数字val
bool Insert_tail(List plist, ElemType val);
//删除plist中第一个val
bool DeleteVal(List plist, ElemType val);
//在plist中查找val值找到返回该结点地址,失败返回NULL
Node* Search(List plist, ElemType val);
//判断plist是否为空链表(没有数据结点)
bool IsEmpty(List plist);
//获取plist长度,数据结点的个数
int GetLength(List plist);
//获取plist链表的pos位置(下标)的值
bool GetElem(List plist, int pos, int* rtval);//输出参数rtval
//获取val前驱
Node* Prior(List plist, ElemType val);
//获取val后继
Node* Next(List plist, ElemType val);
//打印
void Show(List plist);
//清空数据
void Clear(List plist);
//销毁
void Destroy(List plist);
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"list.h"
//初始化plist
void InitList(List plist)
{
assert(plist != NULL);
//头结点的数据不使用plist->data,可以不处理
plist->next = NULL;
}
//书上的写法
void InitList(List* pplist)
{
assert(pplist != NULL);
*pplist = (Node*)malloc(sizeof(Node));//动态创建头结点
assert(*pplist != NULL);
if (*pplist == NULL)
return;
(*pplist)->next = NULL;
}
//头插 往plist中头部插入数字val// O(1)
bool Insert_head(List plist, ElemType val)
{
//1.动态创建一个新结点
Node* p = (Node*)malloc(sizeof(Node));
assert(p != NULL);
if (p == NULL)
return false;
//2.把数据val存放到新结点
p->data = val;
//3.把新结点插入在头结点的后面
p->next = plist->next;
plist->next = p;
return true;
}
//尾插 往plist中尾部插入数字val
bool Insert_tail(List plist, ElemType val)//O(n)
{
//1.动态创建新结点
Node* p = (Node*)malloc(sizeof(Node));
assert(p != NULL);
if (p == NULL)
return false;
//2.把值存放到新结点
p->data = val;
//3.找到链表尾巴
Node* q;
for (q = plist; q->next != NULL; q = q->next);
//4.把新结点插在尾结点后面
p->next=q->next;//p->next=NULL
q->next = p;
return true;
}
//在plist中查找val值,找到返回该结点地址,失败返回NULL
Node* Search(List plist, ElemType val)
{
for (Node* p = plist->next; p != NULL; p = p->next)
{
if (p->data ==val)//找到了
return p;
}
return NULL;//没找到
}
//删除plist中第一个val
bool DeleteVal(List plist, ElemType val)
{
//1.找到需要删除的结点前驱
Node* p= Prior(plist,val);//指向前驱结点
if (p == NULL)//没有val
return false;
Node* q = p->next;//标记需要删除的结点
p->next = q->next;//将q从链表中剔除
free(q); //释放结点
return true;
}
//判断plist是否为空链表(没有数据结点)
bool IsEmpty(List plist)
{
return plist->next == NULL;
//等同于
/*if (plist->next == NULL)
return true;
else
return false;*/
}
//获取plist长度,数据结点的个数
int GetLength(List plist)
{
int count = 0;
for (Node* p = plist->next; p != NULL; p = p->next)
{
count++;
}
return count;
}
//获取plist链表的pos位置(下标)的值
bool GetElem(List plist, int pos, int* rtval)//输出参数rtval
{
if (pos < 0 || pos >= GetLength(plist))//下标不存在
return false;
Node* p=plist->next;
for (int i=0;i < pos; p = p->next, i++)
{
;
}
*rtval= p->data;
return true;
}
//获取val前驱
Node* Prior(List plist, ElemType val)
{
for (Node* p = plist->next; p ->next!= NULL; p = p->next)
{
if (p->next->data == val)
return p; //找到了
}
return NULL;//失败了
}
//获取val后继
Node* Next(List plist, ElemType val)
{
for (Node* p = plist->next; p != NULL; p = p->next)
{
if (p->data == val)
return p->next;
}
return NULL;
}
//打印
void Show(List plist)
{
for (Node* p = plist->next; p != NULL; p = p->next) //遍历所有的数据结点
{
printf("%d ", p->data);
}
printf("\n");
}
//清空数据 把所有的数据删除
void Clear(List plist)
{
Destroy(plist);
}
//销毁
void Destroy(List plist)
{/*
if (plist == NULL || plist->next == NULL)
return;
Node* p = plist->next;
Node* q;
plist->next = NULL;
while (p != NULL)
{
q = p->next;
free(p);
p = q;
}
*/
Node* p;//指向第一个数据结点
while (plist->next != NULL)//还有数据结点
{
p = plist->next;
plist->next = p->next;//剔除p
free(p);
}
}
//测试用例
int main()
{
Node head;//创建头结点
InitList(&head);
//for (int i = 0; i < 10; i++) //....4 3 2 1 0头插
//{
// Insert_head(&head, i);
//}
for (int i = 0; i < 10; i++) //0 1 2 3 4....尾插
{
Insert_tail(&head, i);
}
Show(&head);
DeleteVal(&head, 3);
Show(&head);
printf("长度为:%d\n", GetLength(&head));
int rtval;
for (int i = 0; i < 10; i++)
{
if (GetElem(&head, i, &rtval))
printf("%d %d\n", i, rtval);
}
for (int i = -1; i < 10; i++)
{
Node* p = Next(&head, i);//没有后继
if (p == NULL)
printf("%d没有后继\n", i);
else
printf("%d的后继数据为%d\n", i, p->data);
}
if (IsEmpty(&head))
printf("空");
else
printf("不空");
return 0;
}
看到这里了不妨点个赞!创作不易
标签:Node,单链,val,List,带图,next,基本操作,NULL,plist 来源: https://blog.csdn.net/weixin_51609435/article/details/122394889