前言
为了避免顺序表再插入和和删除操作时的线性开销,可以将表不连续存储。
链表由一系列不必在内存中相连的结构组成。每一个结构均含有表元素和指向包含该表元素后继元的结构的指针,称之为next指针。最后一个单元的next指针指向NULL。
单链表:结点只有一个指针域的链表。
链表的特点:
(1)、结点在存储器的位置是任意的,逻辑上相邻的数据元素物理上不一定相邻。
(2)、访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点。
一、单链表是什么?
单链表是结点只有一个指针域的链表。分为带头结点的单链表和不带头结点的单链表。以下代码全是对带头结点的单链表操作。
单链表示意图
二、使用步骤
1.头文件
#include <stdio.h>
#include <stdlib.h>
2.单链表结构体定义
typedef struct Lnode{
int data;
struct Lnode* next;
}LNode,*LinkList;
3.单链表的初始化
对于这里使用LinkList* L的解释:如果只是用一个LinkList L来接收主程序传入的头指针,那么这段函数,只是获得了头指针的一个副本,当函数结束时这个副本就消失了。如果用LinkList* L,则接收的时头指针的地址,在函数体中通过*L操作的就是头指针本身。即如果不用*L则只是在函数的生命周期内产生了一个同样指向头结点的另一个头指针,当函数结束时,他就消失了,而真正的头指针没有变化。
void InitList(LinkList* L){
*L=(LinkList)malloc(sizeof(LNode));
if(*L==NULL){
return NULL;
}
(*L)->next=NULL;
}
4.尾插法创建单链表
void CreateList_R(LinkList L){//尾插法
int n,m=2;
LinkList p,p1=L,r=L;
if(L->next==NULL){ //单链表中无数据时
printf("请输入第1个数据('.'结束):");
while(scanf("%d",&n)){
p=(LinkList)malloc(sizeof(LNode));
p->data=n;
p->next=NULL;
r->next=p;
r=p;
printf("请输入第%d个数据('.'结束):",m);
m++;
}
}
else{ //防止在第二次添加元素时,依然从头开始,使之前的数据丢失
while(p1->next){//找到此时尾指针的位置
p1=p1->next;
r=p1;
}
printf("请输入第1个数据('.'结束):");
while(scanf("%d",&n)){
p=(LinkList)malloc(sizeof(LNode));
p->data=n;
p->next=NULL;
r->next=p;
r=p;
printf("请输入第%d个数据('.'结束):",m);
m++;
}
}
printf("输入完成!!!\n");
}
5.头插法创建单链表
void CreateList_H(LinkList L){//头插法
int n,m=2;
LinkList p;
printf("请输入第1个数据('.'结束):");
while(scanf("%d",&n)){
p=(LinkList)malloc(sizeof(LNode));
p->data=n;
p->next=L->next;
L->next=p;
printf("请输入第%d个数据('.'结束):",m);
m++;
}
printf("输入完成!!!\n");
}
6.求单链表长度
int LengthList(LinkList L){//求表长
int len=0;
LinkList p=L->next;
while(p!=NULL){
len++;
p=p->next;
}
return len;
}
7.判断链表是否为空
void ListEmpty(LinkList L) {//判空
if(L->next==NULL) printf("单链表为空。\n") ;
else printf("单链表不为空。\n") ;
}
8.取单链表第i个数据
void GetElem(LinkList L){//取单链表中第i个数据
int i,j=0;
LinkList p=L;
printf("请输入要查找的数据序号:");
scanf("%d",&i);
while(p&&j<i){
p=p->next;
j++;
}
if(p==NULL||i==0){
printf("第%d个元素不存在!!!\n",i);
}
else
printf("第%d号元素为:%d\n",i,p->data);
}
9.单链表按值查找
void LocateElem(LinkList L){//按值查找
int i,j=1;
LinkList p=L->next;
printf("请输入要查找的数据:");
scanf("%d",&i);
while(p&&p->data!=i){
p=p->next;
j++;
}
if(p){
printf("要查找的数据编号为:%d\n",j);
printf("要查找的数据地址为:%p\n",p);
}
else{
printf("要查找的数据不存在!!!\n");
}
}
10.单链表的插入
在第i个结点前插入值为e的新结点。
LinkList InsertElem(LinkList L){//插入数据
LinkList p=L,s;
int i,j=0,e;
printf("请输入要插入的位置:");
scanf("%d",&i);
printf("请输入要插入的数据:");
scanf("%d",&e);
while(p&&j<i-1){
p=p->next;
j++;
}
if(!p||j>i-1){
printf("插入位置不合理!!\n");
}
else{
s=(LinkList)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
printf("插入成功!!\n");
}
return L;
}
11.单链表的删除
void DeleteElem(LinkList L){//删除第i个数据
LinkList p=L,q;
int i,j=0,e;
printf("请输入要删除的数据位置:");
scanf("%d",&i);
while(p&&j<i-1){
p=p->next;
j++;
}
if(!p||j>i-1||(!p->next&&j==0)){//.(!p->next&&j==0)表示要删除空表的第一个数据
printf("删除位置不合理!!\n");
}
else{
q=p->next;
p->next=q->next;
e=q->data;
free(q);
printf("删除成功,删除的数据为:%d\n",e);
}
}
12.单链表的打印
void printElem(LinkList L,int len){//打印单链表
LinkList p=L->next;
int i;
if(p==NULL){
printf("单链表已被清空!!!\n");
}
else{
printf("单链表:");
for(i=0;i<len;i++){
printf("%d-",p->data);
p=p->next;
}
printf("\n");
}
}
13.清空单链表
void ClearList(LinkList L){//清空单链表
LinkList p=L->next,q;
while(p){
q=p; //逻辑与销毁相同,起始位置不同
p=p->next;
free(q);
}
L->next=NULL;
printf("清空成功!!!\n");
}
14.销毁单链表
void DestoryList(LinkList* L){//销毁单链表
LinkList p=*L,q;
while(p){
q=p;
p=p->next;
free(q);
}
*L=NULL; //头指针置空,表示链表已销毁
printf("销毁成功!!!\n");
}
15.菜单
void menu(){
printf( "******************************************************************\n");
printf( "**************** 1.输入1 尾插法创建单链表 *******************\n");
printf( "**************** 2.输入2 头插法创建单链表 *******************\n");
printf( "**************** 3.输入3 求单链表长度 *******************\n");
printf( "**************** 4.输入4 判断单链表是否为空******************\n");
printf( "**************** 5.输入5 取单链表中第i个数据*****************\n");
printf( "**************** 6.输入6 按值查找 *******************\n");
printf( "**************** 7.输入7 插入数据 *******************\n");
printf( "**************** 8.输入8 打印单链表 *******************\n");
printf( "**************** 9.输入9 删除数据 *******************\n");
printf( "**************** 10.输入10 清空单链表 *******************\n");
printf( "**************** 11.输入11 销毁单链表 *******************\n");
printf( "**************** 12.输入12 退出程序 *******************\n");
printf( "******************************************************************\n");
}
16.主函数
int main()
{
menu();
LinkList L;
int N, c;
InitList(&L);//L=InitList(L);
printf("请输入要进行的操作序号:");
while (scanf_s("%d", &N)) {
if (N == 12) {
printf("已退出程序!!!\n");
DestoryList(&L);
break;
}
else {
switch (N) {
case 1:
CreateList_R(L);
while ((c = getchar()) != '\n' && c != EOF) {
// 清空输入缓冲区中的其他字符
}
break;
case 2:
CreateList_H(L);
while ((c = getchar()) != '\n' && c != EOF) {
// 清空输入缓冲区中的其他字符
}
break;
case 3:
ListEmpty(L);
printf("单链表长度为:%d\n", LengthList(L));
break;
case 4:
ListEmpty(L);
break;
case 5:
GetElem(L);
break;
case 6:
LocateElem(L);
break;
case 7:
InsertElem(L);
break;
case 8:
if (L == NULL) {
printf("单链表已被销毁!!!\n");
}
else {
int q = LengthList(L);
printElem(L, q);
}
break;
case 9:
DeleteElem(L);
break;
case 10:
ClearList(L);
break;
case 11:
DestoryList(&L);
printf("销毁成功!!!\n");
break;
default:
printf("您的输入有误,请重新输入!!!\n");
break;
}
printf("请输入要进行的操作序号:");
}
}
return 0;
}
总结
以上内容是对单链表的基本操作,包好对单链表的增删查。子函数中的输入和输出可根据需要放到主函数中。