单链表的结构
线性表的存储有两种,一种是用数组实现,一种就是用链表方式实现,链式存储是最常见的存储方式之一,链表分为单链表,双链表,循环链表等。
链表节点分散在内存的不同的地方,用指针连接起来,在内存的指向可能是这样的:
单链表包含两个域,一个信息域和一个指针域,信息域包含具体的数据,指针域指向下一个节点,节点定义如下:
//定义节点结构体
typedef struct LNode
{
ElemType data;
struct LNode * next;
}LNode,*LinkList;
利用单链表可以解决顺序表需要大量连续存储空间的缺点,但是单链表附件指针域,也带来浪费存储空间的缺点。
通用用“头指针”来标识一个单链表,头指针是指向单链表的第一个节点的指针,如单链表head(head为头指针),如下:
head == NULL
head == NULL 表示一个空链表。
为了操作上的方便,可以在单链表第一个节点之前附加一个节点,为头节点
head->next == NULL
head->next == NULL 表示一个空链表。
单链表初始化
这里使用头节点的方法进行初始化,那么初始化就只创建一个头节点
代码如下:
//链表初始化
LinkList LinkListInit()
{
LinkList pHead;
pHead=(LinkList)malloc(sizeof(LNode));
if(pHead==NULL)
{
printf("初始化失败!\n");
exit(-1);
}
//初始化成功
printf("初始化链表成功!\n");
pHead->next=NULL;
return pHead;
}
创建一个单链表
在初始化的基础上,连续添加节点,有两种方式,一种是头插法,一种是尾插法,顾名思义,就是分别从头部和尾部插入节点。
头插法:
注意上述指针的顺序,若是先执行步骤2,则a节点会失去控制,关键代码:
q->next = head->next;
head->next = q;
尾插法:
上述两个步骤可以不用这样的顺序,因为最终q->next=NULL,关键代码如下:
q->next = NULL; //也可以写成q->next = c->next;
c->next = q;
完整的代码如下:
//创建链表
void CreateList(LNode * head)
{
// 已经创建了不需要再创建了
if(head->next != NULL)
{
printf("链表已经存在,不需要再创建了!\n");
return;
}
int n;
int x;
LinkList p=head;
printf("请输入节点个数:");
scanf("%d", &n);
if(n<=0)
{
exit(-1);
}
for(int i=1;i<=n;i++)
{
LinkList q=(LinkList )malloc(sizeof(LNode));
printf("请输入第%d个节点值:",i);
scanf("%d",&x);
q->data=x;
q->next=NULL;
//关键的两句,用尾插法
p->next=q;
p=q;
}
printf("创建链表成功!");
}
添加节点
添加节点与插入节点是一致的,只不过这个插入的节点可能在链表中间,先找到要插入的节点d
同样的,图中的两个步骤不能颠倒,关键代码:
q->next = d->next;
d->next = q;
完整的添加代码如下:
//插入节点
void addNode(LNode * head)
{
int n;
int x;
int length=getlistlong(head);
if(head==NULL)
{
printf("请先创建头节点\n");
}
else
{
printf("请输入要插入的位置:\n");
scanf("%d", &n);
if(n>length)
{
printf("因为要插入的位置比最后的位置还要大,已经为你插入到最后的位置\n");
n=length+1;
}
if(n<=0)
{
printf("请输入大于0的位置\n");
}
printf("请输入要插入的值:");
scanf("%d", &x);
LinkList p=(LinkList)malloc(sizeof(LNode));
p->data=x;
LinkList q=head;
for(int i=1;i<n;i++)
{
q=q->next;
}
p->next=q->next;
q->next=p;
printf("插入成功!\n");
}
}
删除节点
找到要删除的位置,记录该节点的前驱位置,若要删除节点d。
上述步骤中,先要保存要删除的节点,关键代码如下:
q = c->next;
c->next = q->next;
free(q); //释放要删除的节点空间
完整代码如下:
void deleteNode(LNode * head)
{
LinkList p; //要删除的元素
int n;
if(head==NULL || head->next==NULL)
{
printf("链表为空或长度为0,不能删除链表\n");
}
else
{
printf("请输入要删除的位置:");
scanf("%d", &n);
if(n<=0)
{
printf("链表为空或长度为0,不能删除链表\n");
}
else
{
int len = listlen(head);
if(len < n)
{
printf("删除的位置不能大于当前链表长度,当前链表长度是:%d\n", len);
return;
}
LinkList q=head;
for(int i=1;i<n;i++)
{
q=q->next;
}
p=q->next;
q->next=q->next->next;
printf("删除的元素是:%d\n", p->data);
}
}
}
将链表逆序
链表逆序就是将原来的顺序从尾到头的一种方式,如1-2-3-4-5,逆序后变成5-4-3-2-1
注意图中代码执行顺序,关键代码:
q=p->next;
p->next=q->next;
q->next=head->next;
head->next=q;
完整代码:
//链表逆序
void reverList(LNode * head)
{
LinkList p=NULL;
LinkList q=NULL;
if(head == NULL)
{
printf("链表为空,不需逆序!\n");
}
p=head->next;
while(p->next != NULL)
{
q=p->next;
p->next=q->next;
q->next=head->next;
head->next=q;
}
printf("逆序成功,现在的链表的顺序是:\n");
printList(head);
}
链表清空
当程序退出时,需要将链表的资源清空,包括头节点,从头节点开始遍历,释放空间,直到结束。
代码如下:
// 释放链表空间
void freeList(LNode *phead)
{
for(; phead!=NULL;)
{
LNode *cur = phead;
phead = cur->next;
free(cur);
}
}
完整代码
完整代码给出了输出链表,获取链表长度等操作,并给出main函数持续操作。
头文件mylist.h
/**********************************
*Copyright(C),2010-2020,programer
*Filename:mylist.h
*Author:programer-L
*Description:list list some operator function
*Date:2020年8月10日
*Version: v1.0
**********************************/
#ifndef MYLIST_H
#define MYLIST_H
typedef int ElemType;
//定义节点结构体
typedef struct LNode
{
ElemType data;
struct LNode * next;
}LNode,*LinkList;
//函数声明
/**********************************
*Function:CreateLLinkLiLinkListInitstInitist
*Description:创建一个单链表
*Input:
*Output:
*Return: 返回链表头节点
*Others:
**********************************/
LinkList LinkListInit();
/**********************************
*Function:CreateList
*Description:创建一个单链表
*Input:
*Output:
*Return: 返回链头节点
*Others:
**********************************/
void CreateList(LNode *phead);
/**********************************
*Function:printList
*Description:打印列表
*Input:
*Output:
*Return:
*Others:
**********************************/
void printList(LNode *phead);
/**********************************
*Function:addNode
*Description:打印列表
*Input:
*Output:
*Return:
*Others:
**********************************/
void addNode(LNode *phead);
/**********************************
*Function:deleteNode
*Description:删除列表
*Input:
*Output:
*Return:
*Others:
**********************************/
void deleteNode(LNode *phead);
/**********************************
*Function:searchNode
*Description:删除列表
*Input:
*Output:
*Return:
*Others:
**********************************/
void searchNode(LNode *phead);
/**********************************
*Function:reverList
*Description:翻转链表
*Input:
*Output:
*Return:
*Others:
**********************************/
void reverList(LNode *phead);
/**********************************
*Function:getlistlong
*Description:链表长度
*Input:
*Output:
*Return:
*Others:
**********************************/
int getlistlong(LNode * head);
/**********************************
*Function:freeList
*Description:释放链表空间
*Input:
*Output:
*Return:
*Others:
**********************************/
void freeList(LNode *phead);
/**********************************
*Function:select
*Description:翻转链表
*Input:
*Output:
*Return:
*Others:
**********************************/
void select(LNode *phead);
/**********************************
*Function:showmeu
*Description:翻转链表
*Input:
*Output:
*Return:
*Others:
**********************************/
void showmeu();
#endif
实现接口函数mylist.c:
#include <stddef.h>
#include "mylist.h"
//链表初始化
LinkList LinkListInit()
{
LinkList pHead;
pHead=(LinkList)malloc(sizeof(LNode));
if(pHead==NULL)
{
printf("初始化失败!\n");
exit(-1);
}
//初始化成功
printf("初始化链表成功!\n");
pHead->next=NULL;
return pHead;
}
void showmeu()
{
printf("\n***************菜单,选择操作**********************\n");
printf("1.新建链表\n");
printf("2.遍历链表\n");
printf("3.添加节点\n");
printf("4.删除节点\n");
printf("5.查找节点\n");
printf("6.逆序链表\n");
printf("7.链表长度\n");
printf("8.退出\n");
printf("*****************************************************\n");
}
void select(LNode *head)
{
int selection;
while(1)
{
// system("cls");//清屏
showmeu();
printf("请选择你的操作:");
scanf("%d",&selection);
switch(selection)
{
case 1:
CreateList(head);
break;
case 2:
printList(head);
break;
case 3:
addNode(head);
break;
case 4:
deleteNode(head);
break;
case 5:
searchNode(head);
break;
case 6:
reverList(head);
break;
case 7:
getlistlong(head);
break;
case 8:
freeList(head);
exit(-1);
}
}
}
//创建链表
void CreateList(LNode * head)
{
// 已经创建了不需要再创建了
if(head->next != NULL)
{
printf("链表已经存在,不需要再创建了!\n");
return;
}
int n;
int x;
LinkList p=head;
printf("请输入节点个数:");
scanf("%d", &n);
if(n<=0)
{
exit(-1);
}
for(int i=1;i<=n;i++)
{
LinkList q=(LinkList )malloc(sizeof(LNode));
printf("请输入第%d个节点值:",i);
scanf("%d",&x);
q->data=x;
q->next=NULL;
//关键的两句,用尾插法
p->next=q;
p=q;
}
printf("创建链表成功!");
}
//遍历链表
void printList(LNode * head)
{
LinkList p=head->next;
if(p==NULL)
{
printf("该链表还没有节点元素\n");
}
printf("遍历的结果是:\n");
while(p!=NULL)
{
printf("%d ", p->data);
p=p->next;
}
}
int getlistlong(LNode * head)
{
LinkList p=head->next;
int n=0;
if(p==NULL)
return 0;
while(p!=NULL)
{
n++;
p=p->next;
}
printf("list len is:%d", n);
return n;
}
//插入节点
void addNode(LNode * head)
{
int n;
int x;
int length=getlistlong(head);
if(head==NULL)
{
printf("请先创建头节点\n");
}
else
{
printf("请输入要插入的位置:\n");
scanf("%d", &n);
if(n>length)
{
printf("因为要插入的位置比最后的位置还要大,已经为你插入到最后的位置\n");
n=length+1;
}
if(n<=0)
{
printf("请输入大于0的位置\n");
}
printf("请输入要插入的值:");
scanf("%d", &x);
LinkList p=(LinkList)malloc(sizeof(LNode));
p->data=x;
LinkList q=head;
for(int i=1;i<n;i++)
{
q=q->next;
}
p->next=q->next;
q->next=p;
printf("插入成功!\n");
}
}
//删除节点
void deleteNode(LNode * head)
{
LinkList p; //要删除的元素
int n;
if(head==NULL || head->next==NULL)
{
printf("链表为空或长度为0,不能删除链表\n");
}
else
{
printf("请输入要删除的位置:");
scanf("%d", &n);
if(n<=0)
{
printf("链表为空或长度为0,不能删除链表\n");
}
else
{
int len = getlistlong(head);
if(len < n)
{
printf("删除的位置不能大于当前链表长度,当前链表长度是:%d\n", len);
return;
}
LinkList q=head;
for(int i=1;i<n;i++)
{
q=q->next;
}
p=q->next;
q->next=q->next->next;
printf("删除的元素是:%d\n", p->data);
}
}
}
//查找节点
void searchNode(LNode * head)
{
LinkList p=head;
int n=0;
int x;
printf("输入你要寻找的节点:");
scanf("%d", &x);
if(head==NULL || head->next==NULL) //若头节点为空,或后面也指向一个空的节点
{
printf("链表为空,没有你要找的节点:\n");
}
else
{
while(p->next!=NULL)
{
if(p->data==x)
{
printf("找到你要找的节点的位置了,它在%d个位置\n", n);
break;
}
else
{
p=p->next;
n++;
}
}
if(p->next==NULL)
{
printf("没有找到你要的这个节点!\n");
}
}
}
// 释放链表空间
void freeList(LNode *phead)
{
for(; phead!=NULL;)
{
LNode *cur = phead;
phead = cur->next;
free(cur);
}
}
//链表逆序
void reverList(LNode * head)
{
LinkList p=NULL;
LinkList q=NULL;
if(head == NULL)
{
printf("链表为空,不需逆序!\n");
}
p=head->next;
while(p->next != NULL)
{
q=p->next;
p->next=q->next;
q->next=head->next;
head->next=q;
}
printf("逆序成功,现在的链表的顺序是:\n");
printList(head);
}
主函数main.c:
#include <stdio.h>
#include "mylist.h"
int main(int argc, char* argv[])
{
LinkList head = LinkListInit();
select(head);
return 0;
}
运行结果
root@ubuntu:~/myvscode/list# ./main
初始化链表成功!
菜单,选择操作*******
1.新建链表
2.遍历链表
3.添加节点
4.删除节点
5.查找节点
6.逆序链表
7.链表长度
8.退出
请选择你的操作:1
请输入节点个数:2
请输入第1个节点值:1
请输入第2个节点值:2
创建链表成功!
菜单,选择操作*******
1.新建链表
2.遍历链表
3.添加节点
4.删除节点
5.查找节点
6.逆序链表
7.链表长度
8.退出
请选择你的操作:2
遍历的结果是:
1 2
菜单,选择操作*******
1.新建链表
2.遍历链表
3.添加节点
4.删除节点
5.查找节点
6.逆序链表
7.链表长度
8.退出
请选择你的操作:3
list len is:2请输入要插入的位置:
2
请输入要插入的值:6
插入成功!
菜单,选择操作*******
1.新建链表
2.遍历链表
3.添加节点
4.删除节点
5.查找节点
6.逆序链表
7.链表长度
8.退出
请选择你的操作:2
遍历的结果是:
1 6 2
菜单,选择操作*******
1.新建链表
2.遍历链表
3.添加节点
4.删除节点
5.查找节点
6.逆序链表
7.链表长度
8.退出
请选择你的操作:6
逆序成功,现在的链表的顺序是:
遍历的结果是:
2 6 1
菜单,选择操作*******
1.新建链表
2.遍历链表
3.添加节点
4.删除节点
5.查找节点
6.逆序链表
7.链表长度
8.退出
请选择你的操作:7
list len is:3
菜单,选择操作*******
1.新建链表
2.遍历链表
3.添加节点
4.删除节点
5.查找节点
6.逆序链表
7.链表长度
8.退出
请选择你的操作:5
输入你要寻找的节点:7
没有找到你要的这个节点!
菜单,选择操作*******
1.新建链表
2.遍历链表
3.添加节点
4.删除节点
5.查找节点
6.逆序链表
7.链表长度
8.退出
请选择你的操作:8
以上是单链表的一些记录,欢迎指正交流。