数据结构·单链表经典例题

news2024/12/28 5:39:02

1. 移除链表元素

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

        本题是说给出一个链表的头节点head和一个整数val,如果发现节点中存的数据有val就删掉它,最后返回修改后的链表头节点地址

        如果题目中没有明确提及给出的链表是否是带头的,那就默认是不带头的链表,此时题目中再提到头节点就是指链表的第一个节点

思路1:

        从第二个节点开始,判断其内含的数据是否是val,然后遍历链表,最后判断头节点中数据是否是val,如果是,再挪移头节点的指向

        至于为什么从第二个节点开始扫描是为了不用每次都判断一下头节点要不要移动,先把后面的节点都处理好,最后再确定头节点的指向

        但是这个思路还是太复杂了

思路2:

        创建一个新链表,把不是val的节点都丢到新链表中去,最后返回新链表的头节点

        与其说是创建了一个新链表,不如说是将原链表的链接顺序拿到一个新的地方进行更改

struct ListNode* removeElements(struct ListNode* head, int val) 
{
    //记录新链表的头和尾
    struct ListNode* newhead = NULL;
    struct ListNode* newtail = NULL;
    //pcur用来扫描原链表
    struct ListNode* pcur = head;

    while (pcur)
    {
        //不是val尾插到新链表的尾
        if (pcur->val != val)
        {
            //如果新链表为空,那么新加入的节点既是头节点也是尾节点
            if (newhead == NULL)
            {
                newhead = pcur;
                newtail = pcur;
            }
            //如果链表不为空,就将新节点放到链表尾,
            else
            {
                newtail->next = pcur;
                newtail = pcur;
            }
        }
        //pcur指向的节点是不是val都要往下走
        pcur = pcur->next;
    }
    //因为有可能返回的是空链表,所以不能粗暴的去访问newtail->next
    //要先判断要返回的newtail是否为空,也就是说是否是空链表
    if (newtail)
    {
        newtail->next = NULL;
    }
    return newhead;
}

2. 链表的中间结点

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

        本题是说,给出一个链表的头节点head,然后找到这个链表的中间节点,如果有两个中间节点,就返回第二个中间节点

思路1:

        遍历整个链表,数出一共有几个节点,然后通过除2找到中间节点的"下标",然后通过"下标",再访问出中间节点的地址

        很明显,这个方法很麻烦

思路2:快慢指针法

        跟前面双指针那节一样,这个快慢指针也不是真正的创造两个指针,而是创造两个具有类似指针功能的变量

        思路就是创造一个慢指针slow,一个快指针fast,然后slow走一步,fast走两步,这样fast走完的时候slow刚好来到链表中间

        快慢指针法就是通过只遍历一次就能达到对整体进行类似除法运算的功效,比如slow走1步fast走4步,fast走完,slow就走到整体的四分之一处

        回到本题,在判断结束扫描的条件时要注意,先判断fast是否为NULL,利用短路的特性避免判断fast->next,因为如果fast为假了,去访问next的时候就会崩

struct ListNode* middleNode(struct ListNode* head) 
{
	struct ListNode* slow = head;
	struct ListNode* fast = head;

	//有一个为假就跳出循环
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}

3. 反转链表

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

        本体是说给一个单链表的头节点地址head,让反转链表,再将新链表的头返回,值得注意的是这个链表可能是个空链表

思路1:

        和第一题类似,创建一个"新链表",每次将旧链表的第一个节点拿下来,头插到新链表

        因为这题给的是单链表,不能用后面的节点访问前面的节点,所以不要想从最后一个节点开始改变链接方向,因为遍历到最后一个节点就找不到前面的节点了

思路2:

        用3个指针n1、n2、n3,初始状态n1指向空,n2指向头节点,n3指向第二个节点。然后将n2节点的指向变成n1,然后把n1变到n2的位置,n2变到n3的位置,再把n3滑到下一个节点。然后在n2不为空的情况下一直循环这个操作。

struct ListNode* reverseList(struct ListNode* head) 
{
    //如果传过来的是空链表
    if (head == NULL)
    {
        return head;
    }
    struct ListNode* n1 = NULL;
    struct ListNode* n2 = head;
    struct ListNode* n3 = head->next;

    while (n2)
    {
        n2->next = n1;
        n1 = n2;
        n3 = n2;
        //如果n3已经指向NULL了就不用往下滑了
        if (n3)
        {
            n3 = n3->next;
        }        
    }
    return n1;
}

4. 合并两个有序链表

        OJ链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

        本题是说,给两个升序链表的头节点地址list1和list2,然后将两个链表合并成一共新的升序链表,并返回新链表的头,值得注意的是给出的两个链表可能有空的

思路:

        创建一个"新链表",再用顺序表中讲到的那道合并数组的题的思想

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
    //如果给出的某个链表为空,就返回另一个链表
    if (list1 == NULL)
    {
        return list2;
    }
    if (list2 == NULL)
    {
        return list1;
    }
    //两个扫描原链表的指针
    struct ListNode* p1 = list1;
    struct ListNode* p2 = list2;

    //两个控制新链表的指针
    struct ListNode* newhead = NULL;
    struct ListNode* newtail = NULL;

    //p1或p2有一个扫描完就退出循环
    while (p1 && p2)
    {
        //如果p1扫到的数据更小
        if (p1->val < p2->val)
        {
            //如果新链表中没有节点
            if (newhead == NULL)
            {
                newhead = p1;
                newtail = p1;
            }
            //如果新链表不为空
            else
            {
                newtail->next = p1;
                newtail = p1;
            }
            p1 = p1->next;
        }
        //如果p2扫到的数据更小
        else
        {
            //如果新链表为空
            if (newhead == NULL)
            {
                newhead = p2;
                newtail = p2;
            }
            //如果新链表不为空
            else
            {
                newtail->next = p2;
                newtail = p2;
            }
            p2 = p2->next;
        }
    }
    //处理旧链表没挪完的那部分
    if (p1)
    {
        newtail->next = p1;
    }
    if (p2)
    {
        newtail->next = p2;
    }
    return newhead;
}

5. 分割链表(带头链表的优势)

        本题我会距离说明带头链表比不带头链表好在哪里

        OJ链接:https://leetcode.cn/problems/partition-list-lcci/description/

        本题给出一个链表的头节点地址,和一个特定的值x,要求是把所存数据小于x的节点堆在前面,把大于等于x的节点堆在后面,堆放的时候不需要保存节点原来相对位置的有关信息

思路1:

        创建一个"新链表"把原链表中小于x的节点头插,把大于等于x的节点尾插

思路2:带头链表的优点

        先说解题思路,我们创造两个"新链表",lesshead尾插小于x的节点,bighead尾插大于等于x的节点,最后把bighead连到lesshead后面

        现在我们回忆一下,之前的代码再每次插入新节点的时候都要判断一下链表是否为空,这很麻烦。所以我们直接让链表带头,这样每次插入新节点的时候就不用判断了,因为链表一定不为空,它有一个不存储数据的头或者说"哨兵位",省去了插入时的很多麻烦

struct ListNode* partition(struct ListNode* head, int x) 
{
	//判断传过来的是否时空链表
	if (head == NULL)
	{
		return head;
	}

	//创建两个带头新链表
	struct ListNode* lesshead, * lesstail;
	struct ListNode* bighead, * bigtail;
	//申请头节点空间,并将其地址记录
	lesshead = lesstail = (struct ListNode*)malloc(sizeof(struct ListNode));
	bighead = bigtail = (struct ListNode*)malloc(sizeof(struct ListNode));

	//用pcur遍历原链表,将节点放到对应的新链表中
	struct ListNode* pcur = head;

	while (pcur)
	{
		if (pcur->val < x)
		{
			lesstail->next = pcur;
			lesstail = lesstail->next;
		}
		else
		{
			bigtail->next = pcur;
			bigtail = bigtail->next;
		}
		pcur = pcur->next;
	}
	//链接两个链表
	//先将大链表的尾置空
	bigtail->next = NULL;
	//再接上,注意要掠过大链表的那个没意义的头
	lesstail->next = bighead->next;
	//先存上小链表的第一个存有效数据的节点
	struct ListNode* ret = lesshead->next;
	//在释放两个新链表的头
	free(bighead);
	free(lesshead);

	return ret;
}

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

        OJ链接:环形链表的约瑟夫问题_牛客题霸_牛客网

        本题······哎呀这题我就不复述了,人家题干说的挺清楚的

思路:循环链表

        创建不带头单向循环链表,模拟围成一圈的人,逢m就删除节点

#include<stdlib.h>
//创建新节点
struct ListNode* BuyNode(int x)
{
	struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
	newnode->val = x;
	newnode->next = NULL;
	return newnode;
}
//创建链表
struct ListNode* creatlist(int n)
{
	//先创建一个头节点
	struct ListNode* phead = BuyNode(1);
	struct ListNode* ptail = phead;
	//再循环进行尾插,形成链表
	for (int i = 2; i <= n; i++)
	{
		ptail->next = BuyNode(i);
		ptail = ptail->next;
	}
	//让链表首尾相连
	ptail->next = phead;
	return phead;
}

int ysf(int n, int m) 
{
	//创建不带头单向循环链表
	struct ListNode* phead = creatlist(n);
	struct ListNode* pcur = phead;
	struct ListNode* prev = NULL;
	//逢m删除节点,直到剩下最后一个节点
	int count = 1;
	while (pcur->next != pcur)
	{
		if (m == count)
		{
			//删除当前节点
			prev->next = pcur->next;
			free(pcur);
			count = 1;
			pcur = prev->next;
		}
		else
		{
			//pcur往后走
			prev = pcur;
			pcur = pcur->next;
			count++;
		}
	}
	return pcur->val;
}

        不必担心当 m==1 的使得 prev->next 非法的情况,while的循环条件就已经把这种情况挡在外面了,而且题干里说了一定会剩下来一个人,所以返回pcur的数据也是没有问题的。

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

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

相关文章

【LeetCode每日一题】56. 合并区间插入区间

一、判断区间是否重叠 力扣 252. 会议室 给定一个会议时间安排的数组 intervals &#xff0c;每个会议时间都会包括开始和结束的时间 intervals[i] [starti, endi] &#xff0c;请你判断一个人是否能够参加这里面的全部会议。 思路分析 因为一个人在同一时刻只能参加一个会…

掌握使用 React 和 Ant Design 的个人博客艺术之美

文章目录 前言在React的海洋中起航安装 Create React App安装Ant Design 打造个性化的博客风格通过路由实现多页面美化与样式定制部署与分享总结 前言 在当今数字时代&#xff0c;个人博客成为表达观点、分享经验和展示技能的独特平台。在这个互联网浪潮中&#xff0c;选择使用…

Unity 外观模式(实例详解)

文章目录 示例1&#xff1a;初始化游戏场景中的多个子系统示例2&#xff1a;管理音频播放示例3&#xff1a;场景加载流程示例4&#xff1a;UI管理器示例5&#xff1a;网络服务通信 在Unity中使用外观模式&#xff08;Facade&#xff09;时&#xff0c;主要目的是为了简化复杂子…

如何配置MacLinuxWindows环境变量

这里写目录标题 什么是环境变量什么是PATH为什么要配置环境变量 如何配置环境变量环境变量有哪些环境变量加载顺序环境变量加载详解 配置参考方法一&#xff1a; export PATHLinux环境变量配置方法二&#xff1a;vim ~/.bashrcLinux环境变量配置方法三&#xff1a;vim ~/.bash_…

使用程序设计流程图解析并建立神经网络(不依赖深度学习library)

介绍&#xff1a; ## Flow chart for a simple neural network: #(1)Take inputs 输入 #(2)Add bias (if required) #(3)Assign random weights to input features 随机一个权重 #(4)Run the code for training. 训练集训练 #(5)Find the error in prediction. 找预测损失 #(6…

RAG应用-七个最常见的故障点

近日&#xff0c;国外研究者发布了一篇论文《Seven Failure Points When Engineering a Retrieval Augmented Generation System》&#xff0c;探讨了在实际工程落地RAG应用过程中容易出的七类问题。 论文地址&#xff1a;https://arxiv.org/pdf/2401.05856.pdf 一、丢失内容&…

网络协议与攻击模拟_10DHCP攻击与DHCP欺骗

一、DHCP的报文格式 Message type&#xff1a;消息类型&#xff08;1表示请求&#xff0c;2表示响应&#xff09;Hardware type&#xff1a;硬件类型Hardware address length&#xff1a;硬件地址长度Hops&#xff1a;DHCP报文经过中继的数目。Transaction ID&#xff1a;事务…

树莓派部署Nginx服务结合内网穿透实现远程访问本地站点

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Ngi…

springboot mongodb简单教程

&#xff08;1&#xff09;依赖 compile(org.springframework.boot:spring-boot-starter-data-mongodb) &#xff08;2&#xff09;application.properties配置文件 spring.data.mongodb.host127.0.0.1 spring.data.mongodb.port27017 spring.data.mongodb.databasetest &a…

如何成为一个更好的沟通者?

新年伊始&#xff0c;我想跟大家分享9条我给自己定下的「沟通准则」。 这9条准则&#xff0c;是我对自己的要求&#xff0c;也是我身体力行践行了许多年的做法。我可能也没能践行得非常完美&#xff0c;但这也是我一直在努力的目标。 如果你希望能够跟别人「好好说话」&#xf…

Linux——磁盘和文件系统(一)

Linux——磁盘和文件系统 磁盘机械式磁盘固态硬盘 机械式磁盘结构磁盘&#xff0c;磁道&#xff0c;扇区柱面 文件系统的初始化划卷&#xff08;划盘&#xff09; 挂载C盘放了什么东西Boot Block&#xff08;启动模块&#xff09; 0号组放了什么东西Super Block&#xff08;超级…

[m1pro ] ssh: connect to host localhost port 22: Connection refused

在学习Hadoop 的时候&#xff0c;使用 ssh localhost 遇到以下问题 原因&#xff1a; 本地没有打开远程登录 解决办法&#xff1a;打开远程登录 成功结果

leetcode刷题(剑指offer) 191.位1的个数

191.位1的个数 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 ‘1’ 的个数&#xff08;也被称为汉明重量&#xff09;。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;…

【Linux网络编程】网络编程套接字(1)

【Linux网络编程】网络编程套接字(1) 目录 【Linux网络编程】网络编程套接字(1)源IP地址和目的IP地址端口号端口号和进程ID的关系 网络通信TCP协议UDP协议网络字节序socket编程接口简单的UDP网络程序 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2024.1.29 前言&#xff1…

时序预测 | Python基于Multihead-Attention-TCN-LSTM的时间序列预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 时序预测 | Python基于Multihead-Attention-TCN-LSTM的时间序列预测 Multihead-Attention-TCN-LSTM&#xff08;多头注意力-TCN-LSTM&#xff09;是一种结合了多个注意力机制、时序卷积网络&#xff08;TCN&#xff0…

ORB-SLAM策略思考之RANSAC

ORB-SLAM策略思考之RANSAC 1. 初始化器的RANSAC ORB-SLAM中的初始化器是一个端到端的地图初始化策略&#xff0c;即不需要人的参与双线程同时计算本质矩阵和单应性矩阵使用基于RANSAC和卡方检验的评价方法 为了保证两种算法评价的一致性&#xff0c;计算本质矩阵F和单应性矩阵…

ETCD监控方法以及核心指标

文章目录 1. 监控指标采集1.1 监控指标采集1.2 配置promethues采集和大盘 2. 核心告警指标3. 参考文章 探讨etcd的监控数据采集方式以及需要关注的核心指标&#xff0c;便于日常生产进行监控和巡检。 1. 监控指标采集 etcd默认通过/metrics指标暴露相关指标&#xff0c;因此不…

引入 js 文件报错: Uncaught SyntaxError: Unexpected token <

文章目录 问题分析问题 在开发中引入 Cesium.js 文件时报错如下 分析 我是这么引入的<body><noscript><strong>Were sorry but <%= htmlWebpackPlugin.options.title %> doesnt work properly without JavaScript enabled.Please enable it to c

C++仿函数、万能头文件、transform学习

这是网上的一个代码,里面的一些东西以前没用过; #include <bits/stdc++.h> using namespace std;// A Functor class increment { private:int num; public:increment(int n) : num(n) { }int operator () (int arr_num) const {return num + arr_num;} };// Driver …

Python列表中的append功能及用法举例

Python列表中的append功能及用法举例 &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;append()&#x1f333;&#x1f340;功能介绍&#x1f340;&#x1f340;语法&#x1f340;&#x1f340;示例&#x1f340;&#x1f340;注意事项&#x…