了解链表
啥叫链表
链表有这么几个特点:
1.链表是一种物理存储单元上非连续、非顺序的存储结构。也就是说,链表的数据存放并不是按照顺序存放的,也不能使用访问一节点相邻的节点的地址来获取逻辑上节点附近的数据。
那么该如何对某个节点进行操作呢?
那就要看他的第二个特点。
2.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。如下图:这是一个单链表的基本模型。
那么根据上面链表的特性,我们可以尝试建立链表。
在建立之前我们首先要具备以下几个知识。
- 结构体
- 指针
- stdlib库函数 malloc函数(可以帮助我们动态的申请一块内存)free函数
(链表实质上就是结构体与指针的一个综合应用的实例。)
建立链表
1.需要的变量
1>存放数据的位置 2>指向下一个地址的指针
可以用结构体来实现
typedef struct Node{
int value;//存放数据
struct Node *next;//指向下一地址 由于需要建立多个节点 所以用递归的思想定义指针
}List;
2.定义头指针,定义节点
(如过不明白为什么要定义一个头指针请看上面的图)
1.头插法(反向建立)
每录入一个值都放在头指针的后面
List *createfirst()
{
List *head,*list;//定义头结点 普通节点
head=(List*)malloc(sizeof(List));//建立一个空链表
head->next=NULL;
int x;
scanf("%d",&x);
while(x!=-1)//录入数据终止条件 可根据需要修改
{
list=(List*)malloc(sizeof(List));//建立新节点
list->value=x;
list->next=head->next;//将头指针指向的区域赋给新节点
head->next=list;//将新节点的地址赋给头指针
scanf("%d",&x);
}
return head;
}
2.尾插法
每录入一个数据放在最后一个节点之后
List *createlast()
{
List *head,*list,*end;//
head=(List*)malloc(sizeof(List));
head->next=NULL;
end=head;
int x;
scanf("%d",&x);
while(x!=-1)
{
list=(List*)malloc(sizeof(List));
list->value=x;
end->next=list;
end=list;
scanf("%d",&x);
}
end->next=NULL;
return head;
}
3.总体效果
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
int value;
struct Node *next;
}List;
List *create();
int main()
{
List *list=create();
}
List *createlast()
{
List *head,*list,*end;
head=(List*)malloc(sizeof(List));
head->next=NULL;
end=head;
int x;
scanf("%d",&x);
while(x!=-1)
{
list=(List*)malloc(sizeof(List));
list->value=x;
end->next=list;
end=list;
scanf("%d",&x);
}
end->next=NULL;
return head;
}
4.遍历
while(list->next!=NULL)
{
list=list->next;
printf("%d\n",list->value);
}
因为第一个地址是头指针,所以其数据不需要访问。
循环链表
循环链表中,将单链表中的终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,使得链表的头尾相接,这种链表称之为单循环链表,简称为循环链表。
循环链表和单链表的主要差异就在于循环的判断条件上,单链表判断循环结束为:node->next==NULL;而循环链表判断循环结束为:(node->next)等于头结点。
List *createlast()
{
List *head,*list,*end;
head=(List*)malloc(sizeof(List));
head->next=NULL;
end=head;
int x;
scanf("%d",&x);
while(x!=-1)
{
list=(List*)malloc(sizeof(List));
list->value=x;
end->next=list;
end=list;
scanf("%d",&x);
}
end=list;//循环链表与单链表主要的区别
end->next=head->next;//循环链表与单链表主要的区别
return head;
}
应用(约瑟夫环)
来历
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样能确定被丢下去的人最开始的序号。
当然我们也可以将其推广到一般形式,总共有sum个人,数value个数,剩n个人。
递归解法
我们先假设有sum个人,value代表每次数的数,我们会发现,从第一次循环开始,到最后一次循环(循环到目标剩余人数)其中本次的sum是上一次sum-1的结果,value一直不变,这不是也可以用递归的思路去解决?
那我们就可以先尝试写一下代码
void ysfh(int sum,int value)
{
if(sum==1)//终止条件
{
return 0;
}
else
{
return (ysfh(sum-1,value)+value)%sum; //至于为什么要做这个操作 小伙伴可以带几个数进去试一试就知道了
}
}
链表解法
#include <stdio.h>
#include <stdlib.h>
struct Node{
int data;
struct Node *next;
};
typedef struct Node link;
int main(){
int m, n, i;
scanf("%d%d", &m, &n);
link *t, *x;
t = (link *)malloc(sizeof(link));
t -> data = 1;
t -> next = t;
x = t;
for(i = 2; i <= m; i++){
x -> next = (link*)malloc(sizeof(link));
x = x -> next;
x -> data = i;
x -> next = t;
}
while(m != 1){
for(i = 1; i < n; i++){
x = x -> next;
}
x -> next = x -> next -> next;
m--;
}
printf("%d", x -> data);
return 0;
}