链表的操作实现
1.单链表
(1)单链表结点的定义
//单链表结点的定义
//单链表中每个结点包含数据域和指针域
typedef struct Node
{
DataType data;
struct Node *next;
}SLNode;
(2)测试
LinList.h里面包含单链表的初始化、插入结点、删除结点、取数据元素、撤销单链表操作。
....................................................................................................LinList.h...............................................................................
//单链表结点的定义
//单链表中每个结点包含数据域和指针域
typedef struct Node
{
DataType data;
struct Node *next;
}SLNode;
//初始化ListInitiate(SLNode **head)
//在初始化操作前,头指针参数head没有具体的地址值,在初始化操作时,头指针
//参数head才得到了具体的地址值,而这个地址值要返回给调用函数,所以此时头指针参数head要
//设计成指针的指针类型。如果此时头指针参数head设计成指针类型,那么函数将无法得到在
//初始化函数中被赋值的头指针参数head的数值。
void ListInitiate(SLNode **head)
{
*head=(SLNode *)malloc(sizeof(SLNode));//申请头结点,由head指示其地址
(*head)->next=NULL; //置结束标记NULL
}
//求当前数据元素个数ListLength(SLNode *head)
int ListLength(SLNode *head)
{
SLNode *p=head;
int size=0;
while(p->next!=NULL)
{
p=p->next;
size++;
}
return size;
}
//插入ListInsert(SLNode *head,int i,DataType x)
//首先要在单链表中寻找到第i-1个结点并由指针p指示,然后动态申请一个结点存储空间并由指针q指示
//并把数据元素x的值赋予新结点的数据元素域(即q->data=x),最后修改新结点的指针域指向ai结点(即q->next=p->next),
//并修改ai-1结点的指针域使之指向新结点q(即p->next=q)
int ListInsert(SLNode *head,int i,DataType x)
{
//在带头结点的单链表head的第i(0<=i<=size)个结点前
//插入一个存放数据元素的x的结点。插入成功则返回1;失败则返回0;
SLNode *p,*q;
int j;
p=head;
j=-1;
while (p->next!=NULL&&j<i-1)
{
p=p->next;
j++;
}
if(j!=i-1)
{
printf("插入位置参数错!\n");
return 0;
}
q=(SLNode *)malloc(sizeof(SLNode));//生成新结点
q->data=x;
q->next=p->next;
p->next=q;
return 1;
}
//删除ListDelete(SLNode *head,int i,DataType *x)
int ListDelete(SLNode *head,int i,DataType *x)
{
//删除带头结点单链表head的第i(0<=i<=size-1)个结点
//被删除结点的数据域由x带回,删除成功则返回1,失败返回0
SLNode *p,*s;
int j;
p=head;
j=-1;
while(p->next!=NULL&&p->next->next!=NULL&&j<i-1)
{
p=p->next;
j++;
}
if(j!=i-1)
{
printf("删除位置出错!\n");
return 0;
}
s=p->next;
*x=s->data;
p->next=s->next;
free(s);
return 1;
}
//取数据元素ListGet(SLNode *head,int i,DataType *x)
int ListGet(SLNode *head,int i,DataType *x)
{
SLNode *p;
int j;
p=head;
j=-1;
while(p->next!=NULL&&j<i)
{
p=p->next;
j++;
}
if(j!=i)
{
printf("取元素位置参数出错!");
return 0;
}
*x=p->data;
return 1;
}
//和顺序表相比,单链表要增加一个撤销单链表操作,用来在调用程序退出前释放动态申请的内存空间
//因为单链表的结点空间是在程序运行时动态申请的,而系统只负责自动回收程序中静态分配的内存空间
void Destroy(SLNode **head)
{
SLNode *p,*p1;
p=*head;
while(p!=NULL)
{
p1=p;
p=p->next;
free(p1);
}
*head=NULL;
}
........................................................................................................mai.c.........................................................................................
#include<stdio.h>
#include<malloc.h>
typedef int DataType;
#include"LinList.h"
void main(void)
{
SLNode *head;
int i,x;
ListInitiate(&head);//初始化
for(i=0;i<10;i++)
ListInsert(head,i,i+1);
ListDelete(head,4,&x);
for(i=0;i<ListLength(head);i++)
{
ListGet(head,i,&x);
printf("%d ",x);
}
Destroy(&head);
system("pause");
}
2.循环单链表
循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针域不再是结束标记,而是指向整个链表的第一个结点,从而使整个链表形成一个环。
带头结点的循环单链表的操作实现方法和带头结点的单链表的操作实现方法类同,差别只是一下两点:
(1)在初始化函数中,把语句(*head)->next=NULL改为(*head)->next=*head。
(2)在其它函数中,循环判断条件p->next!=NULL和p->next->next!=NULL中的NULL改为头指针head。
3.双向链表
(1)双向循环链表的存储结构
//双向循环链表的存储结构
typedef struct Node{
DataType data;
struct Node *next;//后继指针
struct Node *prior;//前驱指针
}DLNode;
(2)初始化
void ListInitiate(DLNode **head)
{
*head=(DLNode *)malloc(sizeof(DLNode));//申请头结点,由head指示其地址
(*head)->prior=*head;
(*head)->next=*head;
}
(3)插入数据元素
int ListInsert(DLNode *head,int i,DataType x)
{
//在带头结点的双向循环链表head的第i(0<=i<=size)个结点前
//插入一个存放数据元素x的结点。插入成功则返回1;失败则返回0
DLNode *p,*s;
int j;
p=head->next;
j=0;
while (p!=head&&j<i)
{
p=p->next;
j++;
}
if(j!=i)
{
printf("插入位置参数错!\n");
return 0;
}
s=(DLNode *)malloc(sizeof(DLNode));//生成新结点
s->data=x;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
return 1;
}
(4)删除数据元素
int ListDelete(DLNode *head,int i,DataType *x)
{
//删除带头结点双向循环链表head的第i(0<=i<=size-1)个结点
//被删除结点的数据元素域由x带回。删除成功则返回1,失败则返回0
DLNode *p;
int j;
p=head->next;
j=0;
while(p->next!=head&&j<i)//寻找第i个结点
{
//和单链表相比,双向循环链表的删除算法指针p可以直接指在第i个结点上,而不需要让指针p指在第i-1个结点上
p=p->next;
j++;
}
*x=p->data;
if(j!=i)
{
printf("删除位置出错!\n");
return 0;
}
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
return 1;
}
(5)求当前数据元素个数
int ListLength(DLNode *head)
{
DLNode *p=head;//p指向头结点
int size=0;//size初始为0
while(p->next!=NULL){
p=p->next;
size++;
}
return size;
}
(6)撤销内存空间
void Destroy(DLNode **head)
{
DLNode *p,*p1;
int i,n=ListLength(*head);
p=*head;
for(i=0;i<=n;i++)
{
p1=p;
p=p->next;
free(p1);
}
*head=NULL;
}
4.单链表应用
题目:约瑟夫环问题模拟
问题描述:设编号为1,2,……n(n>0)的n个人按顺时针方向坐一圈,每人持有一个正整数密码。开始时,任意给出一个报数上限值m,从第一个人开始按顺时针方向顺序报数,报到m时停止报数,报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人起重新从1开始顺序报数;如此下去,直到所有人全部出列为止。
测试数据:n=7,7人的密码依次为3,1,7,2,4,8,4。初始报数上限为m=20。
.......................................................................................................SCLinList.h.........................................................................
typedef struct Node
{
DataType data;
struct Node *next;
}SLNode;
void SCLLInitiate(SLNode **head)//初始化
{
if((*head=(SLNode *)malloc(sizeof(SLNode)))==NULL)exit(1);
(*head)->next=*head;
}
int SCLLInsert(SLNode *head,int i,DataType x)//插入
{
SLNode *p,*q;
int j;
p=head->next;
j=1;
while(p!=head&&j<i-1)
{
p=p->next;
j++;
}
if(j!=i-1&&i!=1)
{
printf("插入参数出错!");
return 0;
}
if((q=(SLNode *)malloc(sizeof(SLNode)))==NULL)exit(1);
q->data=x;
q->next=p->next;
p->next=q;
return 1;
}
int ListDelete(SLNode *head,int i,DataType *x)//删除
{
SLNode *p,*q;
int j;
p=head;
j=0;
while(p->next!=head&&j<i-1)
{
p=p->next;
j++;
}
if(j!=i-1)
{
printf("删除参数出错!");
return 0;
}
q=p->next;
p->next=q->next;
*x=q->data;
free(q);
return 1;
}
int SCLLGet(SLNode *head,int i,DataType *x)//取数据元素
{
SLNode *p;
p=head;
int j=0;
while(p->next!=head&&j<i)
{
p=p->next;
j++;
}
if(j!=i)
{
printf("取元素位置参数出错!");
return 0;
}
*x=p->data;
return 1;
}
int SCLLNotEmpty(SLNode *head)//
{
if(head->next==head)return 0;
else return 1;
}
.......................................................................................................main.cpp....................................................................................
#include<stdio.h>
#include<stdlib.h>
typedef struct
{
int number;
int cipher;
}DataType; //定义具体的数据类型为DataType
#include "SCLinList.h" //包含头文件SCLinList
void SCLLDeleteAfter(SLNode *p) //删除p指针所指结点的下一个结点
{
SLNode *q=p->next;
p->next=p->next->next;
free(q);
}
void JesephRing(SLNode *head,int m)
//对带头结点单链表head,初始值为m的约瑟夫环问题函数
{
SLNode *pre,*curr;
int i;
pre=head;
curr=head->next;
while(SCLLNotEmpty(head)==1)
{
for(i=1;i<m;i++)
{
pre=curr;
curr=curr->next;
if(curr==head)
{
pre=curr;
curr=curr->next;
}
}
printf("%d ",curr->data.number);
m=curr->data.cipher;
curr=curr->next;
if(curr==head)curr=curr->next;
SCLLDeleteAfter(pre);
}
}
void main(void){
DataType test[7]={{1,3},{2,1},{3,7},{4,2},{5,4},{6,8},{7,4}};
int n=7,m=20,i;
SLNode *head;
SCLLInitiate(&head);//初始化
for(i=1;i<=n;i++)
SCLLInsert(head,i,test[i-1]);//循环插入建立单循环链表
JesephRing(head,m); //调用约瑟夫环问题函数
printf("\n");
system("pause");
}