数据结构->链表分类与oj(题),带你提升代码好感

✅作者简介:大家好,我是橘橙黄又青,一个想要与大家共同进步的男人😉😉

🍎个人主页:橘橙黄又青-CSDN博客

1.🍎链表的分类

前面我们学过顺序表,顺序表问题:

  • 1. 中间/头部的插入删除,时间复杂度为O(N)
  • 2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  • 3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到 200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

这些问题,链表给出了相应的答案。

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。

 比如说我们前面学习的单链表就是如此。

单链表是单向不循环不带头链表

 

实际中要实现的链表的结构非常多样,以下情况组合起来就有8种链表结构:

  • 1. 单向、双向
  • 2. 带头、不带头
  • 3. 循环、非循环

又称2x2x2 

图解:

 

  • 1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结 构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  • 2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都 是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带 来很多优势,实现反而简单了,后面我们代码实现了就知道了。 

2.🍎链表相关基础oj

题目链接
设计链表. - 力扣(LeetCode)
两数相加. - 力扣(LeetCode)
合并两个有序链表. - 力扣(LeetCode)
两两交换链表中的节点. - 力扣(LeetCode)
删除排序链表中的重复元素. - 力扣(LeetCode)
删除排序链表中的重复元素II. - 力扣(LeetCode)
相交链表. - 力扣(LeetCode)
移除链表元素. - 力扣(LeetCode)
回文链表. - 力扣(LeetCode)
奇偶链表. - 力扣(LeetCode)
链表的中间结点. - 力扣(LeetCode)
链表最大孪生和. - 力扣(LeetCode)
两两交换链表中的节点404: This page could not be found

合并两个链表中,可以安排一个哨兵(头)可以减少判断空的代码。

哨兵位的申请:

这些都是比较好的题目,

接下来我们分析几道题目

题目链接
反转链表. - 力扣(LeetCode)
反转链表II. - 力扣(LeetCode)
链表中间节点问题. - 力扣(LeetCode)
环形链表的约瑟夫问题环形链表的约瑟夫问题_牛客题霸_牛客网

2.1🍎第一个反转链表

第一种:头插法

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*///头插术
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {ListNode* returnHead = NULL;ListNode* ListLt = head;while(ListLt != NULL){//保存下一个节点ListNode* temp = ListLt -> next;//换头手术ListLt -> next = returnHead;returnHead = ListLt;//迭代ListLt = temp;}return returnHead;
}

这里说一个错误代码:

输出结果为1,为什么来:

 

所以temp要保存下一个节点。 

第二种解法:

三指针法:

代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {if(head == NULL){return head;}ListNode* n1 = NULL;ListNode* n2 = head;ListNode* n3 = head -> next;ListNode* pcur = head;while(n2){n2-> next = n1;n1 = n2;n2 = n3;if(n3){//这里防止n3为空,如果为空就无nextn3 = n3 -> next; }}return n1;
}

图解:

链表II与这个差不多,思路更为复杂。 

2.2🍎链表中间节点问题

可以遍历链表计数除2,也可以使用双指针法

双指针法:

思路:

代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {ListNode* slow,*fast;slow = fast = head;while(fast && fast -> next){slow = slow -> next;fast = fast -> next -> next;}return slow;
}

值得注意的是循环条件不能写成 fast -> next &&fast ,原因是计算机编译器是从左往右编译的,如果fast为空,就没有fast -> next。

快慢指针法以后会经常使用务必理解.

2.3.环形链表的约瑟夫问题 

题目:

这里我们输入Li 它就有暗示,说明已经有结构体结构了

解题代码:

/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param n int整型 * @param m int整型 * @return int整型*/typedef struct ListNode ListNode;
//申请一个节点ListNode* BuyNode(int x){ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));newNode -> val = x;newNode -> next = NULL;return newNode;}ListNode* creteList(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;}
#include <stdio.h>
int ysf(int n, int m ) {//逢m删除节点ListNode* head = creteList(n);ListNode* pcur = head;//前驱节点ListNode* prev = NULL;int conut = 1;while (pcur ->next != pcur) {if(conut == m){//删除当前节点prev -> next = pcur ->next;free(pcur);//删除节点后往后走,让pcur走到新的位置,conut为初始值pcur = prev -> next;conut = 1;}else{//pcur往后走prev = pcur;pcur =  pcur -> next;conut++;}}//此时pcur节点就是活下来的节点return pcur -> val;
}

3.🍎上节链表代码建议重复观看

SList.c文件:

 SList.h文件

 test.c文件测试代码: 

 test.c文件代码

#include"SList.h"//void SlistTest01() {
// 
//	链表的打印
//	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
//	node1->data = 1;
//	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
//	node2->data = 2;
//	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
//	node3->data = 3;
//	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
//	node4->data = 4;
//
//	node1->next = node2;
//	node2->next = node3;
//	node3->next = node4;
//	node4->next = NULL;
//
//	SLTNode* plist = node1;
//	SLTPrint(plist);
//}
void SlistTest02() {//尾插测试SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist); //1->2->3->4->NULL//头插测试//SLTPushFront(&plist, 5);//SLTPrint(plist);          //5->1->2->3->4->NULL//SLTPushFront(&plist, 6);//SLTPrint(plist);         //6->5->1->2->3->4->NULL//SLTPushFront(&plist, 7);//SLTPrint(plist);         //7-6->5->1->2->3->4->NULLSLTPopBack(&plist);SLTPrint(plist);//1->2->3->NULLSLTPopBack(&plist);SLTPrint(plist);//1->2->3->NULLSLTPopBack(&plist);SLTPrint(plist);//1->2->3->NULLSLTPopBack(&plist);SLTPrint(plist);//1->2->3->NULLSLTPopBack(&plist);SLTPrint(plist);//1->2->3->NULL
}void SlistTest03() {SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist); //1->2->3->4->NULLSListDesTroy(&plist);头删//SLTPopFront(&plist);//SLTPrint(plist);    //2->3->4->NULL//SLTPopFront(&plist);//SLTPrint(plist);    //3->4->NULL//SLTPopFront(&plist);//SLTPrint(plist);    //4->NULL//SLTPopFront(&plist);//SLTPrint(plist);    //NULL//SLTPopFront(&plist);//SLTPrint(plist);    //assert//查找测试//SLTNode* FindRet = SLTFind(&plist, 3);//if (FindRet) {//	printf("找到了!\n");//}//else {//	printf("未找到!\n");//}//SLTInsert(&plist, FindRet, 100);//SLTInsertAfter(FindRet, 100);//删除指定位置的节点测试//SLTErase(&plist, FindRet);//SLTPrint(plist); //1->2->3->NULL
}
int main() {//SlistTest01();//SlistTest02();SlistTest03();return 0;
}

SList.c文件代码:

#include"SList.h"
//打印
void SLTPrint(SLTNode* phead) {SLTNode* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
//开辟空间
SLTNode* SLTBuyNode(SLTDataType x) {SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL) {perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}
//
void SLTPushBack(SLTNode** pphead, SLTDataType x) {assert(pphead);//开辟空间SLTNode* newnode = SLTBuyNode(x);//链表为空,新节点作为pheadif (*pphead == NULL) {*pphead = newnode;return;}//链表不为空,找尾节点SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}//ptail就是尾节点ptail->next = newnode;
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newnode = SLTBuyNode(x);//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}
//尾删
void SLTPopBack(SLTNode** pphead) {assert(pphead);//链表不能为空assert(*pphead);//链表不为空//链表只有一个节点,有多个节点if ((*pphead)->next == NULL) {free(*pphead);*pphead = NULL;return;}SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;//销毁尾结点free(ptail);ptail = NULL;
}
//头删
void SLTPopFront(SLTNode** pphead) {assert(pphead);//链表不能为空assert(*pphead);//让第二个节点成为新的头//把旧的头结点释放掉SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}
//查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x) {assert(pphead);//遍历链表SLTNode* pcur = *pphead;while (pcur) //等价于pcur != NULL{if (pcur->data == x) {return pcur;}pcur = pcur->next;}//没有找到return NULL;
}
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {assert(pphead);assert(pos);//要加上链表不能为空assert(*pphead);SLTNode* newnode = SLTBuyNode(x);//pos刚好是头结点if (pos == *pphead) {//头插SLTPushFront(pphead, x);return;}//pos不是头结点的情况SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev -> newnode -> posprev->next = newnode;newnode->next = pos;
}
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x) {assert(pos);SLTNode* newnode = SLTBuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;pos->next = newnode;
}
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos) {assert(pphead);assert(*pphead);assert(pos);//pos刚好是头结点,没有前驱节点,执行头删if (*pphead == pos) {//头删SLTPopFront(pphead);return;}SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev pos pos->nextprev->next = pos->next;free(pos);pos = NULL;
}
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos) {assert(pos);//pos->next不能为空assert(pos->next);//pos  pos->next  pos->next->nextSLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}
//销毁链表
void SListDesTroy(SLTNode** pphead) {assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

SList.h文件代码:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLTDataType;
//链表是由节点组成
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;//typedef struct SListNode SLTNode;
//打印链表
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** pphead, SLTDataType x);//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);//销毁链表
void SListDesT

今天就到这里了,都看到这里了,点一个赞吧,感谢观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/724313.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大唐杯学习笔记:Day5

1.1 小区搜索 搜索流程 PLMN选择 自动模式&#xff1a;UE根据NAS的请求或自主地向NAS报告可用的PLMN 手动模式&#xff1a;通过手动选择一个可用的VPLMN获取正常服务 频点选择 5G NR中,3GPP主要指定了两个频率范围,一个是6GHZ以下,另一个是毫米波,分别称之为FR1和FR2。 N…

AIOps实践中常见的挑战:故障根因与可观测性数据的割裂

运维的挑战与责任 在数字化时代&#xff0c;运维团队面临的挑战前所未有。他们不仅要确保系统的高可用性和高性能&#xff0c;还要快速响应并解决故障&#xff0c;以减少对业务的影响。在这种背景下&#xff0c;运维团队急需工具和技术&#xff0c;能够帮助他们提高效率&#…

一文解释python中的实例方法,类方法和静态方法作用和区别是啥?该如何使用

我们都知道 &#xff0c;python类中有三种常见的方法 &#xff0c;分别是实例方法 &#xff0c;类方法和静态方法 。那么这几个方法到底有什么作用 &#xff1f; 它们之间有什么区别 &#xff1f;该如何使用 &#xff1f; 带着这些问题 &#xff0c;下面我们就来了解下这三种方…

1688商品详情数据采集,工程数据采集丨店铺数据采集丨商品详情数据采集

1688是中国的一个大型B2B电子商务平台&#xff0c;主要用于批发和采购各种商品。对于需要从1688上获取商品详情数据、工程数据或店铺数据的用户来说&#xff0c;可以采用以下几种常见的方法&#xff1a; 官方API接口&#xff1a;如果1688提供了官方的API接口&#xff0c;那么可…

【高效开发工具系列】vimdiff简介与使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

米哈游排名首超腾讯,登顶榜首 !!!

米哈游排名首超腾讯&#xff0c;登顶榜首 &#xff01;&#xff01;&#xff01; 大家好&#xff0c;我是銘&#xff0c;全栈开发程序员。 近日&#xff0c;第三方机构 data.ai 公布 2023 年中国游戏厂商及应用出海收入 30 强。 其中米哈游超越腾讯&#xff0c;首次登顶年度…

Selenum八种常用定位(案例解析)

Selenium是一个备受推崇的工具。它有着丰富的功能&#xff0c;让我们能够与网页互动&#xff0c;执行各种任务&#xff0c;能为测试工程师和开发人员提供了很大的便利。 要充分利用Selenium&#xff0c;就需要了解如何正确定位网页上的元素。 接下来我将带大家共同探讨Seleni…

多模态入门

VIT处理图像 CNN VS Transformer 多模态BLIP模型 网络结构 视觉编码器: 就是 ViT 的架构。将输入图像分割成一个个的 Patch 并将它们编码为一系列 Image Embedding,并使用额外的 [CLS] token 来表示全局的图像特征。视觉编码器不采用之前的基于目标检测器的形式,因为 ViLT 和…

推荐书籍《低代码平台开发实践:基于React》—— 提升开发效率,构建优质应用

写在前面 随着数字化转型的深入&#xff0c;企业对应用开发效率和灵活性的要求不断提高。低代码平台作为新兴的软件开发方式&#xff0c;通过可视化界面和预构建组件&#xff0c;极大简化了应用开发流程&#xff0c;降低了技术门槛。基于React的低代码平台以其组件化、响应式和…

Kube-Prometheus 监控Istio

推荐 Istio 多集群监控使用 Prometheus&#xff0c;其主要原因是基于 Prometheus 的分层联邦&#xff08;Hierarchical Federation&#xff09;。 通过 Istio 部署到每个集群中的 Prometheus 实例作为初始收集器&#xff0c;然后将数据聚合到网格层次的 Prometheus 实例上。 网…

3.6作业

作业要求&#xff1a;数据库操作的增、删、改 程序代码&#xff1a; #include<myhead.h> int main(int argc, const char *argv[]) {//定义数据库句柄指针sqlite3 * ppDb NULL;//打开数据库&#xff0c;如果数据库不存在&#xff0c;则创建数据库//将数据库句柄由参数…

【Leetcode每日一刷】数组|704. 二分查找、27. 移除元素

力扣每日刷题 一、704. 二分查找1.1、题目1.2、解题思路1.3、代码实现——C1.4、 总结&易错 二、27. 移除元素2.1&#xff1a;题目2.2、解题思路2.3、代码实现——C1.4、 总结&易错 一、704. 二分查找 1.1、题目 704. 二分查找 1.2、解题思路 题型&#xff1a;数组…

2024年洗地机推荐,希亦、美的、西屋、顺造洗地机哪个品牌最耐用质量好?

对许多人来说&#xff0c;全屋清洁可能是件让人望而却步的任务&#xff0c;因为它需要花费大量的体力和时间。但是&#xff0c;随着科技的发展&#xff0c;我们可以找到一些能够简化这个过程的神器&#xff0c;比如洗地机。有了洗地机&#xff0c;我们可以轻松地完成扫地、拖地…

【CSP试题回顾】201509-1-数列分段

CSP-201509-1-数列分段 解题代码 #include <iostream> #include <vector> #include <algorithm> using namespace std;int n, t, maxSeg 0;int main() {cin >> n;vector<int>list(n);for (int i 0; i < n; i){cin >> list[i];}auto…

JavaBoy假期如何学习项目?弯道块才是真的快!

至暗时刻 老话说的好&#xff0c;弯道快才是真的快&#xff0c;谁直线不会加油&#xff1f;每到假期都是在座的各位弯道超车的时候。转眼自己已经出来搬了快四年砖头了&#xff0c;偶尔访问下牛客发现行情真是一年不如一年。。。不由得回想起自己春招时候的经历。 回想起2020年…

HI3519DV500 HI3519DRFCV500 HI3519DRBCV500 海思安防监控芯片 提供原厂开发包

一、总体介绍 Hi3519DV500是一颗面向视觉行业推出的超 高清智能 SoC。该芯片最高支持四路sensor输 入&#xff0c;支持最高4K30fps的ISP图像处理能力&#xff0c;支持 2F WDR、多级降噪、六轴防抖、全景拼接、多光 谱融合等多种传统图像增强和处理算法&#xff0c;支持通过AI…

浏览器发出一个请求到收到响应步骤详解

前言 在网络通信中&#xff0c;浏览器向Web服务器发送HTTP请求消息的过程是一个复杂而精密的环节&#xff0c;涉及到URL解析、DNS解析、数据拆分、路由表规则和MAC头部添加等一系列步骤。本文将深入探讨这一过程的每个环节&#xff0c;帮助读者更全面地了解浏览器与Web服务器之…

双体系Java学习之关键字,标识符以及命名规范

重新开始从Java基础开始学&#xff0c;保持每周两更的状态&#xff0c;刚开学事情有点多。 关键字 标识符 命名规范

docker 安装 portainer

小编给友友们总结了一下 Portainer 的好处以下 Portainer是Docker的图形化管理工具&#xff0c;提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作&#xff08;包括上传下载镜像&#xff0c;创建容器等操作&#xff09;、事件日志显示、容器控制台操作、Swar…

STM32CubeIDE基础学习-安装芯片固件支持包

STM32CubeIDE基础学习-添加芯片固件支持包 前言 前面的文章在安装STM32CubeIDE软件时没有安装这个芯片PACK包&#xff0c;如果工程没有这个固件支持包的话是无法正常使用的&#xff0c;随便安装一个和芯片对应系列的支持包就可以了。 这篇文章来记录一下新增PACK包的常用操作…