当前位置: 首页 > news >正文

C语言数据结构(4)单链表专题2.单链表的应用

1. 链表经典算法——OJ题目

1.1 单链表相关经典算法OJ题1:移除链表元素

1.2 单链表相关经典算法OJ题2:反转链表

1.3 单链表相关经典算法OJ题3:合并两个有序链表

1.4 单链表相关经典算法OJ题4:链表的中间结点

1.5 循环链表经典应用-环形链表的约瑟夫问题

著名的Josephus问题

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在 第16个与第31个位置,于是逃过了这场死亡游戏。

1.6 单链表相关经典算法OJ题5:分割链表


1.1 移除链表元素

没要求在原地完成移除元素操作。

新链表不创建新的空间,只需要创建两个头尾指针,辅助完成链表内部结点指向的更改。

1.2 反转链表

思路1:遍历+头插

思路2:三指针

传递给head的实参在函数调用结束后,依然指向Node1——传值调用。

需要用在主调函数内使用主调函数内的head,接收函数调用的返回值。

1.3 合并两个有序链表

思路1:将L2插入到L1中,过程中的控制比较复杂。

思路2:创建新链表L3,谁小谁尾插。

1.4 链表的中间结点

双指针法:源宿指针、快慢指针、……

1.5 环形链表的约瑟夫问题

图解分析。

1.5.1 新建结点

typedef struct ListNode ListNode;
ListNode* BuyNode(int x){ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));//可写可不写,一般不会申请失败//if(newNode == NULL){//    exit(1);//}newNode->val = x;newNode->next = NULL;return newNode;
}

1.5.2 新建环形链表

ListNode* createList(int n){ListNode* phead = BuyNode(1);ListNode* ptail = phead;for (int i = 2; i <= n; i++){ptail->next = BuyNode(i);ptail = ptail->next;}//链表要首尾相连,使其循环起来ptail->next = phead;return phead;
}

1.5.3 约瑟夫问题

int ysf(int n, int m ) {//创建不带头单向循环链表ListNode* head = createList(n);ListNode* pcur = head;ListNode* prev = NULL;//count==m就删除当前结点,并重置count,继续遍历递增int count = 1;                  //一开始pcur指向head,count就应该数了1//逢m删除节点while (pcur->next != pcur) {    //循环条件:不止一个结点if(count == m){//删除当前节点pcur,prev->next = pcur->next;free(pcur);//删除pcur节点之后,要让pcur走到新的位置,count置为初始值pcur = prev->next;count = 1;}else{//pcur往后走prev = pcur;pcur = pcur->next;count++;}}//此时pcur结点就是幸存下来的唯一的一个节点return pcur->val;
}

提交代码。

但是自测运行。

问题:若m = 1时,第一次进循环就要删除pcur,而此时prev还是为空?

解决办法: createList方法不返回头结点,而是返回尾结点。既能知道第一个节点的前驱节点,也能够通过尾结点找到第一个节点。

1.6 分割链表

思路1:创建一个新链表,将原链表中小于x的结点头插,大于等于x的结点尾插。

思路2:创建两个新链表,一个大链表,一个小链表,遍历插入完后,小链表尾插大链表。

数组的分割可以从两边向中间遍历,左下标遇到大于等于x的就停下,右下标遇到小于x的就停下,然后交换数据,继续往中间走,while(left < right)。

2. 基于单链表再实现通讯录项目

2.1 单链表头文件

//SList.h
//
// Created by mm on 2023/6/13.
//
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"contact.h"
typedef struct PersonInfo SLTDataType;
//typedef int SLTDataType;void SLTPrint(SLTNode* phead);
//头部插⼊删除/尾部插⼊删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);

2.2 通讯录头文件


//contact.h
#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SListNode contact;
//用户数据
typedef struct PersonInfo
{char name[NAME_MAX];char sex[SEX_MAX];int age;char tel[TEL_MAX];char addr[ADDR_MAX];
}PeoInfo;//初始化通讯录
void InitContact(contact** con);
//添加通讯录数据
void AddContact(contact** con);
//删除通讯录数据
void DelContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);

2.3 通讯录源文件


//contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
#include"SList.h"void LoadContact(contact** con) {FILE* pf = fopen("contact.txt", "rb");if (pf == NULL) {perror("fopen error!\n");return;}//循环读取文件数据PeoInfo info;while (fread(&info, sizeof(info), 1, pf)){SLTPushBack(con, info);}printf("历史数据导入通讯录成功!\n");
}void InitContact(contact** con) {LoadContact(con);
}void AddContact(contact** con) {PeoInfo info;printf("请输入姓名:\n");scanf("%s", &info.name);printf("请输入性别:\n");scanf("%s", &info.sex);printf("请输入年龄:\n");scanf("%d", &info.age);printf("请输入联系电话:\n");scanf("%s", &info.tel);printf("请输入地址:\n");scanf("%s", &info.addr);SLTPushBack(con, info);printf("插入成功!\n");
}contact* FindByName(contact* con, char name[]) {contact* cur = con;while (cur){if (strcmp(cur->data.name, name) == 0) {return cur;}cur = cur->next;}return NULL;
}void DelContact(contact** con) {char name[NAME_MAX];printf("请输⼊要删除的用户姓名:\n");scanf("%s", name);contact* pos = FindByName(*con, name);if (pos == NULL) {printf("要删除的用户不存在,删除失败!\n");return;}SLTErase(con, pos);printf("删除成功!\n");
}void ShowContact(contact* con) {printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "联系电话",contact* cur = con;while (cur){printf("%-10s %-4s %-4d %15s %-20s\n",cur->data.name,cur->data.sex,cur->data.age,cur->data.tel,cur->data.addr);cur = cur->next;}
}void FindContact(contact* con) {char name[NAME_MAX];printf("请输⼊要查找的用户姓名:\n");scanf("%s", name);contact* pos = FindByName(con, name);if (pos == NULL) {printf("要查找的用户不存在,查找失败!\n");return;}printf("查找成功!\n");printf("%-10s %-4s %-4d %15s %-20s\n",pos->data.name,pos->data.sex,pos->data.age,pos->data.tel,pos->data.addr);
}void ModifyContact(contact** con) {char name[NAME_MAX];printf("请输⼊要修改的用户名称:\n");scanf("%s", &name);contact* pos = FindByName(*con, name);if (pos == NULL) {printf("要查找的用户不存在,修改失败!\n");return;}printf("请输入要修改的姓名:\n");scanf("%s", pos->data.name);printf("请输入要修改的性别:\n");scanf("%s", pos->data.sex);printf("请输入要修改的年龄:\n");scanf("%d", &pos->data.age);printf("请输入要修改的联系电话:\n");scanf("%s", pos->data.tel);printf("请输入要修改的地址:\n");scanf("%s", pos->data.addr);printf("修改成功!\n");
}void SaveContact(contact* con) {FILE* pf = fopen("contact.txt", "wb");if (pf == NULL) {perror("fopen error!\n");return;}//将通讯录数据写入文件contact* cur = con;while (cur){fwrite(&(cur->data), sizeof(cur->data), 1, pf);cur = cur->next;}printf("通讯录数据保存成功!\n");
}void DestroyContact(contact** con) {SaveContact(*con);SListDesTroy(con);
}

http://www.dtcms.com/a/311313.html

相关文章:

  • 什么是需量跟随
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现道路上头盔的检测识别(C#代码,UI界面版)
  • Linux 嵌入式开发全流程
  • C语言(长期更新)第7讲:VS实用调试技巧
  • LVGL + ESP-Brookesia 在Windows下的编译和运行
  • Elasticsearch+Logstash+Filebeat+Kibana单机部署
  • 卡尔曼滤波轨迹跟踪算法与MATLAB实现
  • GitHub 趋势日报 (2025年08月01日)
  • Kubernetes Service 全面详解:从概念到实践
  • 前端Canvas实现图片水印全攻略:从基础到高级防伪技术
  • 解决mac在安装nvm过程中可能遇到的一些问题
  • 深度学习核心:卷积神经网络 - 原理、实现及在医学影像领域的应用
  • 【读论文】从Qwen3技术报告到Qwen3-30B-A3B 模型的深度解读
  • 我的世界进阶模组教程——物品(2)
  • sqli-labs:65个关卡的文章汇总
  • 2025牛客多校第六场 D.漂亮矩阵 K.最大gcd C.栈 L.最小括号串 个人题解
  • C++冰箱管理实战代码
  • 【Flutter3.8x】flutter从入门到实战基础教程(八):公共state的集中管理机制
  • 人工智能开发框架 08. MNIST手写数字识别任务(一)
  • Java基础——实现图书管理系统交互功能
  • Hyper-V + Centos stream 9 搭建K8s集群(一)
  • HTTP数据请求
  • 涉水救援机器人cad【12张】三维图+设计书明说
  • 【网络原理】HTTP协议(一)
  • 【LeetCode 热题 100】(四)子串
  • leetcode热题——组合
  • 【10】VisionMaster入门到精通——脚本打印日志到本地(获取条码和二维码信息)
  • React ahooks——副作用类hooks之useThrottleEffect
  • 易华路副总经理兼交付管理中心部门经理于江平受邀PMO大会主持人
  • Cursor 与 VS Code 与 GitHub Copilot 的全面比较