一、链表与栈相关练习
1. 将双向链表和循环链表自己实现一遍,至少要实现创建、增、删、改、查、销毁工作
1.1双向链表
1.1.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_creatr();
//链表判空
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);
//链表任意位置修改
int list_update_pos(NodePtr L, int pos, datatype e);
//链表删除
void list_destroy(NodePtr L);
#endif
1.1.2 doublelinlist.c
#include "doublelinklist.h"
//创建双向链表
NodePtr list_creatr()
{
//创建头结点
NodePtr L = (NodePtr)malloc(sizeof(Node));
//判断逻辑
if(NULL == L)
{
printf("创建失败\n");
return NULL;
}
//头结点初始化
L->len = 0; //链表初始长度为0
L->prio = NULL; //头结点无前驱节点,防止野指针
L->next = NULL; //防止野指针
printf("创建成功\n");
return L;
}
//链表判空
int list_empty(NodePtr L)
{
return NULL == L->next;
}
//申请节点封装数据
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;
L->next = P;
}
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");
return 0;
}
//按位置查找返回节点
NodePtr list_search_pos(NodePtr L, int pos)
{
//判断逻辑
if(NULL==L || list_empty(L) || pos<0 || pos>L->len)
{
printf("查找失败\n");
return NULL;
}
//查找逻辑
NodePtr P = L->next;
for(int i=1; i<pos; i++) //遍历到pos所在位置
{
P = P->next;
}
return P;
}
//链表任意位置删除
int list_delete_pos(NodePtr L, int pos)
{
//判读逻辑
if(NULL==L || list_empty(L) || pos<0 || pos>L->len)
{
printf("删除失败\n");
return -1;
}
//删除逻辑
NodePtr Q = list_search_pos(L, pos);
if(pos < L->len)
{
Q->prio->next = Q->next;
Q->next->prio = Q->prio;
}
else
{
Q->prio->next = NULL;
}
free(Q);
Q = NULL; //防止野指针
L->len--; //表长变化
return 0;
}
//链表任意位置修改
int list_update_pos(NodePtr L, int pos, datatype e)
{
//判读逻辑
if(NULL==L || list_empty(L) || pos<0 || pos>L->len)
{
printf("修改失败\n");
return -1;
}
//修改逻辑
NodePtr Q = list_search_pos(L, pos);
Q->data = e;
return 0;
}
//链表删除
void list_destroy(NodePtr L)
{
//判断逻辑
if(NULL==L)
{
printf("删除失败\n");
return ;
}
//删除逻辑
while(L->next)
{
list_delete_pos(L, 1);
}
free(L);
L = NULL; //防止野指针
printf("删除成功\n");
return ;
}
1.1.3 main.c
#include "doublelinklist.h"
int main(int argc, const char *argv[])
{
//调用函数创建链表
NodePtr L = list_creatr();
//判断逻辑
if(L == NULL)
{
return -1;
}
//调用函数头插链表
list_insert_head(L, 'a');
list_insert_head(L, 'b');
list_insert_head(L, 'c');
list_insert_head(L, 'd');
list_insert_head(L, 'e');
//调用函数遍历链表
list_show(L);
//调用函数查找节点
NodePtr P = list_search_pos(L, 3);
printf("%c\n", P->data);
//调用函数任意位置删除
list_delete_pos(L, 1);
list_delete_pos(L, 3);
list_delete_pos(L, L->len);
list_show(L);
//调用函数任意位置修改
list_update_pos(L, 1, 'A');
list_update_pos(L, L->len, 'B');
list_show(L);
//调用函数删除链表
list_destroy(L);
L = NULL; //防止野指针
list_show(L);
return 0;
}
1.1.4 程序运行截图
![](https://i-blog.csdnimg.cn/direct/b846944217cf4f1092550e30c6772e03.png)
1.2循环链表
1.2.1 looplinklist.h
#ifndef LOOPLINKLIST_H
#define LOOPLINKLIST_H
#include <myhead.h>
//定义数据类型
typedef char datatype;
//定义节点类型
typedef struct Node
{
union
{
int len;
datatype data;
};
struct Node *next;
}Node, *NodePtr;
//创建循环链表
NodePtr list_creat();
//链表判空
int list_empty(NodePtr L);
//链表申请空间封装节点
NodePtr apply_node(datatype e);
//按位置进行查找
NodePtr list_search_pos(NodePtr L, int pos);
//链表尾插
int list_insert_tail(NodePtr L, datatype e);
//链表按位置修改
int list_update_pos(NodePtr L, int pos, datatype e);
//链表遍历
int list_show(NodePtr L);
//链表头删
int list_delete_head(NodePtr L);
//链表释放
void list_destroy(NodePtr L);
#endif
1.2.2 looplinklist.c
#include "looplinklist.h"
//创建循环链表
NodePtr list_creat()
{
//创建头结点,并在堆区申请空间
NodePtr L = (NodePtr)malloc(sizeof(Node));
if(NULL == L)
{
printf("创建失败\n");
return NULL;
}
//初始化
L->len = 0;
L->next = L; //头结点指针域指向自己
printf("创建成功\n");
return L;
}
//链表判空
int list_empty(NodePtr L)
{
return L == L->next;
}
//链表申请空间封装节点
NodePtr apply_node(datatype e)
{
//创建封装节点
NodePtr P = (NodePtr)malloc(sizeof(Node));
if(NULL == P)
{
printf("封装失败\n");
return NULL;
}
//节点封装
P->data = e;
P->next = NULL;
return P;
}
//按位置进行查找
NodePtr list_search_pos(NodePtr L, int pos)
{
//判断逻辑
if(NULL==L || pos<0 || pos>L->len)
{
printf("查找失败\n");
return NULL;
}
//查找逻辑
NodePtr P = L->next;
for(int i=1; i<pos; i++)
{
P = P->next;
}
return P;
}
//链表尾插
int list_insert_tail(NodePtr L, datatype e)
{
//判断逻辑
if(NULL == L)
{
printf("尾插失败\n");
return -1;
}
//尾插逻辑
NodePtr P = apply_node(e);
if(NULL == P)
{
return -1;
}
NodePtr Q = list_search_pos(L, L->len);
P->next = L;
Q->next = P;
L->len++;
return 0;
}
//链表按位置修改
int list_update_pos(NodePtr L, int pos, datatype e)
{
//判断逻辑
if(NULL == L || list_empty(L) || pos<0 || pos>L->len)
{
printf("修改失败\n");
return -1;
}
//修改逻辑
NodePtr P = list_search_pos(L, pos);
if(NULL == P)
{
printf("修改失败\n");
return -1;
}
P->data = e;
return 0;
}
//链表遍历
int list_show(NodePtr L)
{
//判断逻辑
if(NULL==L ||list_empty(L))
{
printf("遍历失败\n");
return -1;
}
//遍历逻辑
NodePtr P = L->next;
while(P != L)
{
printf("%c\t", P->data);
P = P->next;
}
printf("\n");
return 0;
}
//链表头删
int list_delete_head(NodePtr L)
{
//判断逻辑
if(NULL==L || list_empty(L))
{
printf("头删失败\n");
return -1;
}
//头删逻辑
NodePtr Q = L->next; //标记第一个节点
L->next = L->next->next;
free(Q);
Q = NULL; //防止野指针
L->len--; //表长变化
return 0;
}
//链表释放
void list_destroy(NodePtr L)
{
//判断逻辑
if(NULL == L)
{
printf("释放失败\n");
return ;
}
//释放逻辑
while(L != L->next)
{
list_delete_head(L);
}
free(L);
L = NULL; //防止野指针
printf("释放成功\n");
}
1.2.3 main.c
#include "looplinklist.h"
int main(int argc, const char *argv[])
{
//调用函数创建链表
NodePtr L = list_creat();
//调用函数尾插链表
list_insert_tail(L, 'Q');
list_insert_tail(L, 'W');
list_insert_tail(L, 'E');
list_insert_tail(L, 'R');
list_insert_tail(L, 'D');
list_insert_tail(L, 'F');
//调用函数遍历链表
list_show(L);
//调用函数头删链表
list_delete_head(L);
list_show(L);
list_delete_head(L);
list_show(L);
list_delete_head(L);
list_show(L);
list_delete_head(L);
list_show(L);
list_delete_head(L);
list_show(L);
//调用函数尾插链表
list_insert_tail(L, 'D');
list_insert_tail(L, 'R');
list_insert_tail(L, 'E');
list_insert_tail(L, 'W');
list_insert_tail(L, 'Q');
list_show(L);
//调用函数任意位置修改
list_update_pos(L, 1, 'A');
list_update_pos(L, 2, 'B');
list_update_pos(L, L->len, 'C');
list_show(L);
//调用函数释放链表
list_destroy(L);
L = NULL; //防止野指针
list_show(L);
return 0;
}
1.2.4 程序运行截图
![](https://i-blog.csdnimg.cn/direct/183ec396db154184ab8f6371b1d039b2.png)
2.使用循环链表完成约瑟夫环问题
2.1 looplinklist.h
#ifndef LOOPLINKLIST_H
#define LOOPLINKLIST_H
#include <myhead.h>
//定义数据类型
typedef int datatype;
//定义节点类型
typedef struct Node
{
union
{
int len;
datatype data;
};
struct Node *next;
}Node, *NodePtr;
//创建循环链表
NodePtr list_creat();
//链表判空
int list_empty(NodePtr L);
//链表申请空间封装节点
NodePtr apply_node(datatype e);
//按位置进行查找
NodePtr list_search_pos(NodePtr L, int pos);
//链表尾插
int list_insert_tail(NodePtr L, datatype e);
//链表按位置修改
int list_update_pos(NodePtr L, int pos, datatype e);
//链表遍历
int list_show(NodePtr L);
//链表头删
int list_delete_head(NodePtr L);
//链表释放
void list_destroy(NodePtr L);
//约瑟夫环问题
void josephus();
#endif
2.2 looplinklist.c
#include "looplinklist.h"
//创建循环链表
NodePtr list_creat()
{
//创建头结点,并在堆区申请空间
NodePtr L = (NodePtr)malloc(sizeof(Node));
if(NULL == L)
{
printf("创建失败\n");
return NULL;
}
//初始化
L->len = 0;
L->next = L; //头结点指针域指向自己
printf("创建成功\n");
return L;
}
//链表判空
int list_empty(NodePtr L)
{
return L == L->next;
}
//链表申请空间封装节点
NodePtr apply_node(datatype e)
{
//创建封装节点
NodePtr P = (NodePtr)malloc(sizeof(Node));
if(NULL == P)
{
printf("封装失败\n");
return NULL;
}
//节点封装
P->data = e;
P->next = NULL;
return P;
}
//按位置进行查找
NodePtr list_search_pos(NodePtr L, int pos)
{
//判断逻辑
if(NULL==L || pos<0 || pos>L->len)
{
printf("查找失败\n");
return NULL;
}
//查找逻辑
NodePtr P = L->next;
for(int i=1; i<pos; i++)
{
P = P->next;
}
return P;
}
//链表尾插
int list_insert_tail(NodePtr L, datatype e)
{
//判断逻辑
if(NULL == L)
{
printf("尾插失败\n");
return -1;
}
//尾插逻辑
NodePtr P = apply_node(e);
if(NULL == P)
{
return -1;
}
NodePtr Q = list_search_pos(L, L->len);
P->next = L;
Q->next = P;
L->len++;
return 0;
}
//链表按位置修改
int list_update_pos(NodePtr L, int pos, datatype e)
{
//判断逻辑
if(NULL == L || list_empty(L) || pos<0 || pos>L->len)
{
printf("修改失败\n");
return -1;
}
//修改逻辑
NodePtr P = list_search_pos(L, pos);
if(NULL == P)
{
printf("修改失败\n");
return -1;
}
P->data = e;
return 0;
}
//链表遍历
int list_show(NodePtr L)
{
//判断逻辑
if(NULL==L ||list_empty(L))
{
printf("遍历失败\n");
return -1;
}
//遍历逻辑
NodePtr P = L->next;
while(P != L)
{
printf("%d\t", P->data);
P = P->next;
}
printf("\n");
return 0;
}
//链表头删
int list_delete_head(NodePtr L)
{
//判断逻辑
if(NULL==L || list_empty(L))
{
printf("头删失败\n");
return -1;
}
//头删逻辑
NodePtr Q = L->next; //标记第一个节点
L->next = L->next->next;
free(Q);
Q = NULL; //防止野指针
L->len--; //表长变化
return 0;
}
//链表释放
void list_destroy(NodePtr L)
{
//判断逻辑
if(NULL == L)
{
printf("释放失败\n");
return ;
}
//释放逻辑
while(L != L->next)
{
list_delete_head(L);
}
free(L);
L = NULL; //防止野指针
printf("释放成功\n");
}
//约瑟夫环问题
void josephus()
{
//创建2个循环链表
NodePtr L = list_creat(); //存储问题
int sum = 0, //约瑟夫环总长度
size = 0; //间隔
printf("请输入约瑟夫环的长度:");
scanf("%d", &sum);
printf("请输入约瑟夫环的间隔:");
scanf("%d", &size);
for(int i=1; i<=sum; i++)
{
//将约瑟夫环的数据插入链表
list_insert_tail(L, i);
}
//遍历当前约瑟夫环
list_show(L);
//问题解决逻辑
NodePtr Q = L;
printf("约瑟夫环求解后的顺序为:\n");
while(!list_empty(L))
{
for(int i=1; i<size; i++)
{
if(Q->next == L)
{
Q = L->next;
}
else
{
Q = Q->next;
}
}
if(1 == L->len)
{
printf("%d\n", L->next->data);
list_destroy(L);
L = NULL;
break;
}
else if(Q->next == L)
{
Q = L->next;
NodePtr H = Q->next;
printf("%d\t", H->data);
Q->next = H->next;
free(H);
H = NULL;
L->len--;
}
else
{
NodePtr H = Q->next;
printf("%d\t", H->data);
Q->next = H->next;
free(H);
H = NULL;
L->len--;
}
}
printf("\n");
}
2.3 main.c
#include "looplinklist.h"
int main(int argc, const char *argv[])
{
//调用约瑟夫环求解函数
josephus();
return 0;
}
2.4 程序运行截图
3.使用栈,完成进制转换,输入:一个整数;进制数,输出:该数的对应的进制数
3.1 seqstack.h
#ifndef SEQSTACK_H
#define SEQSTACK_H
#include <myhead.h>
typedef char datatype; //数据类型
#define MAX 31 //顺序栈的最大容量
//定义顺序栈的类型
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, datatype e);
//出栈
void stack_pop(StackPtr S);
//遍历栈
void stack_show(StackPtr S);
//获取栈顶元素地址
datatype* stack_get_top(StackPtr S);
//求栈的大小
int stack_size(StackPtr S);
//释放栈
void stack_destroy(StackPtr S);
//进制转换函数
void decimal_conversion_stack();
#endif
3.2 seqstack.c
#include "seqstack.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)
{
printf("创建失败\n");
return NULL;
}
//memset(S->data, 0, sizeof(datatype)*MAX);
bzero(S->data, sizeof(datatype)*MAX);
S->top = -1;
printf("创建成功\n");
return S;
}
//判空
int stack_empty(StackPtr S)
{
return -1 == S->top;
}
//判满
int stack_full(StackPtr S)
{
return MAX-1 == S->top;
}
//入栈
void stack_push(StackPtr S, datatype e)
{
//判断逻辑
if(NULL==S || stack_full(S))
{
printf("入栈失败\n");
return ;
}
//入栈逻辑
S->top++;
S->data[S->top] = e;
}
//出栈
void stack_pop(StackPtr S)
{
//判断逻辑
if(NULL==S || stack_empty(S))
{
printf("出栈失败\n");
return ;
}
//出栈逻辑
printf("%d出栈成功\n", S->data[S->top]);
S->top--;
}
//遍历栈
void stack_show(StackPtr S)
{
//判断逻辑
if(NULL==S || stack_empty(S))
{
printf("遍历失败\n");
return ;
}
//遍历逻辑
for(int i=S->top; i>-1; i--)
{
printf("%c\t", S->data[i]);
}
printf("\n");
}
//获取栈顶元素地址
datatype* stack_get_top(StackPtr S)
{
//判断逻辑
if(NULL==S || stack_empty(S))
{
printf("获取失败\n");
return NULL;
}
return &S->data[S->top];
}
//求栈的大小
int stack_size(StackPtr S)
{
//判断逻辑
if(NULL==S)
{
printf("求大小失败\n");
return -1;
}
return S->top+1;
}
//释放栈
void stack_destroy(StackPtr S)
{
//判断逻辑
if(NULL==S)
{
printf("释放失败\n");
return ;
}
//释放逻辑
free(S->data);
free(S);
S = NULL;
}
//进制转换函数
void decimal_conversion_stack()
{
int num = 0, //定义一个十进制整数
decimal = 0; //定义一个进制数
char remainder = '0'; //定义一个余数
//创建栈用于储存转换后的进制数
StackPtr S = stack_create();
//输入数据
while(num<0 || decimal<2)
{
printf("请输入一个正整数:");
scanf("%d", &num);
printf("请输入您需要转换的进制数:");
scanf("%d", &decimal);
if(num<0 || decimal<2)
{
printf("输入错误,请重新输入\n");
}
}
//转换逻辑
while(num)
{
remainder = (num % decimal); //求余数
num /= decimal; //求商
//当大于十进制时
if(remainder > 9)
{
remainder += 55;
}
else
{
remainder += 48;
}
//余数入栈
stack_push(S, remainder);
}
printf("%d的%d进制数为:\n", num, decimal);
//遍历栈
stack_show(S);
//销毁栈
stack_destroy(S);
S = NULL; //防止野指针
}
3.3 main.c
#include "seqstack.h"
int main(int argc, const char *argv[])
{
//调用函数创建栈
StackPtr S = stack_create();
if(NULL == S)
{
return -1;
}
//调用函数入栈
stack_push(S, 5+48);
stack_push(S, 2+48);
stack_push(S, 0+48);
stack_push(S, 1+48);
stack_push(S, 3+48);
stack_push(S, 1+48);
stack_push(S, 4+48);
//调用函数遍历栈
stack_show(S);
//调用函数出栈
stack_pop(S);
stack_pop(S);
stack_pop(S);
stack_pop(S);
stack_show(S);
//调用函数求栈的大小
int size = stack_size(S);
printf("栈的大小为:%d\n", size);
//调用函数释放栈
stack_destroy(S);
S = NULL;
stack_show(S);
//进制转换函数
int mune = 1;
while(mune)
{
decimal_conversion_stack();
printf("0退出,1继续:");
scanf("%d", &mune);
printf("\n");
}
return 0;
}
2.4 程序运行截图
![](https://i-blog.csdnimg.cn/direct/c2c178e7e6104426b6b1ef5c218f9674.png)
二、链表、栈相关知识思维导图
1.双向链表
![](https://i-blog.csdnimg.cn/direct/80f2ac5a065f486787ce8cff20631f08.png)
2.循环链表
![](https://i-blog.csdnimg.cn/direct/2d3a22e87007489c902a10da4a04a6ed.png)
3.顺序栈
![](https://i-blog.csdnimg.cn/direct/a3602f8ded6d4323a7f3d0ddca4a51f4.png)