单链表
前言
自己学习数据结构的笔记,看的是王道的视频,写了源代码在最下面,有详细的注释,可以直接跑。(要保存成c++文件)希望对大家学习数据结构有所帮助。
一、单链表的定义
1.定义单链表
typedef struct LNode{
int data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
//LNode 强调节点
//*LinkList 强调链表
2.初始化单链表
bool InitList(LinkList &L){
L = NULL;
return true;
}
二、单链表的插入
1.指定节点的后插操作 [平均时间复杂度O(1)]
//后插操作,在节点p之后插入元素e
bool InsertNextNode(LNode *p,int e){
//1.判断节点p是否存在
if(p == NULL)
return false;
//2.分配内存空间
LNode *s = (LNode*)malloc(sizeof(LNode));
//3.进行后插操作
s->data = e; //将元素e的值赋值给节点s
s->next = p->next; //令s的指针指向p的下一个节点
p->next = s; //令p的指针指向s
return true;
}
2.指定节点的前插操作 [平均时间复杂度O(1)]
//前插操作,在节点p前插入元素e
bool InsertPriorNode(LNode *p,int e){
//1.判断节点p是否存在
if(p == NULL)
return false;
//2.分配内存空间
LNode *s = (LNode*)malloc(sizeof(LNode));
//3.进行前插操作
s->next = p->next; //让s的指针指向p的下一个节点
p->next = s; //让p的指针指向s
s->data = p->data; //将p的数据赋值给s
p->data = e; //将e赋值给p
return true;
}
3.按位序插入(不带头结点) [平均时间复杂度O(n)]
//按位序插入,在第i个位置插入元素e
bool ListInsert(LinkList &L,int i,int e){
//1.判断i的值是否合法
if(i < 1){
return false;
}
//2.当i是第一个节点时,进行特殊处理
if(i == 1){
//分配内存空间
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = e; //将元素e的值赋值给节点s
s->next = L; //令s的指针指向原来的第一个节点(头指针指向的节点)
L = s; //头指针指向新节点s
return true;
}
LNode *p; //指针p指向当前扫描到的节点
int j = 1; //当前p指向的是第几个节点
p = L; //没有头结点,L指向第一个节点
//3.循环找到第i-1个节点
while(p && j < i-1){
p = p->next;
j++;
}
//4.i值不合法
if(p == NULL)
return false;
//5.调用后插操作
return InsertNextNode(p,e);
}
4.按位序插入(带头结点) [平均时间复杂度O(n)]
//按位序插入,在第i个位置插入元素e
bool ListInsert(LinkList &L,int i,int e){
//1.判断i的值是否合法
if(i < 1){
return false;
}
//2.循环找到第i-1个节点
LNode *p; //指针p指向当前扫描到的节点
int j = 0; //当前p指向的是第几个节点
p = L; //L指向头结点,头结点是第0个节点(不存储数据)
while(p && j < i-1){
p = p->next;
j++;
}
//3.i值不合法
if(p == NULL)
return false;
//4.调用后插操作
return InsertNextNode(p,e);
}
三、单链表的删除
1.删除指定节点
//删除指定节点p
//当p为最后一个节点时,不能使用此操作
bool DeleteNode(LNode *p){
//1.判断第i-1个节点后是否存在节点
if(p->next == NULL)
return false;
//2.进行删除操作
LNode *q = p->next; //创建指针q指向被删除节点
p->data = q->data; //用e返回被删除元素的值
p->next = q->next; //p的指针指向被删除节点的下一个节点
free(q);
}
2.按位序删除(不带头结点) [平均时间复杂度O(n)]
//按位序删除,删除位序为i的节点,并用e返回被删除的值
bool ListDelete(LinkList &L,int i,int &e){
//1.判断i的值是否合法
if(i < 1)
return false;
//2.当删除第一个节点时,进行特殊操作,让头指针指向第二个元素
if(i == 1){
L = L->next;
return true;
}
//3.循环找到第i-1个节点
LNode *p; //指针p指向当前扫描到的节点
int j = 1; //当前p指向的是第几个节点,因为没有头结点,p指向第一个节点
p = L;
while(p && j < i-1){
p = p->next;
j++;
}
//4.判断i的值是否合法
if(p == NULL)
return false;
//5.判断第i-1个节点后是否存在节点
if(p->next == NULL)
return false;
//6.进行删除操作
LNode *q = p->next; //创建指针q指向被删除节点
e = q->data; //用e返回被删除元素的值
p->next = q->next; //p的指针指向被删除节点的下一个节点
free(q); //释放节点q的内存空间
return true;
}
3.按位序删除(带头结点) [平均时间复杂度O(n)]
//按位序删除,删除位序为i的节点,并用e返回被删除的值
bool ListDelete(LinkList &L,int i,int &e){
//1.判断i的值是否合法
if(i < 1)
return false;
//2.循环找到第i-1个节点
LNode *p; //指针p指向当前扫描到的节点
int j = 0; //当前p指向的是第几个节点
p = L;
while(p && j < i-1){
p = p->next;
j++;
}
//3.判断i的值是否合法
if(p == NULL)
return false;
//4.判断第i-1个节点后是否存在节点
if(p->next == NULL)
return false;
//6.进行删除操作
LNode *q = p->next; //创建指针q指向被删除节点
e = q->data; //用e返回被删除元素的值
p->next = q->next; //p的指针指向被删除节点的下一个节点
free(q); //释放节点q的内存空间
return true;
}
四、单链表的查找
1.按位查找 [平均时间复杂度O(n)]
//按位查找,返回第i个元素
LNode* GetElem(LinkList L,int i){
//1.判断i值是否合法
if(i < 0) //当不带头结点时,判断条件为(i < 1)
return NULL;
//2.循环找到第i个节点
LNode *p; //指针p指向当前扫描到的节点
int j = 0; //当前p指向的是第几个节点,当不带头结点时,j初始值为(j = 1)
p = L; //因为没有头结点,p指向第一个节点
while(p != NULL && j < i){
p = p->next;
j++ ;
}
return p;
}
2.按值查找 [平均时间复杂度O(n)]
//按值查找,找到数据域等于e的第一个节点
LNode* LocateElem(LinkList L,int e){
//1.创建指针p令其指向第一个节点
LNode *p = L->next; //当不带头结点时*p = L
//2.循环查找数据域为e的节点
while(p != NULL && p->data != e){
p = p->next;
}
return p;
}
3.统计单链表的长度 [平均时间复杂度O(n)]
//求单链表表长
int length(LinkList L){
int len = 0;
LNode *p = L;
while(p->next != NULL){ //当不带头结点时,判断条件为 p != NULL
len++;
p = p->next;
}
return len;
}
五、单链表的建立
1.尾插法建立单链表 [平均时间复杂度O(n)]
//尾插法创建单链表
LinkList TailInsert(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
LNode *s;
LNode *r = L; //表尾指针
//bool is_head = true; //判断插入节点的是否为第一个元素
int x;
scanf("%d",&x); //输入插入单链表的元素
while(x != 9999){ //当输入9999时,结束单链表创建
s = (LNode*)malloc(sizeof(LNode));
/*当不带头结点时,对第一个节点进行特殊操作
if(is_head != false){
s->data = x ;
L = s; //令头指针指向该节点
r = s; //令表尾指针指向该节点
is_head = false;
} */
s->data = x;
r->next = s; //令s的指针指向L的下一个节点
r = s; //令表尾指针指向s
scanf("%d",&x);
}
r->next = NULL;
return L;
}
2.头插法建立单链表 [平均时间复杂度O(n)]
//头插法创建单链表
LinkList HeadInsert(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
LNode *s;
int x;
scanf("%d",&x); //输入插入单链表的元素
while(x != 9999){ //当输入9999时,结束单链表创建
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = L->next; //当不带头结点时,操作为 s->next = L;
L->next = s; //当不带头结点时,操作为 L = s;
scanf("%d",&x);
}
return L;
}
六、单链表的特点
1.不能随机存取。
2.存储密度低,节点处除了存放数据外还需要额外的内存空间存储指针。
3.优点是方便扩展容量,插入删除操作简单。
4.引入头结点的好处:对单链表第一个节点进行操作无需特殊处理。
七、源代码
带头结点的实现方式
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
//单链表的定义
typedef struct LNode{
int data; //表示数据域
struct LNode *next;//指向下一个节点的指针
}LNode,*LinkList;
//LNode 强调节点
//*LinkList 强调链表
//函数声明
bool InitList(LinkList &L); //初始化单链表
bool InsertNextNode(LNode *p,int e); //后插操作
bool InsertPriorNode(LNode *p,int e); //前插操作
bool ListInsert(LinkList &L,int i,int e); //按位序插入
bool ListDelete(LinkList &L,int i,int &e); //按位序删除
bool DeleteNode(LNode *p,int e); //删除指定节点
void LinkListPrint(LinkList L); //遍历单链表
int LinkListlength(LinkList L); //求表长
LNode* GetElem(LinkList L,int i); //按位查找
LNode* LocateElem(LinkList L,int e); //按值查找
LinkList HeadInsert(LinkList &L); //头插法建立单链表
LinkList TailInsert(LinkList &L); //尾插法建立单链表
//主函数
int main(void){
LinkList L;
TailInsert(L);
LinkListPrint(L);
ListInsert(L,1,100);
LinkListPrint(L);
int e = 0;
ListDelete(L,4,e);
LinkListPrint(L);
printf("单链表第2个元素为:%d\n",GetElem(L,2)->data);
printf("按值查找得到的指针地址为:%d\n",LocateElem(L,5));
return 0;
}
//初始化单链表
bool InitList(LinkList &L){
L = NULL;
return true;
}
//后插操作,在节点p之后插入元素e
bool InsertNextNode(LNode *p,int e){
//1.判断节点p是否存在
if(p == NULL)
return false;
//2.分配内存空间
LNode *s = (LNode*)malloc(sizeof(LNode));
//3.进行后插操作
s->data = e; //将元素e的值赋值给节点s
s->next = p->next; //令s的指针指向p的下一个节点
p->next = s; //令p的指针指向s
printf("元素%d插入成功!\n",e);
return true;
}
//前插操作,在p节点前插入元素e
bool InsertPriorNode(LNode *p,int e){
//1.判断节点p是否存在
if(p == NULL)
return false;
//2.分配内存空间
LNode *s = (LNode*)malloc(sizeof(LNode));
//3.进行前插操作
s->next = p->next; //让s的指针指向p的下一个节点
p->next = s; //让p的指针指向s
s->data = p->data; //将p的数据赋值给s
p->data = e; //将e赋值给p
printf("元素%d插入成功\n",e);
return true;
}
//按位序插入,在第i个位置插入元素e
bool ListInsert(LinkList &L,int i,int e){
//1.判断i的值是否合法
if(i < 1){
return false;
}
LNode *p; //指针p指向当前扫描到的节点
int j = 0; //当前p指向的是第几个节点
p = L; //L指向头结点,头结点是第0个节点(不存储数据)
//2.循环找到第i-1个节点
while(p && j < i-1){
p = p->next;
j++;
}
//3.调用后插操作
return InsertNextNode(p,e);
}
//按位序删除,删除位序为i的节点,并用e返回被删除的值
bool ListDelete(LinkList &L,int i,int &e){
//1.判断i的值是否合法
if(i < 1)
return false;
LNode *p; //指针p指向当前扫描到的节点
int j = 0; //当前p指向的是第几个节点
p = L; //因为没有头结点,p指向第一个节点
//2.循环找到第i-1个节点
while(p && j < i-1){
p = p->next;
j++;
}
//3.判断i的值是否合法
if(p == NULL)
return false;
//4.判断第i-1个节点后是否存在节点
if(p->next == NULL)
return false;
//5.进行删除操作
LNode *q = p->next; //创建指针q指向被删除节点
e = q->data; //用e返回被删除元素的值
p->next = q->next; //p的指针指向被删除节点的下一个节点
free(q); //释放节点q的内存空间
printf("元素%d删除成功!\n",e);
return true;
}
//删除指定节点p
//当p为最后一个节点时,不能使用此操作
bool DeleteNode(LNode *p,int e){
//1.判断第i-1个节点后是否存在节点
if(p->next == NULL)
return false;
//2.进行删除操作
LNode *q = p->next; //创建指针q指向被删除节点
p->data = q->data; //用e返回被删除元素的值
p->next = q->next; //p的指针指向被删除节点的下一个节点
free(q);
printf("元素%d删除成功!\n",p->data);
}
//按位查找,返回第i个元素
LNode* GetElem(LinkList L,int i){
//1.判断i值是否合法
if(i < 0)
return NULL;
//2.循环找到第i个节点
LNode *p; //指针p指向当前扫描到的节点
int j = 0; //当前p指向的是第几个节点
p = L; //因为没有头结点,p指向第一个节点
while(p != NULL && j < i){
p = p->next;
j++ ;
}
//3.返回第i个节点的元素值
return p;
}
//按值查找,找到数据域等于e的第一个节点
LNode* LocateElem(LinkList L,int e){
//1.创建指针p令其指向第一个节点
LNode *p = L->next; //不带头结点时 *p = L
//2.查找数据域为e的节点
while(p != NULL && p->data != e){
p = p->next;
}
//3.找到后返回该节点的指针,否则返回NULL
return p;
}
//头插法创建单链表
LinkList HeadInsert(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
LNode *s;
int x;
printf("开始头插法单链表,请输入参数值:\n");
scanf("%d",&x); //输入插入单链表的元素
while(x != 9999){ //当输入9999时,结束单链表创建
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = x;
s->next = L->next; //当不带头结点时,操作为 s->next = L;
L->next = s; //当不带头结点时,操作为 L = s;
scanf("%d",&x);
}
return L;
}
//尾插法创建单链表
LinkList TailInsert(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
LNode *s;
LNode *r = L; //表尾指针
//bool is_head = true;判断插入的是否为第一个元素
printf("开始尾插法单链表,请输入参数值(输入9999结束创建)\n");
int x;
scanf("%d",&x); //输入插入单链表的元素
while(x != 9999){ //当输入9999时,结束单链表创建
s = (LNode*)malloc(sizeof(LNode));
/*当不带头结点时,对第一个节点进行特殊操作
if(is_head != false){
s->data = x ;
L = s; //令头指针指向该节点
r = s; //令表尾指针指向该节点
is_head = false;
} */
s->data = x;
r->next = s; //令s的指针指向L的下一个节点
r = s; //令表尾指针指向s
scanf("%d",&x);
}
r->next = NULL;
return L;
}
//求单链表表长
int LinkListlength(LinkList L){
int len = 0;
LNode *p = L;
while(p->next != NULL){ //当不带头结点时,判断条件为 p != NULL
len++;
p = p->next;
}
printf("单链表表长为:%d\n",len);
return len;
}
//单链表的遍历
void LinkListPrint(LinkList L){
LNode *s = L;
printf("==================\n");
printf("单链表的遍历结果为:");
while(s->next != NULL){
s = s->next;
printf("%d ",s->data);
}
printf("\n");
LinkListlength(L);
printf("==================\n");
}