单链表的创建与基本操作
1、单链表的理解
1.1单链表的定义
线性表的链式存储又称单链表,它是指通过依据任意的存储单元来存储线性表中的数据元素。为了建立数据元素之间的线性关系,对每个链表结点,除存放元素自身的信息外,还需要存放一个指向后继的指针。单链表结点如下所示。其中data为数据域,存放数据元素;next为指针域,存放其后继结点的地址。
1.2单链表的图形解释
1.1.3单链表的特点
1)单链表不要求逻辑上相邻的两个元素在物理位置上也相邻,因此不需要连续的存储空间。
2)单链表是非随机的存储结构,即不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。
1.1.4单链表的优点和缺点
优点:
1)在任意位置插入、删除他的时间复杂度为O(1)。
2) 没有增容问题,插入一个开辟一个空间 ,不会对开辟的空间资源浪费。
缺点:
1) 单链表的数据是链式存储的,它的元素是不支持随机访问的 ,所以它的时间复杂度为O(n)。
2) 每存放一个元素时需要另外开辟一个指针域的空间,使得多次调用。
2.1、单链表的创建代码
typedef struct LNode //定义单链表节点类型
{
ElemType data; //定义节点存放一个数据元素
struct LNode* next; //指针指向下一个节点
}LNode,*LinkList;
2.2代码的实现
2.2.1主函数 main.cpp
#include"LinkList.h"
void test1()
{
LinkList L;
InitList(L);
int main()
{
test1();
return 0;
}
2.2.2 函数的实现
#include"LinkList.h"
//初始化一个单链表(带头节点)
Status InitList(LinkList& L)
{
L = (LNode*)malloc(sizeof(LNode)); //创建头节点
if (L == NULL) //内存不足分配失败
return flase;
L->next = NULL; //只有一个头节点,下一个节点应该置空
return true;
}
//判断单链表是否为空
Status Empty(LinkList L)
{
if (L->next == NULL)
return true;
return flase;
}
//使用头插建立单链表,n表示要插入的个数
Status CreateList_front(LinkList& L, int n)
{
L = (LNode*)malloc(sizeof(LNode)); //创建头节点
if (L == NULL)
return flase;
L->next = NULL; //头节点的next域置为空
int i;
ElemType x; //后续要输入的值,存放入x中
for (i = 0; i < n; i++)
{
LNode* newNode = (LNode*)malloc(sizeof(LNode)); //新建节点
if (newNode == NULL)
return flase;
cout << "请输入x的值:";
cin >> x; //输入数据
newNode->data = x; //存放数据
newNode->next = L->next; //将节点插在首节点之间,头节点之后
L->next = newNode;
}
}
//使用尾插建立单链表,n表示要插入的个数
Status CreateList_tail(LinkList& L, int n)
{
LinkList s;
L = (LNode*)malloc(sizeof(LNode)); //新建头节点
if (L == NULL)
return flase;
L->next = NULL;
s = L; //用来指向链表的尾节点
int i;
ElemType x;
for (i = 0; i < n; i++)
{
LNode* newNode = (LNode*)malloc(sizeof(LNode)); //创建节点
if (newNode == NULL)
return flase;
cout << "请输入x的值:";
cin >> x;
newNode->data = x;
newNode->next = s->next; //将节点插入尾节点之后
s->next = newNode;
s = newNode;
}
s->next = NULL; //尾节点的next域置NULL
}
//按位查找,查找第 i 个位置的值,并用 e 返回
ElemType LocateElem(LinkList L, int i, ElemType& e)
{
LNode* p; //p指向头节点
p = L->next;
int j=1;
while (p&&j!=i) //寻找下标为i的节点p
{
p = p->next;
j++;
}
if (j < i) //查找的i小于链表的长度
return flase;
e = p->data;
return e; //找到并返回e
}
//按值查找,查找第一个值等于 e 的下标并返回
Status Locate_e(LinkList L, ElemType e)
{
LNode* p; //p指向头节点
p = L->next;
int i = 1;
while (p && p->data != e) //查找值为e的节点p
{
p = p->next;
i++;
}
if (p == NULL) //为找到
return flase;
return i; //找到了,返回i
}
//删除第 i 个位置的值,并用 e 返回
Status DelElem(LinkList& L, int i, ElemType& e)
{
LinkList p, pre; //建立指向头节点的p,和指向节点p前驱的pre
p = L->next;
pre = L;
int j=1;
while (p && j != i) //寻找第i位的节点p
{
pre = p;
p = p->next;
j++;
}
if (j < i) //未找到
return flase;
e = p->data; //找到并释放p指针,并用e存放p指向的值
pre->next = p->next;
free(p);
p = NULL;
return true;
}
//删除第一个值等于 e 的值,并用下标返回 e 出现的位置
Status Del_e(LinkList& L, ElemType e)
{
LNode* p, *pre; //建立指向头节点的p,和指向节点p前驱的pre
p = L->next;
pre = L;
int i = 1;
while (p && p->data != e) //查找值为e的节点
{
pre = p;
p = p->next;
i++;
}
if (p==NULL) //循环结束还没找到时
return flase;
pre->next = p->next; //找到后释放p指针
free(p);
p = NULL;
return i; //返回找到值的下标
}
//依次打印单链表的值
Status LinkList_print(LinkList L)
{
LNode* p;
p = L->next;
cout << "单链表的值为:";
while (p)
{
cout << " "<<p->data;
p = p->next;
}
if (p != NULL)
return flase;
return true;
}
//在第 i 个位置后插入值e
Status Insert_e(LinkList& L, int i, ElemType e)
{
LNode* p; //建立指向头节点的p
p = L->next;
int j = 1;
ElemType x;
while (p && j != i) //寻找第i位的节点p
{
p = p->next;
j++;
}
if (j < i) //未找到
return flase;
LNode* newNode = (LNode*)malloc(sizeof(LNode)); //创建节点
if (newNode == NULL)
return flase;
cout << "请输入x的值:";
cin >> x;
newNode->data = x;
newNode->next = p->next; //将新建节点插入i之后
p->next = newNode;
}
2.2.3 函数的声明
#pragma once
#include<iostream>
#include<stdlib.h>
using namespace std;
#define flase -1
#define ture 1;
typedef int ElemType;
typedef int Status;
typedef struct LNode //定义单链表节点类型
{
ElemType data; //定义节点存放一个数据元素
struct LNode* next; //指针指向下一个节点
}LNode,*LinkList;
//初始化一个单链表(带头节点)
Status InitList(LinkList& L);
//判断单链表是否为空
Status Empty(LinkList L);
//使用头插建立单链表,n表示要插入的个数
Status CreateList_front(LinkList& L, int n);
//依次打印单链表的值
Status LinkList_print(LinkList L);
//使用尾插建立单链表,n表示要插入的个数
Status CreateList_tail(LinkList& L, int n);
//按位查找,查找第 i 个位置的值,并用 e 返回
ElemType LocateElem(LinkList L, int i, ElemType& e);
//按值查找,查找第一个值等于 e 的下标并返回
Status Locate_e(LinkList L, ElemType e);
//删除第 i 个位置的值,并用 e 返回
Status DelElem(LinkList &L, int i, ElemType& e);
//删除第一个值等于 e 的值,并用下标返回 e 出现的位置
Status Del_e(LinkList &L, ElemType e);
//在第 i 个位置后插入值e
Status Insert_e(LinkList& L, int i, ElemType e);
3、代码的部分功能运行截图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-skaEu4Hw-1651591499834)(D:\博客\单链表\3.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aj6zpnRq-1651591499837)(D:\博客\单链表\4.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BpTZjMQl-1651591499838)(D:\博客\单链表\5.png)]
4、总结
曾经老师和我们说过,敲代码的三个境界就是:看代码是代码,看代码是内存,看代码就是代码。
我觉得这境界的体现就在学习单链表中能够更好的体现出来。单链表是 一组地址任意的存储单元存放线性表中的数据元素 ,你既要想象出数据的存储,还有理解下一个数据的存放地址。
``
3、代码的部分功能运行截图
4、总结
曾经老师和我们说过,敲代码的三个境界就是:看代码是代码,看代码是内存,看代码就是代码。
我觉得这境界的体现就在学习单链表中能够更好的体现出来。单链表是 一组地址任意的存储单元存放线性表中的数据元素 ,你既要想象出数据的存储,还有理解下一个数据的存放地址。