数据结构——链表(练习题)

大家好,我是小锋我们继续来学习链表。

我们在上一节已经把链表中的单链表讲解完了,大家感觉怎么样我们今天来带大家做一些练习帮助大家巩固所学内容。

1. 删除链表中等于给定值 val 的所有结点

. - 力扣(LeetCode)

我们大家来分析一下这个题,我们能想到的思路有两种,1,删除 2,插入

第一种,我们直接一个一个找当找到val我们就删除,然后继续重复下去。

第二种,我们创建一个新的头节点并沿着原节点一个一个走下去如果不是val我们就在新的头节点尾插。

第一种

#include<stdlib.h>
 struct ListNode* removeElements(struct ListNode* head, int val) {
     struct ListNode* ps = head;
     while (ps)
     {
         if (ps->val == val)
         {
             head = head->next;
             ps->next = NULL;
             ps = head;
         }
         else
         {
             struct ListNode* pt = ps->next;
             if (pt) 
             {
                 if (pt->val == val)
                 {
                     ps->next = pt->next;
                     pt->next = NULL;
                 }
                 else
                 {
                     ps = ps->next;
                 }

             }
             else 
             {
                 ps = ps->next;
             }
         }
     }
     return head;
 }

第二种

#include<stdlib.h>
 struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* pt=NULL;
    struct ListNode* ps=NULL;
    struct ListNode* cur=head;
    while(cur){
        if(cur->val==val){
            cur=cur->next;
        }
        else{
            if(ps){
                pt->next=cur;
                cur=cur->next;
                pt=pt->next;
                pt->next=NULL;
            }
            else{
                 ps=cur;
                cur=cur->next;
                ps->next=NULL;
                pt=ps;
            }
        }
    }
    return ps;
 }

反转一个单链表

. - 力扣(LeetCode)

我们大家来分析这道题,有很多种思路这里我向大家推荐两种

1,我们将链表的朝向改变让最后一个节点变成表头,然后依次改变每个节点的指向就完成了反转

2,我们找到最后一个节点然后依次将每个节点尾插。

第一种

# include<stdio.h>
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode*ps=head;
    if(ps){
    struct ListNode*pt=ps->next;
    while(pt)
    {
         struct ListNode*cur=pt->next;
        if(ps==head)
        {
            pt->next=ps;
            ps->next=NULL;
            ps=pt;
            pt=cur;
        }
        else
        {
            pt->next=ps;
            ps=pt;
            pt=cur;
        }
    }
    }
    else{
        return NULL;
    }
    return ps;
}

第二种

# include<stdio.h>
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode*ps=NULL;
    struct ListNode*cur=head;
    struct ListNode*pt=NULL;
    struct ListNode*tall=NULL;
    if(cur)
    {
        while(cur)
        {
            if(cur->next){
        while(cur->next->next)
        {
            cur=cur->next;
        }
        tall=cur;
        cur=cur->next;
        tall->next=NULL;
        }
        else
        {
            head=NULL;
        }
        if(ps)
        {
            
             pt->next=cur;
            pt=pt->next;
        }
        else
        {
            ps=cur;
            pt=ps;
        }
        cur=head;
    }
    }
    else
    {
        return NULL;
    }
    return ps;
}

返回链表的中间结点

. - 力扣(LeetCode)

这一道题的思路也有很多,我们这里主要用快慢指针的思路来解决

我们创建两个指针,都指向头节点,然后一个指针一次走两步,一个指针一次走一步当快的指针走到最后一个节点是慢的指针刚好走到中间节点。

struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode*quick=head;
    struct ListNode*slow=head;
    if(head){
        while(quick&&quick->next){
            quick=quick->next->next;
            slow=slow->next;   
            }
    }
    else{
        return NULL;
    }
    return slow;
}

输入一个链表,输出该链表中倒数第k个结点

这道题的思路在头节点定义两个指针先让一个指针先走k个然后再一起走当其中一个指针走到链表末尾时另一个指针就在倒数第k个。

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

 struct ListNode* middleNode(int k, struct ListNode* head) {
	 struct ListNode* ps = head;
	 struct ListNode* pt = head;
	 if (head) {
		 while (k) {
			 if (ps) {
				 ps = ps->next;
			 }
			 else {
				 break;
			 }
			 k--;
		 }
		 while (ps) {
			 ps = ps->next;
			 pt = pt->next;
		 }
	 }
	 else {
		 return NULL;
	 }
	 if (k<=0) {
		 return pt;
	 }
	 else {
		 return NULL;
	 }
 }

下面这个是测试函数

int main() {
	 ListNode* n1 = (ListNode*)malloc(sizeof(ListNode));
	assert(n1);
	ListNode* n2 = (ListNode*)malloc(sizeof(ListNode));
	assert(n2);
	ListNode* n3 = (ListNode*)malloc(sizeof(ListNode));
	assert(n3);
	ListNode* n4 = (ListNode*)malloc(sizeof(ListNode));
	assert(n4);
    ListNode* n5 = (ListNode*)malloc(sizeof(ListNode));
    assert(n5);
	n1->val = 1;
	n2->val = 2;
	n3->val = 3;
    n4->val = 4;
    n5->val = 5;
	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
    n4->next = n5;
    n5->next = NULL;
	ListNode* add= middleNode(6,NULL);
	if (add)
		printf("%d", add->val);
	else
		printf("NULL");


	return 0;
}

有序链表合并

. - 力扣(LeetCode)

这道题的解题思路

创建一个新的头节点然后建立两个指针分别指向两个升序链表对比两个节点的val,选小的尾插选中的那个节点的指针指向下一个节点,最后还没有走完的指针将全部节点都尾插。

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
     struct ListNode*ps=NULL;
    struct ListNode*cur=NULL;
    if(list1==NULL)
    return list2;
    if(list2==NULL)
    return list1;
    while(list1&&list2){
        if(list1->val>list2->val){
            if(ps){
                cur->next=list2;
                cur=cur->next;
                list2=list2->next;
            }
            else{
                ps=list2;
                cur=ps;
                list2=list2->next;
            }
        }
        else{
            if(ps){
                cur->next=list1;
                cur=cur->next;
                list1=list1->next;
            }
            else{
                ps=list1;
                cur=ps;
                list1=list1->next;
            }
        }
    }
    if(list1){
        cur->next=list1;
    }else{
        cur->next=list2;
    }
    return ps;
}

链表分割

这一道题我想的思路是创建一个头节点,把小于x的节点在原链表中删除后尾插该节点,然后在把该链表与原链表连接。

但是还有一种更好的方法,我们直接创建两个头节点分别尾插大于x和小于x的节点最后再连接。

无疑这一种方法更为简单。

我们来试试

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        ListNode*ps,*pt,*psd,*ptd;
        psd=ps=(ListNode*)malloc(sizeof(int)+sizeof(ListNode*));
        ptd=pt=(ListNode*)malloc(sizeof(int)+sizeof(ListNode*));
        ListNode*cur=pHead;
        while(cur){
        if(cur->val<x){
            psd->next=cur;
            psd=cur;
        }
        else{
            ptd->next=cur;
            ptd=cur;
        }
        cur=cur->next;
        }
        psd->next=pt->next;
        ptd->next=NULL;
        pHead=ps->next;
        free(ps);
        free(pt);
        return pHead;
    }
};

链表的回文结构

这一道题我们可以找到链表的中间节点把中间节点后面的反转然后从头与尾向中间比较如果都一样那么就是回文结构了。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        ListNode*ps=A;
        ListNode*pt=A;
        while(ps&&ps->next){
            ps=ps->next->next;
            pt=pt->next;
        }
        ListNode*cur=pt;
        ListNode*list=cur->next;
        ListNode*add=list->next;
        while(list){
            list->next=cur;
            cur=list;
            list=add;
            if(add)
            add=add->next;
        }
        pt->next=NULL;
        ListNode*are=A;
        while(cur){
            if(are->val!=cur->val){
                return false;
            }
            else{
            are=are->next;
            cur=cur->next;
            }
        }
        return true;
    }
};

相交链表

这道题我们的思路是分别找出长的链表与短的链表节点个数然后用长的节点数减去短的节点数得到的数就是长的链表比短的链表多出的节点个数然后创建两个指针long,short,long先走多出的个数然后再一起走当long与short指向的next相等时就找到了相交节点。

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int a=0;
    int b=0;
    int n=0;
    struct ListNode *cut=headA;
    while(cut->next){
        a++;
        cut=cut->next;
    }
    cut=headB;
    while(cut->next){
        b++;
        cut=cut->next;
    }
    struct ListNode *longg=headA;
    struct ListNode *shortt=headB;
    if(a<b){
        n=b-a;
         while(n--){
        shortt=shortt->next;
    }
    }
    else{
        n=a-b;
         while(n--){
        longg=longg->next;
    }
    }
    while(longg&&shortt){
        if(longg==shortt){
            return longg;
        }
        else{
             longg=longg->next;
             shortt=shortt->next;
        }
    }
    return NULL;
}

判断环形链表

让我们来看看这道题,我的思路是快慢指针,类似追击问题,我们创建两个指针一个指针以一次两个节点的速度走下去,一个指针一次一个节点走下去,如果是有环的链表那么指针一定会相交,如果不是环形链表那么快的会遇到NULL。

bool hasCycle(struct ListNode *head) {
    struct ListNode *quick=head;
    struct ListNode *siow=head;
    while(quick&&quick->next){
        quick=quick->next->next;
        siow=siow->next;
        if(quick==siow){
            return true;
        }
    }
    return false;  
}

找环形链表入口

先说一个结论:

让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环
运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇
这样看这道题是不是清晰明了
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *quick=head;
    struct ListNode *siow=head;
    while(quick&&quick->next){
        quick=quick->next->next;
        siow=siow->next;
        if(quick==siow){
            struct ListNode *ps=head;
            while(quick!=ps){
                quick=quick->next;
                ps=ps->next;
            }
            return ps;
        }

    }
    return NULL;
}

接下来大家思考几个问题

快指针一次走 3 步,走 4 步, ...n 步行吗?

  以上就是全部内容了,如果有错误或者不足的地方欢迎大家给予建议。 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

登录拦截器

目录 &#x1f388;1.登陆拦截器的使用 &#x1f38a;2.ThreadLocal的简单使用 &#x1f383;3.登录拦截器拦截和放行配置 1.登陆拦截器的使用 创建一个拦截器类&#xff0c;必须让其实现HandlerInterceptor接口 1.获取前端的token 2.判断token是否为空 3.若为空&#xff…

蓝桥杯基础练习详细解析(四)——Fibonacci费伯纳西数列(题目分析、代码实现、Python)

试题 基础练习 Fibonacci数列 提交此题 评测记录 资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 Fibonacci数列的递推公式为&#xff1a;FnFn-1Fn-2&#xff0c;其…

CI/CD实战-jenkins结合ansible

配置主机环境 在jenkins上断开并删除docker1节点 重新给master添加构建任务 将server3&#xff0c;server4作为测试主机&#xff0c;停掉其上后面的docker 在server2&#xff08;jenkins&#xff09;主机上安装ansible 设置jenkins用户到目标主机的免密 给测试主机创建用户并…

swagger/knife4j 接口文档增加图标 springboot

1.在资源目录下增加图标文件 2.配置/favicon.ico 资源 Configuration public class WebConfig implements WebMvcConfigurer {Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/favicon.ico").addResour…

小程序利用WebService跟asp.net交互过程发现的问题并处理

最近在研究一个项目&#xff0c;用到asp.net跟小程序交互&#xff0c;简单的说就是小程序端利用wx.request发起请求。获取asp.net 响应回来的数据。但经常会报错。点击下图的测试按钮 出现如下错误&#xff1a; 百思不得其解&#xff0c;试了若干方法&#xff0c;都不行。 因为…

axios发送get请求但参数中有数组导致请求路径多出了“[]“的处理办法

一、情况 使用axios发送get请求携带了数组参数时&#xff0c;请求路径中就会多出[]字符&#xff0c;而在后端也会报错 二、解决办法 1、安装qs 当前项目的命令行中安装 npm install qs2、引入qs库(使用qs库来将参数对象转换为字符串) // 全局 import qs from qs Vue.proto…

Vite 为什么比 Webpack 快?

目录 1. Webpack 的构建原理 2. Script 的模块化&#xff08;主流浏览器对 ES Modules 的支持&#xff09; 3. Webpack vs Vite 开发模式的差异 对 ES Modules 的支持 底层语言的差异 热更新的处理 1. Webpack 的构建原理 前端之所以需要类似于 Webpack 这样的构建工具&…

智慧工地安全生产与风险预警大平台的构建,需要哪些技术?

随着科技的不断发展&#xff0c;智慧工地已成为现代建筑行业的重要发展趋势。智慧工地方案是一种基于先进信息技术的工程管理模式&#xff0c;旨在提高施工效率、降低施工成本、保障施工安全、提升施工质量。一般来说&#xff0c;智慧工地方案的构建&#xff0c;需要通过集成物…

kubernetes(K8S)学习(一):K8S集群搭建(1 master 2 worker)

K8S集群搭建&#xff08;1 master 2 worker&#xff09; 一、环境资源准备1.1、版本统一1.2、k8s环境系统要求1.3、准备三台Centos7虚拟机 二、集群搭建2.1、更新yum&#xff0c;并安装依赖包2.2、安装Docker2.3、设置hostname&#xff0c;修改hosts文件2.4、设置k8s的系统要求…

【IP 组播】PIM-SM

目录 原理概述 实验目的 实验内容 实验拓扑 1.基本配置 2.配置IGP 3.配置PIM-SM 4.用户端DR与组播源端DR 5.从RPT切换到SPT 6.配置PIM-Silent接口 原理概述 PIM-SM 是一种基于Group-Shared Tree 的组播路由协议&#xff0c;与 PIM-DM 不同&#xff0c;它适合于组播组成…

SpringMVC第一个helloword项目

文章目录 前言一、SpringMVC是什么&#xff1f;二、使用步骤1.引入库2.创建控制层3.创建springmvc.xml4.配置web.xml文件5.编写视图页面 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; SpringMVC 提示&#xff1a;以下是本篇文章正文内容&#xf…

如何创建纯净版Django项目并启动?——让Django更加简洁

目录 1. Django的基本目录结构 2. 创建APP 2.1 创建app 2.2 配置文件介绍 3. 迁移数据库文件 3.2 连接数据库 3.1 创建迁移文件 3.2 同步数据库 4. 纯净版Django创建 4.1 剔除APP 4.2 剔除中间件 4.3 剔除模板引擎 5. 最终 1. Django的基本目录结构 在我们创建Django项…

吴恩达机器学习笔记 三十 什么是聚类 K-means

聚类(clustering)是一种无监督学习算法&#xff0c;关注多个数据点并自动找到相似的数据点&#xff0c;在数据中找到一种特定的结构。无监督学习算法的数据集中没有标签 y &#xff0c;所以不能说哪个是“正确的 y ”。 K-means算法 K-means算法就是在重复做两件事&#xff1a…

k8s 如何获取加入节点命名

当k8s集群初始化成功的时候&#xff0c;就会出现 加入节点 的命令如下&#xff1a; 但是如果忘记了就需要找回这条命令了。 kubeadm join 的命令格式如下&#xff1a;kubeadm join --token <token> --discovery-token-ca-cert-hash sha256:<hash>--token 令牌--…

【NLP笔记】大模型prompt推理(提问)技巧

文章目录 prompt概述推理&#xff08;提问&#xff09;技巧基础prompt构造技巧进阶优化技巧prompt自动优化 参考链接&#xff1a; Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing预训练、提示和预测&#xff1a;NL…

Qt打印系统库的日志 - QLoggingCategory

Qt的动态库通过源码可以可以看到含有大量的qCInfo 和 qCDebug 等大量的日志&#xff0c; 但是我们正常运行Qt程序&#xff0c;这些动态库或插件里面的日志是不会输出到我们的控制台里面的。 所以本章主要记录怎么输出这些日志出来。 一&#xff1a; 步骤 主要使用的是Qt的 函…

磐启/PAN7030/2.4GHz 无线收发SOC芯片/ESSOP10/SOP16

1 概述 PAN7030 是一款集成 8 位 OTP MCU 和 2.4GHz 无线收发电路芯片&#xff0c;适合应用于玩具小车、 遥控器等领域。 PAN7030 内置 8 位 OTP MCU&#xff0c;包括 1.25KW 的程序存储器、80 字节数据存储器、16 位定 时器和 8 位/11 位 PWM 定时器、看门狗、电压比较器等…

OpenCV 如何使用 XML 和 YAML 文件的文件输入和输出

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;如何利用OpenCV4.9离散傅里叶变换 下一篇: 目标 本文内容主要介绍&#xff1a; 如何使用 YAML 或 XML 文件打印和读取文件和 OpenCV 的文本条目&#xff1f;如何对 OpenCV …

Apache Hive的基本使用语法(二)

Hive SQL操作 7、修改表 表重命名 alter table score4 rename to score5;修改表属性值 # 修改内外表属性 ALTER TABLE table_name SET TBLPROPERTIES("EXTERNAL""TRUE"); # 修改表注释 ALTER TABLE table_name SET TBLPROPERTIES (comment new_commen…

JAVA的NIO和BIO底层原理分析

文章目录 一、操作系统底层IO原理1. 简介2. 操作系统进行IO的流程 二、BIO底层原理1. 什么是Socket2. JDK原生编程的BIO 三、Java原生编程的NIO1. 简介2. NIO和BIO的主要区别3. Reactor模式4. NIO的三大核心组件5. NIO核心源码分析 一、操作系统底层IO原理 1. 简介 IO&#x…