单链表的应用

news2024/10/5 20:17:22

文章目录

  • 目录
    • 1. 单链表经典算法OJ题目
      • 1.1 [移除链表元素](https://leetcode.cn/problems/remove-linked-list-elements/description/)
      • 1.2 [链表的中间节点](https://leetcode.cn/problems/middle-of-the-linked-list/description/)
      • 1.3 [反转链表](https://leetcode.cn/problems/reverse-linked-list/description/)
      • 1.4 [合并两个有序链表](https://leetcode.cn/problems/merge-two-sorted-lists/description/)
      • 1.5 [分割链表](https://leetcode.cn/problems/partition-list-lcci/description/)
      • 1.6 [环形链表的约瑟夫问题](https://www.nowcoder.com/practice/41c399fdb6004b31a6cbb047c641ed8a)
    • 2. 基于单链表再实现通讯录项目

目录

  • 单链表经典算法OJ题目
  • 基于单链表再实现通讯录项目

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

1.1 移除链表元素

移除链表元素

#include <stdio.h>

typedef struct ListNode
{
	int val;
	struct ListNode* next;
}ListNode;

struct ListNode* removeElements(struct ListNode* head, int val)
{
	//定义新链表的头尾指针
	ListNode* newHead, * newTail;
	newHead = newTail = NULL;

	ListNode* pcur = head;

	while (pcur)
	{
		//不是val,尾插到新链表
		if (pcur->val != val)
		{
			if (NULL == newHead)
			{
				//链表为空
				newHead = newTail = pcur;
			}
			else
			{
				//链表不为空
				newTail->next = pcur;
				newTail = newTail->next;
			}

			pcur = pcur->next;
		}
		else
		{
			ListNode* del = pcur;
			pcur = pcur->next;
			free(del);
			del = NULL;
		}
	}

	if (newTail)//为了防止传过来的是空链表,newTail为空;或者链表中都是同一个值,而正好删除的是这个值,删完之后新链表中newTail依然是空
	{
		newTail->next = NULL;
	}

	return newHead;
}

1.2 链表的中间节点

链表的中间节点

typedef struct ListNode
{
	int val;
	struct ListNode* next;
}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;
}

1.3 反转链表

反转链表

#include <stdio.h>

typedef struct ListNode
{
	int val;
	struct ListNode* next;
}ListNode;

struct ListNode* reverseList(struct ListNode* head)
{
	//处理空链表
	if (NULL == head)
	{
		return head;
	}
	
	//先创建三个指针
	ListNode* n1, * n2, * n3;
	n1 = NULL, n2 = head, n3 = head->next;

	//遍历原链表,修改节点指针的指向
	while (n2)
	{
		//修改n2的指向
		n2->next = n1;
		//修改三个指针的位置
		n1 = n2;
		n2 = n3;

		if (n3)
		{
			n3 = n3->next;
		}
	}

	return n1;
}

1.4 合并两个有序链表

合并两个有序链表

#include <stdio.h>

typedef struct ListNode
{
	int val;
	struct ListNode* next;
}ListNode;

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
	if (NULL == list1)
	{
		return list2;
	}

	if (NULL == list2)
	{
		return list1;
	}
	
	ListNode* l1, * l2;
	l1 = list1, l2 = list2;
	ListNode* newHead, * newTail;
	newHead = newTail = NULL;

	while (l1 && l2)
	{
		if (l1->val < l2->val)
		{
			//l1小,插入到新链表中
			if (NULL == newHead)
			{
				//链表为空,头尾指针都指向该节点
				newHead = newTail = l1;
			}
			else
			{
				//链表不为空
				newTail->next = l1;
				newTail = newTail->next;
			}

			l1 = l1->next;
		}
		else
		{
			//l2小,插入到新链表中
			if (NULL == newHead)
			{
				//链表为空
				newHead = newTail = l2;
			}
			else
			{
				//链表不为空
				newTail->next = l2;
				newTail = newTail->next;
			}
			l2 = l2->next;
		}
	}

	//跳出循环存在两种情况:要么l1走到空了l2不为空,要么l2走到空了l1不为空
	//不可能存在都为空的情况
	if (l1)
	{
		newTail->next = l1;
	}

	if (l2)
	{
		newTail->next = l2;
	}

	return newHead;
}

但是我们会发现以上代码在l1小或l2小时把数据插入到新链表中都要判断链表是否为空,出现了代码的重复,我们应该如何优化呢?

代码重复的根源在于链表可能会出现为空的情况,那么我们就创建一个头节点(这里的头就是带头链表中的头,是哨兵位,不存储有效的数值),让链表不可能存在为空的情况,就可以避免代码重复。

#include <stdio.h>
#include <stdlib.h>

typedef struct ListNode
{
	int val;
	struct ListNode* next;
}ListNode;

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
	if (NULL == list1)
	{
		return list2;
	}

	if (NULL == list2)
	{
		return list1;
	}

	ListNode* l1, * l2;
	l1 = list1, l2 = list2;
	ListNode* newHead, * newTail;
	newHead = newTail = (ListNode*)malloc(sizeof(ListNode));

	while (l1 && l2)
	{
		if (l1->val < l2->val)
		{
			//l1小,插入到新链表中
			newTail->next = l1;
			newTail = newTail->next;
			l1 = l1->next;
		}
		else
		{
			//l2小,插入到新链表中
			newTail->next = l2;
			newTail = newTail->next;
			l2 = l2->next;
		}
	}

	//跳出循环存在两种情况:要么l1走到空了l2不为空,要么l2走到空了l1不为空
	//不可能存在都为空的情况
	if (l1)
	{
		newTail->next = l1;
	}

	if (l2)
	{
		newTail->next = l2;
	}

	//malloc了空间,但是这块空间实际用不了,应该将其释放掉
	ListNode* ret = newHead->next;
	free(newHead);
	newHead = newTail = NULL;

	return ret;
}

1.5 分割链表

分割链表

#include <stdio.h>
#include <stdlib.h>

typedef struct ListNode
{
	int val;
	struct ListNode* next;
}ListNode;

struct ListNode* partition(struct ListNode* head, int x)
{
	if (NULL == head)
	{
		return head;
	}

	//创建带头的大小链表
	ListNode* lessHead, * lessTail;
	ListNode* greaterHead, * greaterTail;

	lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));
	greaterHead = greaterTail = (ListNode*)malloc(sizeof(ListNode));

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

	while (pcur)
	{
		if (pcur->val < x)
		{
			//放到小链表中
			lessTail->next = pcur;
			lessTail = lessTail->next;
		}
		else
		{
			//放到大链表中
			greaterTail->next = pcur;
			greaterTail = greaterTail->next;
		}

		pcur = pcur->next;
	}

	greaterTail->next = NULL;

	//大小链表进行首尾相连
	lessTail->next = greaterHead->next;
	ListNode* ret = lessHead->next;
	free(greaterHead);
	greaterHead = greaterTail = NULL;
	free(lessHead);
	lessHead = lessTail = NULL;

	return ret;
}

1.6 环形链表的约瑟夫问题

著名的Josephus问题:
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到⼀个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成⼀个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下⼀个重新报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

环形链表的约瑟夫问题

#include <stdio.h>
#include <stdlib.h>

typedef struct ListNode
{
	int val;
	struct ListNode* next;
}ListNode;

ListNode* BuyNode(int x)
{
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	
	if (NULL == newNode)
	{
		perror("BuyNode");
		exit(1);
	}

	newNode->val = x;
	newNode->next = NULL;

	return newNode;
}

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 ptail;//返回ptail的目的是避免prev为空,解决m = 1时出现的问题;这样写既能知道第一个节点的前驱节点,也能够通过尾节点找到第一个节点
}

int ysf(int n, int m)
{
	//创建不带头单向循环链表
	ListNode* prev = createList(n);//定义prev来接收尾节点指针
	//逢m删除节点
	ListNode* pcur = prev->next;
	int count = 1;

	while (pcur->next != pcur)
	{
		if (m == count)
		{
			//删除当前节点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;
}

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

这里基于单链表实现通讯录项目和之前基于顺序表实现通讯录项目的步骤大致相同,思路是相通的,因此可以参考之前顺序表的应用这篇文章。

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

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

相关文章

【八股】Redisson分布式锁

Redisson分布式锁 主要了解了Redisson分布式锁实现的三个功能&#xff1a; 1.可重入 -> 防止死锁 2.可重试&#xff08;i.e. 非阻塞获取锁&#xff09; 3.自动续约 1. 可重入 原理&#xff1a; 利用Redis的Hash结构&#xff0c;记录了使用当前锁的线程id和重用次数&#…

C++初级----list(STL)

1、 list介绍 1.1、 list介绍 1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 1. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向 其前一…

git 分支-变基

在git中&#xff0c;将一个分支的更改集成到另一个分支有两种主要方式&#xff1a;合并&#xff08;merge&#xff09;和变基&#xff08;rebase&#xff09;。在本节中&#xff0c;将学习什么是变基&#xff0c;如何执行变基操作&#xff0c;为什么它是一个非常强大的工具&…

Web 题记

[极客大挑战 2019]LoveSQL 看到这种就肯定先想到万能密码&#xff0c;试试&#xff0c;得到了用户名和密码 总结了一些万能密码&#xff1a; or 11 oror admin admin-- admin or 44-- admin or 11-- admin888 "or "a""a admin or 22# a having 11# a havin…

vue2知识点1 ———— (vue指令,vue的响应式基础)

vue2的知识点&#xff0c;更多前端知识在主页&#xff0c;还有其他知识会持续更新 Vue 指令 Vue指令是Vue.js中的一个重要概念&#xff0c;用于向DOM元素添加特定行为或功能。Vue指令以v-开头&#xff0c;例如v-bind、v-if、v-for等。 v-bind 动态绑定属性 用法&#xff1a…

Linux sort/uniq/wc

文章目录 1. sort 排序将线程ID从大到小排序 2.uniq 临近去重3.wc word cnt 统计 1. sort 排序 将线程ID从大到小排序 grep -v是反向筛选&#xff0c;利用USER&#xff0c;排除掉首行 awk是打印第1 2列 sort -n是代码以数值大小做排序&#xff0c;不加的话会以字符排序。 -k是…

数通HCIE考试分享:考前心态很重要,心情放松好过一次练习

誉天数通HCIE晚班火热预约中&#xff01;真机实验考前辅导备考资料&#xff0c;名师保驾护航&#xff0c;助你稳定通关&#xff01;识别二维码&#xff0c;即可获取免费试听名额&#xff01; 备考阶段 我是去年10月底完成了笔试考试&#xff0c;在笔试之前就将PY的课程过了一遍…

白话transformer(六)编码器与解码器

B 站视频&#xff1a;https://www.bilibili.com/video/BV1fE421T7tR/?vd_source9e18a9285284a1fa191d507ae548fa01 白话transformer&#xff08;六&#xff09; 1、前言 今天我们将探讨Transformer模型中的两个核心组件&#xff1a;编码器和解码器。我们将通过一个具体的任务…

【数据分析】AHP层次分析法

博主总结&#xff1a;根据每个方案x各准则因素权重累加结果 对比来选择目标。数据主观性强 简介 AHP层次分析法是一种解决多目标复杂问题的定性和定量相结合进行计算决策权重的研究方法。该方法将定量分析与定性分析结合起来&#xff0c;用决策者的经验判断各衡量目标之间能…

微软开源 WizardLM-2,70B优于GPT4-0613,7B持平阿里最新的Qwen1.5-32B

当地时间4月15号&#xff0c;微软发布了新一代大语言模型 WizardLM-2&#xff0c;新家族包括三个尖端型号:WizardLM-2 8x22B, WizardLM-2 70B&#xff0c;和WizardLM-2 7B&#xff0c;作为下一代最先进的大型语言模型&#xff0c;它在复杂聊天、多语言、推理和代理方面的性能有…

为什么还有人再问鸿蒙开发有必要学吗?

前言 学习鸿蒙开发&#xff0c;这事儿真的挺有必要的。鸿蒙操作系统&#xff0c;它厉害就厉害在高性能、可扩展&#xff0c;还特智能。现在智能设备和物联网火得不行&#xff0c;鸿蒙就是要成为这个时代的领头羊。它可不是来跟安卓抢饭碗的&#xff0c;它的眼光可远了&#xf…

4/17 FreeRTOS_day2

1.总结串口的发送和接收功能使用到的函数 端口发送数据 HAL_StatusTypeDef HAL_UART_Transmit( UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout ) UART_HandleTypeDef *huart&#xff1a;指定要使用的串口 const uint8_t *pData&#…

【STM32】嵌入式实验二 GPIO 实验 (前三个设计)

1&#xff0e; 按键亮灯 设计 GPIO 实验项目 1&#xff0c;功能&#xff1a;当按键 KB1 按下时&#xff0c;实验板上全彩发光二极管周边 的发光二极管全亮&#xff0c;当按键 KB2 按下时跑马灯 D0 闪亮。 实验要求基于寄存器的GPIO配置&#xff0c;所以需要手动操作寄存器来配…

Spring5深入浅出篇:Spring动态代理详解

Spring5深入浅出篇:Spring动态代理详解 很多粉丝私信我这个Spring5的课程在哪看,这边是在B站免费观看欢迎大家投币支持一下. 视频地址 Spring动态代理详解 这篇主要介绍MethodBeforeAdvice,MethodInterceptor俩者在动态代理中起到的作用,并且详解俩者区别 额外功能的详解 Met…

重定向原理和缓冲区

文章目录 重定向缓冲区 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 点击跳转到网站。 重定向 内核中为了管理被打开的文件&#xff0c;一定会存在描述一…

【热门话题】常见分类算法解析

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 常见分类算法解析1. 逻辑回归&#xff08;Logistic Regression&#xff09;2. 朴…

【Spring进阶系列丨第十篇】基于注解的面向切面编程(AOP)详解

文章目录 一、基于注解的AOP1、配置Spring环境2、在beans.xml文件中定义AOP约束3、定义记录日志的类【切面】4、定义Bean5、在主配置文件中配置扫描的包6、在主配置文件中去开启AOP的注解支持7、测试8、优化改进9、总结 一、基于注解的AOP 1、配置Spring环境 <dependencie…

1.SCI各模块

1.学会“抄” 写论文&#xff0c;一定要学会“抄”&#xff01;这样才能事半功倍&#xff0c;尤其是对于初次写作的新手&#xff0c;否则写作过程一定会让你痛不欲生&#xff0c;而且写出来的东西就是一坨shi&#xff0c;不仅折磨自己&#xff0c;也折磨导师。 写论文与建大楼…

SparkUI 讲解

目录 Executors Environment Storage SQL Exchange Sort Aggregate Jobs Stages Stage DAG Event Timeline Task Metrics Summary Metrics Tasks &#x1f490;&#x1f490;扫码关注公众号&#xff0c;回复 spark 关键字下载geekbang 原价 90 元 零基础入门 Spar…

OpenCV从入门到精通实战(二)——文档OCR识别(tesseract)

导入环境 导入必要的库 numpy: 用于处理数值计算。 argparse: 用于处理命令行参数。 cv2: OpenCV库&#xff0c;用于图像处理。 import numpy as np import argparse import cv2设置命令行参数 ap argparse.ArgumentParser() ap.add_argument("-i", "--imag…