C语言对单链表所有操作与一些相关面试题
目录
单链表的特性
单链表的所有操作
定义一个单链表
创建一个链表头
插入数据(头插法)
插入数据(尾插法)
查找节点
修改数据节点
删除节点
打印数据
销毁链表
翻转链表
打印链表长度
冒泡排序
快排
堆排
查找倒数第K个节点(双指针法)
完整测试代码
单链表的特性
单链表是一种线性数据结构,它由一系列的节点组成,每个节点包含一个数据域和一个指向下一个节点的指针域。单链表的特性有:
- 单链表的长度是可变的,可以动态地插入和删除节点。
- 单链表的访问是顺序的,要访问某个节点,必须从头节点开始遍历,直到找到该节点或者到达链表尾部。
- 单链表不需要连续的内存空间,可以利用零散的空间存储数据。
- 单链表的优势是:
- 插入和删除操作比较简单,只需要修改指针域即可,不需要移动其他节点。
- 单链表可以实现一些特殊的功能,如栈、队列、循环链表等。
- 单链表的劣势是:
- 访问操作比较慢,需要遍历整个链表,时间复杂度为O(n)。
- 单链表需要额外的空间存储指针域,增加了空间开销。
- 单链表容易产生内存碎片,如果频繁地插入和删除节点,可能导致内存不连续。
单链表的所有操作
定义一个单链表
// 声明并定义个单链表结构体
typedef struct _ListNode
{int val; //数据 成员变量struct _ListNode * next;//结构体调用自己的类型
}ListNode;
创建一个链表头
void listCreate(ListNode *node)
{//初始化链表内数据node->val = -1;node->next = NULL;
}
插入数据(头插法)
void listInsert(ListNode *node, int data)
{// 创建一个节点,并申请内存ListNode *t_node = (ListNode *)malloc(sizeof(ListNode));// 节点内容赋值t_node->val = data;// 头插法,新数据在前t_node->next = node->next;node->next = t_node;
}
插入数据(尾插法)
void listTailInsert(ListNode *node, int data)
{// 创建一个节点ListNode *t_node = (ListNode*)malloc(sizeof(ListNode));// 节点内容赋值t_node->val = data;t_node->next = NULL;// 声明一个尾节点ListNode* t_tail = node;// 获取最后一个节点while(t_tail->next != NULL){// 后移t_tail = t_tail->next;}//添加节点t_tail->next = t_node;
}
查找节点
ListNode* listFind(ListNode *node, int data)
{//申明一个空节点ListNode *t_node = NULL;//遍历链表ListNode *t_temp;for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next){//如果找到该节点if(t_temp->val == data){t_node = t_temp;//跳出循环break;}}return t_node;
}
修改数据节点
void listModify(ListNode *node, int oldData, int newData)
{// 查找值是否存在ListNode *t_node = listFind(node, oldData);// 判断值是否存在if(t_node == NULL){printf("该值不存在\n");return;}t_node->val = newData;
}
删除节点
void listDelete(ListNode *node, int data)
{// 查找是否存在改制的数据ListNode *t_node = listFind(node, data);// 如果该值对应的节点不存在if(NULL == t_node){printf("该值不存在\n");return;}// 求出被删节点的前一个节点ListNode *t_prev = node;// 遍历链表while(t_prev->next != t_node){t_prev = t_prev->next;}// 前一个节点的next指向被删除节点的下一个节点t_prev->next = t_node->next;// 释放内存free(t_node);// 指针置空t_node = NULL;
}
打印数据
void listDisplay(ListNode *node)
{// 遍历链表ListNode *t_temp;for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next){printf("%d ",t_temp->val);}printf("\n");
}
销毁链表
void listDestroy(ListNode *node)
{// 遍历链表ListNode *t_temp = node->next;while(t_temp != NULL){// 先将当前节点保存ListNode *t_node = t_temp;// 移动到下一各节点t_temp = t_temp->next;// 释放保存内容的节点free(t_node);}
}
翻转链表
void listReverse(ListNode *node)
{ListNode * head = NULL, *now = NULL, *temp = NULL;head = node->next;// head是来保存我们翻转以后链表中的头节点的now = head->next;// now用来保存我们当前待处理的节点head->next = NULL;// 一定要置为NULL,否则可能导致循环while(now){temp = now->next; // 利用一个临时指针来保存下一个待处理的节点now->next = head; // 将当前节点插入到逆序节点的第一个节点之前,并更改head指向head = now;node->next = head; // 使链表头指针指向逆序后的第一个节点now = temp; // 更新链表到下一个待处理的节点}
}
打印链表长度
int listLength(ListNode *node)
{ListNode *t_temp;int t_length = 0;for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next){t_length++;}return t_length;
}
冒泡排序
void listBubbleSort(ListNode *node)
{int t_length = listLength(node);int i,j;ListNode *t_temp;for(i = 0; i < t_length; i++){t_temp = node->next;for(j = 0;j < t_length - i - 1; j++){if(t_temp->val > t_temp->next->val){int t_data = t_temp->val;t_temp->val = t_temp->next->val;t_temp->next->val = t_data;}t_temp = t_temp->next;}}
}
快排
void quickSort(struct _ListNode *head, struct _ListNode *tail) {// 如果链表为空或只有一个节点,直接返回if (head == NULL || head == tail) return;// 定义两个指针p和q,用于分割链表struct _ListNode *p = head, *q = head->next;// 选取第一个节点作为基准值int pivot = head->val;// 遍历链表,将小于基准值的节点放到p的后面while (q != tail->next) {if (q->val < pivot) {p = p->next;// 交换p和q指向的节点的值int temp = p->val;p->val = q->val;q->val = temp;}q = q->next;}// 交换head和p指向的节点的值,使得p指向的节点为基准值int temp = head->val;head->val = p->val;p->val = temp;// 对左右两部分递归进行快速排序quickSort(head, p);quickSort(p->next, tail);
}
堆排
// 待实现
查找倒数第K个节点(双指针法)
ListNode* listFindKthToTail(ListNode *node, int k)
{// 超过长度直接返回空if(node == NULL || k >= listLength(node))return NULL;ListNode *first = node, *second = node;for(int i = 0; i < k; i++){first = first->next;}while (first){first = first->next;second = second->next;}return second;
}
完整测试代码
#include <stdio.h>
#include <stdlib.h>// 声明并定义个单链表结构体
typedef struct _ListNode
{int val; //数据 成员变量struct _ListNode * next;//结构体调用自己的类型
}ListNode;/*** 创建链表
*/
void listCreate(ListNode *node)
{//初始化链表内数据node->val = -1;node->next = NULL;
}/*** 插入数据,头插法
*/
void listInsert(ListNode *node, int data)
{// 创建一个节点,并申请内存ListNode *t_node = (ListNode *)malloc(sizeof(ListNode));// 节点内容赋值t_node->val = data;// 头插法,新数据在前t_node->next = node->next;node->next = t_node;
}/*** 插入数据,尾插法
*/
void listTailInsert(ListNode *node, int data)
{// 创建一个节点ListNode *t_node = (ListNode*)malloc(sizeof(ListNode));// 节点内容赋值t_node->val = data;t_node->next = NULL;// 声明一个尾节点ListNode* t_tail = node;// 获取最后一个节点while(t_tail->next != NULL){// 后移t_tail = t_tail->next;}//添加节点t_tail->next = t_node;
}/*** 查找数据
*/
ListNode* listFind(ListNode *node, int data)
{//申明一个空节点ListNode *t_node = NULL;//遍历链表ListNode *t_temp;for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next){//如果找到该节点if(t_temp->val == data){t_node = t_temp;//跳出循环break;}}return t_node;
}/*** 修改数据
*/
void listModify(ListNode *node, int oldData, int newData)
{// 查找值是否存在ListNode *t_node = listFind(node, oldData);// 判断值是否存在if(t_node == NULL){printf("该值不存在\n");return;}t_node->val = newData;
}/*** 删除数据
*/
void listDelete(ListNode *node, int data)
{// 查找是否存在改制的数据ListNode *t_node = listFind(node, data);// 如果该值对应的节点不存在if(NULL == t_node){printf("该值不存在\n");return;}// 求出被删节点的前一个节点ListNode *t_prev = node;// 遍历链表while(t_prev->next != t_node){t_prev = t_prev->next;}// 前一个节点的next指向被删除节点的下一个节点t_prev->next = t_node->next;// 释放内存free(t_node);// 指针置空t_node = NULL;
}/*** 打印数据
*/
void listDisplay(ListNode *node)
{// 遍历链表ListNode *t_temp;for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next){printf("%d ",t_temp->val);}printf("\n");
}/*** 销毁链表
*/
void listDestroy(ListNode *node)
{// 遍历链表ListNode *t_temp = node->next;while(t_temp != NULL){// 先将当前节点保存ListNode *t_node = t_temp;// 移动到下一各节点t_temp = t_temp->next;// 释放保存内容的节点free(t_node);}
}/*** 翻转链表
*/
void listReverse(ListNode *node)
{ListNode * head = NULL, *now = NULL, *temp = NULL;head = node->next;// head是来保存我们翻转以后链表中的头节点的now = head->next;// now用来保存我们当前待处理的节点head->next = NULL;// 一定要置为NULL,否则可能导致循环while(now){temp = now->next; // 利用一个临时指针来保存下一个待处理的节点now->next = head; // 将当前节点插入到逆序节点的第一个节点之前,并更改head指向head = now;node->next = head; // 使链表头指针指向逆序后的第一个节点now = temp; // 更新链表到下一个待处理的节点}
}/*** 求长度
*/
int listLength(ListNode *node)
{ListNode *t_temp;int t_length = 0;for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next){t_length++;}return t_length;
}/*** 冒泡排序
*/
void listBubbleSort(ListNode *node)
{int t_length = listLength(node);int i,j;ListNode *t_temp;for(i = 0; i < t_length; i++){t_temp = node->next;for(j = 0;j < t_length - i - 1; j++){if(t_temp->val > t_temp->next->val){int t_data = t_temp->val;t_temp->val = t_temp->next->val;t_temp->next->val = t_data;}t_temp = t_temp->next;}}
}/*** 定义快速排序算法
*/
void quickSort(struct _ListNode *head, struct _ListNode *tail) {// 如果链表为空或只有一个节点,直接返回if (head == NULL || head == tail) return;// 定义两个指针p和q,用于分割链表struct _ListNode *p = head, *q = head->next;// 选取第一个节点作为基准值int pivot = head->val;// 遍历链表,将小于基准值的节点放到p的后面while (q != tail->next) {if (q->val < pivot) {p = p->next;// 交换p和q指向的节点的值int temp = p->val;p->val = q->val;q->val = temp;}q = q->next;}// 交换head和p指向的节点的值,使得p指向的节点为基准值int temp = head->val;head->val = p->val;p->val = temp;// 对左右两部分递归进行快速排序quickSort(head, p);quickSort(p->next, tail);
}/*** 快速排序
*/
void listQuickSort(ListNode *node)
{ListNode *tail = node->next;while (tail->next){tail = tail->next;}quickSort(node, tail);
}/*** 堆排序
*/
void listHeapSort(ListNode *node)
{}/*** 获取链表倒数第k个节点,双指针方法
*/
ListNode* listFindKthToTail(ListNode *node, int k)
{// 超过长度直接返回空if(node == NULL || k >= listLength(node))return NULL;ListNode *first = node, *second = node;for(int i = 0; i < k; i++){first = first->next;}while (first){first = first->next;second = second->next;}return second;
}/*** 测试所有函数是否正确有效
*/
int main(int argc, char* argv[])
{//创建一个ListNode变量ListNode node;//创建链表listCreate(&node);int i = 0;for(i = 0;i < 10;i++){
#if 0 listInsert(&node,i); // 插入数据头插法
#elselistTailInsert(&node, i); // 插入数据尾插法
#endif}listDisplay(&node);ListNode* nodeFind = listFind(&node, 3);if(nodeFind)printf("listFind:%d\n", nodeFind->val); const int k = 5;ListNode* nodeFindK = listFindKthToTail(&node, k);if(nodeFindK)printf("listFindKthToTail step:%d :%d\n", k, nodeFindK->val); listModify(&node, 1, 999); //修改节点1为999listDisplay(&node);listDelete(&node, 5); // 删除节点5listDisplay(&node);// listBubbleSort(&node); // 冒泡排序listQuickSort(&node); // quick sortlistDisplay(&node); // 打印链表数据listReverse(&node); // 翻转链表listDisplay(&node); // 打印反转后的链表listDestroy(&node); // 销毁链表return 0;
}相关文章:
C语言对单链表所有操作与一些相关面试题
目录 单链表的特性 单链表的所有操作 定义一个单链表 创建一个链表头 插入数据(头插法) 插入数据(尾插法) 查找节点 修改数据节点 删除节点 打印数据 销毁链表 翻转链表 打印链表长度 冒泡排序 快排 堆排 查找倒数第K个节点(双指针法) …...
高防服务器如何抵御大规模攻击
高防服务器如何抵御大规模攻击?高防服务器是一种专门设计用于抵御大规模攻击的服务器,具备出色的安全性和可靠性。在当今互联网时代,网络安全问题日益严重,DDOS攻击(分布式拒绝服务攻击)等高强度攻击已成为…...
Go 接口和多态
在讲解具体的接口之前,先看如下问题。 使用面向对象的方式,设计一个加减的计算器 代码如下: package mainimport "fmt"//父类,这是结构体 type Operate struct {num1 intnum2 int }//加法子类,这是结构体…...
Git忽略文件的几种方法,以及.gitignore文件的忽略规则
目录 .gitignore文件Git忽略规则以及优先级.gitignore文件忽略规则常用匹配示例: 有三种方法可以实现忽略Git中不想提交的文件。1、在Git项目中定义 .gitignore 文件(优先级最高,推荐!)2、在Git项目的设置中指定排除文…...
C语言——指针进阶(2)
继续上次的指针,想起来还有指针的内容还没有更新完,今天来补上之前的内容,上次我们讲了函数指针,并且使用它来实现一些功能,今天我们就讲一讲函数指针数组等内容,废话不多说,我们开始今天的学习…...
【汇编中的寄存器分类与不同寄存器的用途】
汇编中的寄存器分类与不同寄存器的用途 寄存器分类 在计算机体系结构中,8086CPU,寄存器可以分为以下几类: 1. 通用寄存器: 通用寄存器是用于存储数据和执行算术运算的寄存器。在 x86 架构中,这些通用寄存器通常包括…...
基于文本提示的图像目标检测与分割实践
近年来,计算机视觉取得了显着的进步,特别是在图像分割和目标检测任务方面。 最近值得注意的突破之一是分段任意模型(SAM),这是一种多功能深度学习模型,旨在有效地从图像和输入提示中预测对象掩模。 通过利用…...
【4-5章】Spark编程基础(Python版)
课程资源:(林子雨)Spark编程基础(Python版)_哔哩哔哩_bilibili 第4章 RDD编程(21节) Spark生态系统: Spark Core:底层核心(RDD编程是针对这个)Spark SQL:…...
04 卷积神经网络搭建
一、数据集 MNIST数据集是从NIST的两个手写数字数据集:Special Database 3 和Special Database 1中分别取出部分图像,并经过一些图像处理后得到的[参考]。 MNIST数据集共有70000张图像,其中训练集60000张,测试集10000张。所有图…...
【hadoop运维】running beyond physical memory limits:正确配置yarn中的mapreduce内存
文章目录 一. 问题描述二. 问题分析与解决1. container内存监控1.1. 虚拟内存判断1.2. 物理内存判断 2. 正确配置mapReduce内存2.1. 配置map和reduce进程的物理内存:2.2. Map 和Reduce 进程的JVM 堆大小 3. 小结 一. 问题描述 在hadoop3.0.3集群上执行hive3.1.2的任…...
数据结构--6.5二叉排序树(插入,查找和删除)
目录 一、创建 二、插入 三、删除 二叉排序树(Binary Sort Tree)又称为二叉查找树,它或者是一棵空树,或者是具有下列性质的二叉树: ——若它的左子树不为空,则左子树上所有结点的值均小于它的根结构的值…...
无需公网IP,在家SSH远程连接公司内网服务器「cpolar内网穿透」
文章目录 1. Linux CentOS安装cpolar2. 创建TCP隧道3. 随机地址公网远程连接4. 固定TCP地址5. 使用固定公网TCP地址SSH远程 本次教程我们来实现如何在外公网环境下,SSH远程连接家里/公司的Linux CentOS服务器,无需公网IP,也不需要设置路由器。…...
Java工具类
一、org.apache.commons.io.IOUtils closeQuietly() toString() copy() toByteArray() write() toInputStream() readLines() copyLarge() lineIterator() readFully() 二、org.apache.commons.io.FileUtils deleteDirectory() readFileToString() de…...
makefile之使用函数wildcard和patsubst
Makefile之调用函数 调用makefile机制实现的一些函数 $(function arguments) : function是函数名,arguments是该函数的参数 参数和函数名用空格或Tab分隔,如果有多个参数,之间用逗号隔开. wildcard函数:让通配符在makefile文件中使用有效果 $(wildcard pattern) 输入只有一个参…...
算法通关村第十八关——排列问题
LeetCode46.给定一个没有重复数字的序列,返回其所有可能的全排列。例如: 输入:[1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 元素1在[1,2]中已经使…...
基于STM32设计的生理监测装置
一、项目功能要求 设计并制作一个生理监测装置,能够实时监测人体的心电图、呼吸和温度,并在LCD液晶显示屏上显示相关数据。 随着现代生活节奏的加快和环境的变化,人们对身体健康的关注程度越来越高。为了及时掌握自身的生理状况,…...
Go-Python-Java-C-LeetCode高分解法-第五周合集
前言 本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接:LeetCode-Go-Python-Java-C Go-Python-Java-C-LeetCode高分解法-第一周合集 Go-Python-Java-C-LeetCode高分解法-第二周合集 Go-Python-Java-C-LeetCode高分解法-第三周合集 G…...
【前端知识】前端加密算法(base64、md5、sha1、escape/unescape、AES/DES)
前端加密算法 一、base64加解密算法 简介:Base64算法使用64个字符(A-Z、a-z、0-9、、/)来表示二进制数据的64种可能性,将每3个字节的数据编码为4个可打印字符。如果字节数不是3的倍数,将会进行填充。 优点࿱…...
leetcode 925. 长按键入
2023.9.7 我的基本思路是两数组字符逐一对比,遇到不同的字符,判断一下typed与上一字符是否相同,不相同返回false,相同则继续对比。 最后要分别判断name和typed分别先遍历完时的情况。直接看代码: class Solution { p…...
[CMake教程] 循环
目录 一、foreach()二、while()三、break() 与 continue() 作为一个编程语言,CMake也少不了循环流程控制,他提供两种循环foreach() 和 while()。 一、foreach() 基本语法: foreach(<loop_var> <items>)<commands> endfo…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...
Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...
虚幻基础:角色旋转
能帮到你的话,就给个赞吧 😘 文章目录 移动组件使用控制器所需旋转:组件 使用 控制器旋转将旋转朝向运动:组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转:必须移动才能旋转,不移动不旋转控制器…...
C++ 类基础:封装、继承、多态与多线程模板实现
前言 C 是一门强大的面向对象编程语言,而类(Class)作为其核心特性之一,是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性,包括封装、继承和多态,同时讨论类中的权限控制,并展示如何使用类…...
Git 命令全流程总结
以下是从初始化到版本控制、查看记录、撤回操作的 Git 命令全流程总结,按操作场景分类整理: 一、初始化与基础操作 操作命令初始化仓库git init添加所有文件到暂存区git add .提交到本地仓库git commit -m "提交描述"首次提交需配置身份git c…...
