一.作业
1.使用循环链表完成约瑟夫环问题
代码:
1.主函数
#include "sharen.h"
int main(int argc, char const *argv[])
{
int n,m,s=0;
printf("请输入总人数:");
scanf("%d",&n);
printf("请输入数到几死:");
scanf("%d",&m);
printf("请输入从第几个人开始:");
scanf("%d",&s);
shuchu(m,n,s);
return 0;
}
#ifndef SHAREN_H
#define SHAREN_H
#include <myhead.h>
typedef int detatype;
typedef struct Node
{
detatype data;
struct Node*next;
}Node,*Nodeptr;
//创建约瑟夫链表
Nodeptr list_create(int n);
//操作他们
void shuchu(int m,int n,int s);
#endif
#include "sharen.h"
//创建约瑟夫链表
Nodeptr list_create(int n)
{
Nodeptr L=(Nodeptr)malloc(sizeof(Node));
if (NULL==L)
{
printf("创建失败\n");
return NULL;
}
L->data=1;
L->next=L;
Nodeptr q=L;
for (int i = 1; i < n; i++)
{
Nodeptr p=(Nodeptr)malloc(sizeof(Node));
p->data=i+1;
p->next=L;
q->next=p;
q=p;
}
return L;
}
//操作他们
void shuchu(int m,int n,int s)
{
Nodeptr L=list_create(n);
printf("约瑟夫链表建立成功\n");
Nodeptr p=L;
int temp=0;
for (int i=0;i<s-1;i++)
{
p=p->next;
}
printf("约瑟夫序列:");
while (1)
{
for(int i=0;i<m-2;i++)
{
p=p->next;
}
printf("%d\t",p->next->data);
p->next = p->next->next;
p = p->next;
temp++;
if(temp==n)
break;
}
printf("\n");
}
代码结果:
2.使用栈,完成进制转换
输入:一个整数,进制数
输出:该数的对应的进制数
代码:
#include "erjingzhi.h"
int main(int argc, char const *argv[])
{
int n=0;
int k=0;
printf("请输入一个整数:");
scanf("%d",&n);
printf("请输入进制数:");
scanf("%d",&k);
stackptr S=stack_create();
stack_push(S,n,k);
stack_pop(S,k);
stack_destroy(S);
S=NULL;
return 0;
}
#ifndef EJZ_H
#define EJZ_H
#include <myhead.h>
#define MAX 20 //顺序栈的最大容量
typedef int datatype; //数据类型
//定义顺序栈的类型
typedef struct
{
datatype*data;//创建栈的容器 指向堆区空间
int top;
}stack,*stackptr;
//创建栈
stackptr stack_create();
//判空
int stack_empty(stackptr S);
//判满
int stack_full(stackptr S);
//入栈
void stack_push(stackptr S,int n,int k);
//出栈
void stack_pop(stackptr S,int k);
//销毁栈
void stack_destroy(stackptr S);
#endif
#include "erjingzhi.h"
//创建栈
stackptr stack_create()
{
//在堆区申请一个栈的大小
stackptr S=(stackptr)malloc(sizeof(stack));
if(NULL==S)
{
printf("创建失败\n");
return NULL;
}
//程序执行至此,表示栈申请出来了,但是存储栈的空间还没有申请
S->data=(datatype*)malloc(sizeof(datatype)*MAX);
if(NULL==S->data)
{
printf("创建失败\n");
return NULL;
}
//给空间内容s初始化
memset(S->data,0,sizeof(datatype)*MAX);
//栈顶元素变量初始化
S->top=-1;
printf("创建成功\n");
return S;
}
//判空
int stack_empty(stackptr S)
{
return S->top==-1;
}
//判满
int stack_full(stackptr S)
{
return S->top==MAX-1;
}
//入栈
void stack_push(stackptr S,int n,int k)
{
//判断逻辑
if(NULL==S||stack_full(S))
{
printf("入栈失败\n");
return ;
}
while (n)
{
S->top++;
S->data[S->top]=n%k;
n=n/k;
}
printf("这个整数的%d进制是:",k);
}
//出栈
void stack_pop(stackptr S,int k)
{
//判断逻辑
if(NULL==S||stack_empty(S))
{
printf("出栈失败\n");
return ;
}
while(S->top>-1)
{
if(k==16)
{
printf("%x",S->data[S->top]);
S->top--;
}else
{
printf("%d",S->data[S->top]);
S->top--;
}
}
printf("\n");
}
//销毁栈
void stack_destroy(stackptr S)
{
if(S!=NULL)
{
//销毁栈的容器
free(S->data);
S->data=NULL;
//销毁栈
free(S);
S=NULL;
}
printf("销毁成功\n");
}
代码结果:
二.笔记
一.双向链表
1.1 概念
1> 所谓双向链表,就是从任意一个节点既能存储其前驱节点信息也能存储后继节点信息
2> 节点结构体中增加一个指向前驱节点的指针
3> 头结点没有前驱,最后一个节点没有后继
1.2 创建双向链表
1> 参数:不需要,只需要从堆区申请出一个头结点大小的空间,就创建了一个双向链表
2> 返回值:返回堆区空间中头结点的地址即可
3> 注意:需要将头结点数据域初始化为0,两个指针域分别初始化为NULL
1.3 判空
1> 跟单向链表的判空一致,只需要判断头结点的指针域是否为空即可
1.4 申请节点封装数
1.5 头插
1> 参数:链表、要插入的元素
2> 返回值:int
3> 注意:对于空链表和非空链表的插入,有所不同,并且不能合并
1.6 链表遍历
1> 参数:链表
2> 返回值:int
3> 注意:需要使用一个遍历指针,从第一个节点出发,到最后一个节点
1.7 按位置查找节点
1> 参数:链表、要查找的位置
2> 返回值:成功返回找到的结点,失败返回NULL
1.8 任意位置删除
1> 参数:链表、要删除的位置
2> 返回值:int
3> 注意:对于双向链表的任意位置进行操作时(插入、删除),可以找到前驱结点处理也可以不用找到前驱结点
1.9 销毁链表
1> 参数:链表
2> 返回值:int
3> 注意:需要将所有的节点,全部删除后,然后删除头结点
1.10 代码汇总
1> doublelinklist.h
#ifndef DOUBLELINKLIST_H
#define DOUBLELINKLIST_H
#include<myhead.h>
typedef char datatype; //数据域的类型
//定义节点类型
typedef struct Node
{
union
{
int len; //头节点数据域
datatype data; //普通节点数据域
};
struct Node *prio; //前驱指针
struct Node *next; //后继指针
}Node, *NodePtr;
//创建双向链表
NodePtr list_create();
//链表判空
int list_empty(NodePtr L);
//申请节点封装数据
NodePtr apply_node(datatype e);
//链表头插
int list_insert_head(NodePtr L, datatype e);
//链表遍历
int list_show(NodePtr L);
//按位置查找返回节点
NodePtr list_search_pos(NodePtr L, int pos);
//链表任意位置删除
int list_delete_pos(NodePtr L, int pos);
//链表删除
void list_destroy(NodePtr L);
#endif
2> doublelinklist.c
#include"doublelinklist.h"
//创建双向链表
NodePtr list_create()
{
//在堆区申请一个头结点
NodePtr L = (NodePtr)malloc(sizeof(Node));
if(NULL == L) //if(L = NULL)
{
printf("创建失败\n");
return NULL;
}
//说明链表创建成功
L->len = 0; //链表长度为0
L->prio = NULL; //前驱指针为空
L->next = NULL; //后继指针为空
printf("创建成功\n");
return L;
}
//链表判空
int list_empty(NodePtr L)
{
return L->next == NULL;
}
//申请节点封装数据
NodePtr apply_node(datatype e)
{
//申请节点空间的大小
NodePtr p = (NodePtr)malloc(sizeof(Node));
if(NULL == p)
{
printf("节点申请失败\n");
return NULL;
}
//给节点赋值
p->data = e;
p->prio = NULL;
p->next = NULL;
return p;
}
//链表头插
int list_insert_head(NodePtr L, datatype e)
{
//判断逻辑
if(NULL == L)
{
printf("插入失败\n");
return -1;
}
//申请节点封装数据
NodePtr p = apply_node(e);
if(NULL==p)
{
return -1;
}
//头插逻辑
if(list_empty(L))
{ //链表为空时的插入逻辑
p->prio = L;
L->next = p;
}else
{
//链表非空时的插入
p->prio = L;
p->next = L->next;
L->next->prio = p; //p->next->prio=p;
L->next = p;
}
printf("插入成功\n");
L->len++; //链表长度变化
return 0;
}
//链表遍历
int list_show(NodePtr L)
{
//判断逻辑
if(NULL==L || list_empty(L))
{
printf("遍历失败\n");
return -1;
}
//遍历逻辑
NodePtr q = L->next; //从第一个节点出发
while(q)
{
//输出数据域
printf("%c\t", q->data);
//遍历指针后移
q = q->next;
}
printf("\n");
}
//按位置查找返回节点
NodePtr list_search_pos(NodePtr L, int pos)
{
//判断逻辑
if(NULL==L || list_empty(L)||pos<0 || pos>L->len)
{
printf("查找失败\n");
return NULL;
}
//查找逻辑
NodePtr q = L; //定义遍历指针从头结点出发
for(int i=0; i<pos; i++)
{
q = q->next;
}
//返回节点
return q;
}
//链表任意位置删除
int list_delete_pos(NodePtr L, int pos)
{
//判断逻辑
if(NULL==L || list_empty(L) || pos<1 || pos>L->len)
{
printf("删除失败\n");
return -1;
}
//找到要删除的节点
NodePtr q = list_search_pos(L, pos);
//删除逻辑
if(q->next == NULL)
{
//表示q为最后一个节点
q->prio->next = NULL;
}else
{
//上传下达
q->prio->next = q->next;
q->next->prio = q->prio;
}
free(q); //释放自己
q = NULL;
printf("删除成功\n");
//表长变化
L->len--;
return 0;
}
//链表删除
void list_destroy(NodePtr L)
{
//判断逻辑
if(NULL==L)
{
printf("删除失败\n");
return;
}
//删除所有节点
while(!list_empty(L))
{
list_delete_pos(L, 1);
}
//删除头结点
free(L);
L = NULL;
printf("链表释放成功\n");
}
3> main.c
#include"doublelinklist.h"
int main(int argc, const char *argv[])
{
//调用创建函数
NodePtr L = list_create();
if(NULL == L)
{
return -1;
}
//调用头插函数
list_insert_head(L, 'Q');
list_insert_head(L, 'W');
list_insert_head(L, 'E');
list_insert_head(L, 'R');
list_insert_head(L, 'D');
list_insert_head(L, 'F');
//调用遍历函数
list_show(L);
//调用任意位置删除函数
list_delete_pos(L, 1);
list_delete_pos(L, 3);
list_delete_pos(L, L->len);
list_show(L);
//释放链表
list_destroy(L);
L = NULL;
list_show(L);
return 0;
}
二.循环链表
2.1 概念
1> 循环链表,就是首尾相接的链表。
2> 单向循环链表:只需要将最后一个节点的指针域指向头结点即可
3> 双向循环链表:需要将最后一个阶段的指针域指向头结点,头结点的前驱指针指向最后一个阶段
三.操作受限的线性表
1> 在之前的内容,无论是顺序表还是链表,都是详细处理的线性表,既可以在端点处进行操作,也可以在中间位置操作
2> 现实生活中,有很多并不需要在中间进行操作的序列,只在端点处进行操作,这样的线性表,我们称为操作受限的线性表
3> 根据不同的受限情况,线性表分为:栈、队列
4> 栈:插入和删除操作都只允许在同一端进行。
队列:插入和删除操作都只允许在异端进行。
四.栈
4.1 栈的相关概念
1> 栈:操作受限的线性表,插入和删除只能在同一端进行,不能在中间进行相关操作
2> 允许操作的一端,称为栈顶,不允许操作的一端称为栈底
3> 特点:先进后出(后进先出)
例如:水杯容器、枪的弹夹
4> 栈的分类:根据存储方式的不同,分为顺序栈和链式栈
顺序栈:顺序存储的栈称为顺序栈
链式栈:链式存储的栈称为链式栈