刷题日记 ---- 顺序表与链表相关经典算法题(C语言版)

目录

  • 1. 移除元素
  • 2. 合并两个有序数组
  • 3. 移除链表元素
  • 4. 反转链表
  • 5. 合并两个有序链表
  • 6. 链表的中间结点
  • 7. 环形链表的约瑟夫问题
  • 8. 分割链表
  • 总结


正文开始


1. 移除元素

题目链接: 移除元素

题目描述:
在这里插入图片描述
思路历程:

题目明确要求, 不能使用额外的数组空间, 也就是说不可以创建一个新的数组. 可以使用双指针法, 一个指向源数组, 一个指向目标数组, 定义两个下标, 分别从第一个元素开始向后遍历, 当src所在位置的数组值等于val时, 跳过此元素, 当src指向的数组不等于val值时, 将src位置的元素存放到dst位置, 继续向后遍历, 当遍历到数组结尾, 跳出循环, 此时dst内存放的就是不含val值的数组.

代码描述:

int removeElement(int* nums, int numsSize, int val) {

    int src = 0;
    int dst = 0;
    while(src<numsSize)
    {
        if(nums[src] == val)
        src++;
        else{
            nums[dst] = nums[src];
            dst++;
            src++;
        }
    }
    return dst;
}

2. 合并两个有序数组

题目链接: 合并两个有序数组

题目描述:

在这里插入图片描述

思路历程:

非递减序, 即递增的序列, 定义三个指针, 分别指向第一个数组有效元素的最后一个元素, 第二个数组的最后一个元素, 第一个数组最后的一个位置, 分别为src1 , src2 , dst,
不能采用从前往后遍历, 这样会覆盖掉src1中的内容, 所以从后往前遍历, 创建三个下标, 如果第一个数组元素大于第二个数组元素就方法最后一个位置, 继续向前遍历, 反之亦然, 当跳出循环后, 如果第一个数组还有元素不必理会, 第二个数组还有元素就直接同上直接放到dst里面.

画图演示:

在这里插入图片描述

代码描述:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    int src1 = m-1;
    int src2 = n-1;
    int dst = m+n-1;
    while(src1>=0&&src2>=0)
    {
        if(nums1[src1]>nums2[src2])
        {
            nums1[dst--] = nums1[src1--];
        }else
        {
            nums1[dst--] = nums2[src2--];
        }
    }
        while(src2>=0)
        {
            nums1[dst--] = nums2[src2--];
        }
}

3. 移除链表元素

题目链接: 移除链表元素

题目描述:

在这里插入图片描述

思路历程:

创建一个新的链表, 遍历原链表, 当值不为val时将此节点插入到新的链表中, 为了防止出现链表为NULL会对空指针解引用, 直接创建带头链表, 给上一个哨兵位, 初始化时直接将新链表的next指向NULL, 然后开始循环遍历原链表, 值不相等的时候插入到新的链表中.

切记: 当遍历完成后将新链表的尾指针的指向改为NULL, 不然可能会出现如下情况

在这里插入图片描述

代码描述:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
#include<stdlib.h>
typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
    ListNode* newHead = (ListNode*)malloc(sizeof(ListNode));
    newHead->next = NULL;
    ListNode* newTail = newHead;

    ListNode* pcur = head;
    while(pcur)
    {
        if(pcur->val!=val)
        {
            newTail->next = pcur;
            newTail = newTail->next;
        }
        pcur = pcur->next;
    }
    newTail->next = NULL;

    return newHead->next;
}

4. 反转链表

题目链接: 反转链表

题目描述:

在这里插入图片描述

思路历程:

本题有两种思路, 第一种思路是创建一个新的链表, 将原链表头插, 不够还有另外一种更简单的思路, 直接完成原链表的翻转, 首先创建三个指针变量, 分别指向NULL, 一个元素, 和第一个元素的下一个结点, 只需要每次改变n2让他指向n1, 并且保障下一个结点地址不被丢失, 用n3来记录, 每次遍历一次之后, 继续让n2的next指向n1 , 指向完之后, 让n1 来到 n2, n2 来到n3, n3指向下一个结点.直到n2走到NULL, 此时n1即为新的链表地址.

注意: 最后一次, 当n3走到NULL, 而n2走到最后最后一个结点, 指向n1之后, n2向后走, 但是此时n3不能继续走, 会对NULL解引用,代码报错, 所以要加以判断

画图演示:
在这里插入图片描述

代码描述:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
    ListNode* n1,*n2,*n3;

    if(head==NULL)
    {
        return NULL;
    }
    n1 = NULL;
    n2 = head;
    n3 = head->next;

    while(n2)
    {
        n2->next = n1;
        n1 = n2;
        n2 = n3;
        if(n3)
        {
            n3 = n3->next;
        }
    }
    return n1;
}

5. 合并两个有序链表

题目链接:合并两个有序链表

题目描述:

在这里插入图片描述

思路历程:创建一个新的链表, 带头链表防止指针为NULL,访问越界, 遍历两个链表, 比较两个链表中的val,小的结点插入到新链表中, 跳出循环后, 如果有某一个链表还有值, 直接插入到新的链表之后, 最后返回新链表的第一个有效结点, 并且释放掉自己开辟的内存.

画图演示:

在这里插入图片描述
代码描述:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 //#include<stdlib.h>
 typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    ListNode* newHead = (ListNode*)malloc(sizeof(ListNode));
    newHead->next = NULL;
    ListNode* newTail = newHead;

    while(list1 && list2)
    {
        if(list1->val < list2->val)
        {
            newTail->next = list1;
            newTail = newTail->next;
            list1 = list1->next;
        }else
        {
            newTail->next = list2;
            newTail = newTail->next;
            list2 = list2->next;
        }
    }
    if(list1)
    {
        newTail->next = list1;
    }
    if(list2)
    {
        newTail->next = list2;
    }
    ListNode* ret = newHead->next;
    free(newHead);
    newHead = NULL;
    return ret;
}

6. 链表的中间结点

题目链接:链表的中间结点

题目描述:

在这里插入图片描述

思路历程:

快慢指针, 定义两个指针, 慢指针一次走一步, 快指针一次走两步, 当快指针走到NULL,或者next指向NULL, 此时慢指针即为中间节点.

注意: 使用&&操作符需要先判断fast是否为NULL

画图演示:

在这里插入图片描述

代码描述:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

 typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
    ListNode* slow = head;
    ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    
    return slow;
}

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

题目链接: 环形链表约瑟夫问题

题目描述:

在这里插入图片描述

在这里插入图片描述
思路历程:

如果说我们采用数组的话也可以解决, 不过那太麻烦了, 可以直接创建环形链表.

在这里插入图片描述
分为两步:

第一步创建带环链表,第二步开始计数

首先创建一个带环链表, 可以使用函数创建, 完成之后返回创建好的链表的尾结点, 因为需要找到前一个结点, 返回头结点的话不知道前一个结点是啥, 接着开始计数, 当值为m时移除此节点 , 先让前一个结点的下一个位置指向待删除结点的下一个结点, 之后删除此节点, 并且让pcur指向prev所指向的结点, count从新计数, 当不等于m时,继续遍历结点, 遍历之前先让prev走到pcur保存结点, 再让pcur走到下一个结点, 计数器++,当pcur的下一个节点所指向的结点是本身的话即为要返回的结点.

画图演示:

在这里插入图片描述
代码描述:

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param n int整型 
 * @param m int整型 
 * @return int整型
 */
 #include <stdlib.h>
typedef struct ListNode ListNode;

 ListNode* buyNode(int x)
 {
    ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
    if(newnode==NULL)
    {
        perror("malloc fail");
        exit(1);
    }
    newnode->val = x;
    newnode->next = NULL;
    return newnode;
 }

 ListNode* creatNode(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;
 }

int ysf(int n, int m ) {
    // write code here
    //1.创建带环链表
    ListNode* prev = creatNode(n);
    ListNode* pcur = prev->next;
    int count = 1;
    while(pcur->next != pcur)
    {
        if(count == m)
        {
            prev->next = pcur->next;
            free(pcur);
            pcur = prev->next;
            count = 1;
        }else
        {
            prev = pcur;
            pcur = pcur->next;
            count++;
        }      
    }
    return pcur->val;
}


8. 分割链表

题目链接:分割链表

题目描述:

在这里插入图片描述

思路历程:

本题有三种思路, 其中第三种思路最为简单,创建两个带头结点的新链表, 一个用来存放大于x的结点,一个存放小于x的结点, 最后让小链表的为节点指向大链表的第一个有效位置, 最后将大链表的最后一个结点next置为NULL.返回小链表有效首结点地址.

画图演示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码描述:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){
    ListNode* greaterHead = (ListNode*)malloc(sizeof(ListNode));
    greaterHead->next = NULL;
    ListNode* greaterTail = greaterHead;
    ListNode* lessHead = (ListNode*)malloc(sizeof(ListNode));
    lessHead->next = NULL;
    ListNode* lessTail = lessHead;

    ListNode* pcur = head;
    while(pcur)
    {
        if(pcur->val >= x)
        {
            greaterTail->next = pcur;
            greaterTail = greaterTail->next;
        }
        else
        {
            lessTail->next = pcur;
            lessTail = lessTail->next; 
        }
        pcur = pcur->next;
    }
    greaterTail->next = NULL;
    lessTail->next = greaterHead->next;
    return lessHead->next;
}


总结

以上是对顺序链表学习中比较经典的算法题, 里面的算法思想很值得学习, 并且涵盖了一些细节和错误, 使得对顺序表和链表的理解更加深刻

如果此文有帮助, 感谢关注, 点赞 ! ! !

在这里插入图片描述

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

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

相关文章

新时代凌迟:考研

我不喜欢上班&#xff0c;但我很欣赏老板的品味&#xff0c;因为咱们公司竟然还在订阅报纸&#xff0c;而且只有一份&#xff0c;《中国青年报》。 这份报纸我最喜欢看的是“冰点周刊”专栏&#xff0c;因为这个栏目能让读者相信&#xff1a;报纸远远可以超越一天的生命。 昨天…

类和对象【三】析构函数和拷贝构造函数

文章目录 析构函数析构函数的定义析构函数的作用主要作用次要作用 析构函数的特点 拷贝构造函数拷贝构造函数的定义拷贝构造函数的作用主要作用次要作用 拷贝构造函数的特点浅拷贝和深拷贝浅拷贝深拷贝 拷贝构造函数的调用场景 析构函数 析构函数的定义 析构函数(destructor) …

stm32 hid自定义接收发送程序开发过程记录

cubleMX配置如下 修改端点描述符一次传输的数据大小 根据cubelMX标准在这里修改 编译错误 直接修改&#xff08;因为没有使用nodef &#xff09;编译通过 修改报告描述符&#xff08;默认的描述符无法传输数据&#xff09; 参考&#xff1a;USB协议详解第10讲&#xff08;USB描…

C++从入门到精通——string类

string类 前言一、为什么学习string类C语言中的字符串示例 二、标准库中的string类string类string类的常用接口说明string类对象的常见构造string类对象的容量操作string的接口测试及使用string类对象的访问及遍历操作下标和方括号遍历范围for遍历迭代器遍历相同的代码&#xf…

元宇宙APP搭建重点,会用到哪些三方服务?

元宇宙APP的搭建是一个综合性的项目&#xff0c;涉及到众多关键要素和第三方服务。以下是一些元宇宙APP搭建的重点&#xff0c;以及可能用到的第三方服务&#xff1a; 一、搭建重点 技术框架的选择与搭建&#xff1a;元宇宙APP需要稳定、高效的技术框架来支撑其运行。这包括前…

上位机图像处理和嵌入式模块部署(树莓派4b与视觉slam十四讲)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 实际使用中&#xff0c;树莓派4b是非常好的一个基础平台。本身板子价格也不是很贵&#xff0c;建议大家多多使用。之前关于vslam&#xff0c;也就是…

Leetcode刷题-(36~40)-Java

算法是码农的基本功&#xff0c;也是各个大厂必考察的重点&#xff0c;让我们一起坚持刷算法题吧。 遇事不决&#xff0c;可问春风&#xff0c;春风不语&#xff0c;即是本心。 我们在我们能力范围内&#xff0c;做好我们该做的事&#xff0c;然后相信一切都事最好的安排就可…

深入理解Python协程:从基础到实战

title: 深入理解Python协程&#xff1a;从基础到实战 date: 2024/4/27 16:48:43 updated: 2024/4/27 16:48:43 categories: 后端开发 tags: 协程异步IO并发编程Pythonaiohttpasyncio网络爬虫 第1章&#xff1a;协程基础 1.1 协程概念介绍 协程&#xff08;Coroutines&…

【科学研究】读博:一场精神赌博❓

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

C++必修:类与对象(一)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. 面向过程与面向对象 1.1. 面向过程 我们之前学习的C语言就是一种面向过程的语…

java中http调用组件深入详解

目录 一、前言 二、http调用概述 2.1 什么是http调用 2.1.1 http调用步骤 2.2 HTTP调用特点 2.3 HTTP调用应用场景 三、微服务场景下http调用概述 3.1 微服务开发中http调用场景 3.2 微服务组件中http的应用 四、常用的http调用组件 4.1 java中常用的http组件介绍 4…

用 Python 创建 Voronoi 图

概述 最常见的空间问题之一是找到距离我们当前位置最近的兴趣点 (POI)。假设有人很快就会耗尽汽油&#xff0c;他/她需要在为时已晚之前找到最近的加油站&#xff0c;解决这个问题的最佳解决方案是什么&#xff1f;当然&#xff0c;驾驶员可以检查地图来找到最近的加油站&…

力扣每日一题-总行驶距离-2024.4.25

力扣题目&#xff1a;总行驶距离 题目链接: 2739.总行驶距离 题目描述 代码思路 直接用数学模拟计算即可 代码纯享版 class Solution {public int distanceTraveled(int mainTank, int additionalTank) {int sum 0;while(additionalTank > 0){if(mainTank > 5){mai…

CATO原理中的数学与魔术(六)——Baby Hummer的拓展一

在上一篇中&#xff0c;我们从CATO原理的数学讲解进入了魔术部分&#xff0c;介绍了其经典作品《Baby Hummer》&#xff0c;相关内容请戳&#xff1a; CATO原理中的数学与魔术&#xff08;五&#xff09;——Baby Hummer CATO原理中的数学与魔术&#xff08;四&#xff09;——…

leetcode 221 最大正方形面积

示例 3&#xff1a; 输入&#xff1a;matrix [["0"]] 输出&#xff1a;0 # 最大正方形面积 def max_square(matrix):m len(matrix)n len(matrix[0])if m 0 or n 0::return Nonemax_side 1dp [[0] * (n 1) for _ in range(m 1)]for i in range(1, m 1):fo…

2024全新瀚海跑道:矢量图片迅速养号游戏玩法,每天一小时,日转现200

最初我注意到这种玩法&#xff0c;是因为最近在浏览各大平台的视频时&#xff0c;我发现了一种特殊类型的账号&#xff0c;其养号成功率高达90%。这些账号发布的视频内容和数据非常夸张&#xff0c;而且制作起来非常简单&#xff0c;任何人都可以轻松上手。这些账号主要发布矢量…

Spring Web MVC入门(2)——请求

目录 一、传递单个参数 基础类型和包装类型的区别 1、基础类型 &#xff08;1&#xff09;不传参 &#xff08;2&#xff09;传字符串 2、包装类型 &#xff08;1&#xff09;不传参 &#xff08;2&#xff09;传字符串 3、小结 二、传递多个参数 三、传递对象 四、…

Leetcode_相交链表

✨✨所属专栏&#xff1a;LeetCode刷题专栏✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 题目&#xff1a; 题解&#xff1a; 看到这个题目首先我们要排除链表逆置的想法&#xff0c;如图、因为c1节点只有一个next指针&#xff0c;逆置后不可能同时指向a2和b3节点。 其次有的的同学…

阿里前端常考vue面试题汇总_阿里高级vue面试题

改变 ![](https://img-blog.csdnimg.cn/img_convert/b736620bcd29f08f3685022ab5583d8b.webp?x-oss-processimage/format,png)你会发现&#xff0c; **只有改变的栏目才闪烁&#xff0c;也就是进行重绘** &#xff0c;数据没有改变的栏目还是保持原样&#xff0c;这样就大大节…

WebSocket 深入浅出

WebSocket 深入浅出 1. WebSocket 是什么2. WebSocket 建立连接通信的过程3. WebSocket 和http的联系与区别4. WebSocket 的使用场景及限制 1. WebSocket 是什么 定义&#xff1a;WebSocket 是一种网络通信协议&#xff0c;它允许在单个TCP连接上进行全双工通信。是HTML5规范提…