【数据结构与算法】:10道链表经典OJ

目录

  • 1. 移除链表元素
  • 2. 反转链表
    • 2.1反转指针法
    • 2.2 头插法
  • 3. 合并两个有序链表
  • 4. 分隔链表
  • 5. 环形链表
  • 6. 链表的中间节点
  • 7. 链表中倒数第K个节点
  • 8. 相交链表
  • 9. 环形链表的约瑟夫问题
  • 10. 链表的回文结构

1. 移除链表元素

在这里插入图片描述
思路1:遍历原链表,将 val 所在的节点释放掉。(太麻烦)

思路2:创建新链表,再遍历原链表,找到不为 val 的节点尾插到新链表。

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/342214109d444655a6081115428b345c.png

思路1代码实现如下:

注意:
1.当链表为空时,直接返回NULL即可。
2.当尾插上最后一个有效节点时,此时它的 next 可能还与最后一个节点相链接,一定要断开!


typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
    if (head == NULL)
        return NULL;

    //创建一个新链表
    ListNode* newHead, * newTail;
    newHead = newTail = NULL;
    ListNode* pcur = head;

    //遍历原链表,找不为val的节点尾插
    while (pcur)
    {
        ListNode* next = pcur->next;
        if (pcur->val != val)
        {
            //没有一个节点
            if (newHead == NULL)
            {
                newHead = newTail = pcur;
            }
            else
            {
                //有一个节点以上
                newTail->next = pcur;
                newTail = newTail->next;
            }
        }

        pcur = next;
    }

    if (newTail)
        newTail->next = NULL;

    return newHead;

}

2. 反转链表

在这里插入图片描述

2.1反转指针法

思路:定义三个变量 n1,n2,n3,根据它们的指向关系进行迭代。
在这里插入图片描述

代码实现如下:

注意:
1.当链表为空时,直接返回NULL即可。
2.在迭代过程中别忘记判断 n3 ,防止对空指针解引用。
3.注意循环结束的条件,当 n2 为空时,n1 指向反转后的头,此时循环结束。

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {

    if (head == NULL)
        return NULL;

    ListNode* n1, * n2, * n3;
    n1 = NULL, n2 = head, n3 = n2->next;

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

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

    return n1;
}

2.2 头插法

思路:创建一个新链表,遍历原链表,依次取下原链表的每一个节点头插到新链表中。

代码实现如下:

注意:
1.当链表为空时,直接返回NULL即可。
2.头插时可以不用判断没有节点和有节点的情况。


typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {

    if (head == NULL)
        return NULL;

    ListNode* newHead, * newTail;
    newHead = newTail = NULL;
    ListNode* pcur = head;

    //一个一个拿下来头插
    while (pcur)
    {
        ListNode* next = pcur->next;
        pcur->next = newHead;
        newHead = pcur;
        pcur = next;

    }

    return newHead;
}

3. 合并两个有序链表

在这里插入图片描述

思路:创建一个带哨兵位的新链表,遍历两个原链表,比较两个节点的值,哪个小就先尾插到新链表中。

代码实现如下:

注意:
1.当其中一个链表为空时,返回另一个链表即可。
2.创建哨兵位节点,方便尾插。
3.注意循环结束条件,当其中有一个链表走完时,跳出循环。
4.剩下的没走完的那个链表直接链接到后面。不需要用循环链接,因为它们本来就是连在一起的。
5.别忘记释放释放哨兵位节点,释放前要保存下一个节点。


typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {

    ListNode* n1, * n2;
    n1 = list1;
    n2 = list2;

    if (n1 == NULL)
        return n2;
    if (n2 == NULL)
        return n1;

    //创建哨兵位节点
    ListNode* phead = (ListNode*)malloc(sizeof(ListNode));
    ListNode* newHead, * newTail;
    newHead = newTail = phead;

    while (n1 && n2)
    {
        //比较两个链表中数据的大小,谁小就先尾插到新链表
        if (n1->val < n2->val)
        {
            newTail->next = n1;
            n1 = n1->next;
            newTail = newTail->next;
        }
        else
        {
            newTail->next = n2;
            n2 = n2->next;
            newTail = newTail->next;
        }
    }

    if (n1)
        newTail->next = n1;
    if (n2)
        newTail->next = n2;

    ListNode* ret = newHead->next;
    free(newHead);

    return ret;

}

4. 分隔链表

在这里插入图片描述

思路:创建两个带哨兵位的新链表 less 和 greater 。遍历原链表,把小于x 的节点尾插到 less 链表中,把大于等于x 的节点尾插到greater 链表中。最后把 less 链表中的尾结点的 next 链接到 greater链表中的头结点上。

在这里插入图片描述

代码实现如下:

注意:
1.当链表尾空时,直接返回NULL即可。

2.有可能存在greater 链表中的最后一个结点与原链表中的最后一个结点仍然相链接的情况,一定要断开!


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

    ListNode* lessHead, * lessTail, * greaterHead, * greaterTail;
    ListNode* pcur = head;

    //创建哨兵位节点,方便尾插
    lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));
    greaterHead = greaterTail = (ListNode*)malloc(sizeof(ListNode));
    lessTail->next = NULL;
    greaterTail->next = NULL;

    while (pcur)
    {
        if (pcur->val < x)
        {
            lessTail->next = pcur;
            lessTail = lessTail->next;
        }
        else
        {
            greaterTail->next = pcur;
            greaterTail = greaterTail->next;
        }

        pcur = pcur->next;
    }

    lessTail->next = greaterHead->next;
    greaterTail->next = NULL;

    return lessHead->next;

}

5. 环形链表

在这里插入图片描述

这是一个非常经典的例题,它要用上一种非常经典的算法:快慢指针法

定义一个 slow 变量,fast 变量,从链表的头结点开始,slow 每次走一步,fast 每次走两步,当 slow 进入环中时,fast 开始追逐。若成环,则必会在环内的某处相遇,否则 fast 或是 fast->next 最后会走到NULL。

代码实现如下:

注意:
1.当链表节点个数为偶数个时,若不成环,最终 fast == NULL。
当链表节点个数为奇数个时,若不成环,最终 fast->next == NULL.

2.循环条件 fast && fast->next 的位置不能交换。因为当为偶数个节点,fast走到NULL时,如果是 fast->next && fast ,那就是先执行 fast->next ,对空指针解引用,错误!!


typedef struct ListNode ListNode;
bool hasCycle(struct ListNode* head) {

    ListNode* slow, * fast;
    slow = fast = head;

    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (fast == slow)
            return true;
    }

    return false;
}

6. 链表的中间节点

在这里插入图片描述

也要用快慢指针法。也要分两种情况:
在这里插入图片描述

代码实现如下:

注意:
循环条件 fast && fast->next 的位置不能交换。因为当为偶数个节点,fast走到NULL时,如果是 fast->next && fast ,那就是先执行 fast->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. 链表中倒数第K个节点

在这里插入图片描述

思路:
(1) fast 先走 k 步;
(2) slow 和 fast 再一起走,当 fast == NULL 时,slow 就是倒数第 k 个节点。

代码实现如下:

注意:
当 k 大于链表长度时,fast 会走到 NULL ,不能对空指针解引用,直接返回 NULL。


typedef struct ListNode ListNode;
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
    ListNode* fast ,*slow;
    fast = slow = pHead;
 
    //fast先走K步
    while(k--)
    {
        //K大于链表长度时,直接返回NULL
        if(fast == NULL)
        {
            return NULL;
        }
         
        fast = fast->next;
    }
 
    //再两者一起走
    while(fast)
    {
        fast = fast->next;
        slow = slow->next;
    }
 
    return slow;
    
 }

8. 相交链表

在这里插入图片描述
首先要明确什么是相交链表:
在这里插入图片描述

思路1:暴力求解,时间复杂度O(N*N)
依次取A链表中的每个节点跟B链表中的所有节点比较。如果有相同地址的节点,则相交,第一次相同地址的节点就是交点,否则就不相交。

思路2:时间复杂度O(N)
(1) 先找到两个链表的尾,同时计算出两个链表的长度;
(2) 求出长度差;
(3) 判断哪个是长链表;
(4) 让长链表先走长度差步;
(5) 最后长短链表一起走,直到找到交点。

思路2代码实现如下:

注意:
要注意步骤(3)中判断长短链表的巧妙方法。可以避免写重复代码。

typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    
    ListNode* TailA,*TailB;
    TailA = headA;
    TailB = headB;

    //找尾同时计算长度
    int lenA = 0;
    int lenB = 0;
    while(TailA->next)
    {
        TailA = TailA->next;
        lenA++;
    }

     while(TailB->next)
    {
        TailB = TailB->next;
        lenB++;
    }

    //不相交
    if(TailA != TailB)
    {
        return NULL;
    }

    //求出长度差
    int gap = abs(lenA - lenB);

    //判断哪个是长链表
    ListNode* longList = headA;//先默认A是长链表
    ListNode* shortList = headB;

    if(lenA < lenB)
    {
        shortList = headA;
        longList = headB;
    }

    //让长链表走长度差步
    while(gap--)
    {
        longList = longList->next;
    }

    //最后长短链表一起走,找交点
    while(longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }

    return longList;

}

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

在这里插入图片描述

在这里插入图片描述

思路:
首先要创建一个循环链表,定义一个计数器 count 用于数数,再遍历循环链表,当结点的 val == count 时,就"杀死",即销毁该节点。

代码实现如下:

注意:
要学习创建循环链表的方法!


 typedef struct ListNode ListNode;
   
  //创建节点
  ListNode* BuyNode(int x)
  {
    ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
    if(newnode == NULL)
    {
        exit(-1);
    }
 
    newnode->val = x;
    newnode->next = NULL;
 
    return newnode;
  }
 
 //1.创建循环链表
 ListNode* Createnodecircle(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 ) {
   
     ListNode* prev = Createnodecircle(n);
     ListNode* pcur = prev->next;
     
     //开始数数
     int count = 1;
     while(pcur->next!=prev->next)
     {
         if(count == m)
         {
            prev->next = pcur->next;
            free(pcur);
            pcur = prev->next;
            count = 1;
         }
         else
         {
         prev = pcur;
         pcur = pcur->next;
         count++;
         }
    }
 
         return pcur->val;
     }

10. 链表的回文结构

在这里插入图片描述

这个题在牛客网中不能用C语言实现,我们可以选C++,因为C++是兼容C的,在C++中可以使用C的语法。

在这里插入图片描述

思路:
(1) 找到链表的中间节点;
(2) 把中间节点后面的链表进行逆置;
(3) 最后把逆置后的链表的值与中间节点之前的链表的值进行比较,若所有节点相等,则是回文链表,否则不是。有一个链表结束则比较结束。

代码实现如下:

注意:
逆置完之后,中间节点与前一个结点的链接可以不用断开,因为就算链接在一起也不影响判断。若强行断开,徒增麻烦。


//找中间结点
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;
 
}
 
 //对中间结点之后的链表进行逆置
struct ListNode* reverseList(struct ListNode* head) {
 
    if (head == NULL)
        return NULL;
 
   struct ListNode* n1, * n2, * n3;
    n1 = NULL, n2 = head, n3 = n2->next;
 
    while (n2)
    {
        n2->next = n1;
        n1 = n2;
        n2 = n3;
 
        if (n3)
            n3 = n3->next;
    }
 
    return n1;
}
  
class PalindromeList {
  public:
    bool chkPalindrome(ListNode* A) {
        
    struct ListNode* mid = middleNode(A);
    struct ListNode* rHead = reverseList(mid);
     
    struct ListNode* curA = A;
    struct ListNode* curR = rHead;
 
    //把逆置后的链表与中间结点之前的链表进行比较
    while(curA && curR)
    {
         if(curA->val != curR->val)
         {
            return false;
         }
         else
         {
            curA = curA->next;
            curR = curR->next;
         }
    }
    return true;
 
    }
 };

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

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

相关文章

保姆级教程 | Adobe Illustrator 中插入数学符号

背景 鉴于Adobe Illustrator作为比较专业的绘图/组图软件&#xff0c;我的论文数据作图都会选择先在origin中把原始数据绘制好&#xff0c;后都放入AI中细修。由于在作图过程中需要插入数学符号&#xff0c;但仿佛没有PowerPoint用起来那么熟悉&#xff0c;遂记录下。 步骤 …

【软件设计师】假设系统采用PV操作实现进程同步与互斥。若n个进程共享两台打印机,那么信号量 S的取值范围为 (23) 。2014年下半年第23题。

假设系统采用PV操作实现进程同步与互斥。若n个进程共享两台打印机&#xff0c;那么信号量 S的取值范围为 &#xff08;23&#xff09; 。 答案&#xff1a;D 解析见下图所示&#xff1a;

Maven POM元素解析(二)

一、parent <parent>元素包含定位此项目将从中继承的父项目所需的信息。注意&#xff1a;此元素的子元素不是插值的&#xff0c;必须作为文字值给定。 ElementTypeDescriptiongroupIdString要从中继承的父项目的组id。artifactIdString要从中继承的父项目的项目id。ver…

面向对象的C++题目以及解法2

01串排序 #include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; class String { public:string str; String(const string& s) : str(s) {} int length() const {return str.length();}i…

【零基础入门TypeScript】模块

目录 内部模块 内部模块语法&#xff08;旧&#xff09; 命名空间语法&#xff08;新&#xff09; 两种情况下生成的 JavaScript 是相同的 外部模块 选择模块加载器 定义外部模块 句法 例子 文件&#xff1a;IShape.js 文件&#xff1a;Circle.js 文件&#xff1a;…

力扣101. 对称二叉树(java)

思路&#xff1a; 一、验证 左右子树是否可翻转对称的&#xff1f; 二、分析左右子树情况&#xff1a; 1&#xff09;左右都也空 对称 2&#xff09;左右有一个为空 不对称 3&#xff09;左右都不为空&#xff0c;但数字不同 不对称 4&#xff09;左右都不为空&#xff0c;且数…

bugku-web-安慰奖

提示备份 开始扫后台 得到备份文件index.php.bak 得到php代码 <?phpheader("Content-Type: text/html;charsetutf-8"); error_reporting(0); echo "<!-- YmFja3Vwcw -->"; class ctf {protected $username hack;protected $cmd NULL;public f…

【java】static关键字

类与对象的关系 类是构建对象的模板&#xff0c;一个类可以构建多个对象。 类在方法区当中&#xff0c;对象在堆中。 static修饰的变量是独属于类的变量&#xff0c;没有给对象。 public class Person {private String name;private int age;private static String like;public…

Leetcode刷题(异或)

一、2980. 检查按位或是否存在尾随零 奇数二进制形式最右一位一定为1 偶数二进制形式最右一位一定为0 要存在尾随0至少要两个偶数进行或运算 代码 class Solution:def hasTrailingZeros(self, nums: List[int]) -> bool:cnt 0for x in nums:if x%20:cnt1return True if c…

RK3568 学习笔记 : 更改 u-boot spl 中的 emmc 的启动次序

环境 开发板&#xff1a; 【正点原子】 的 RK3568 开发板 ATK-DLRK3568 u-boot 版本&#xff1a;来自 【正点原子】 的 RK3568 开发板 Linux SDK&#xff0c;单独复制出来一份&#xff0c;手动编译 编译环境&#xff1a;VMware 虚拟机 ubuntu 20.04 问题描述 RK3568 默认 …

大模型面试准备(十七):深入理解 Transformer 技术原理

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何备战、面试常考点分享等热门话题进行了深入的讨论。 合集在这…

Vision GNN: An Image is Worth Graph of Nodes

感受野&#xff1a;在卷积神经网络中,感受野(Receptive Field)是指特征图上的某个点能看到的输入图像的区域,即特征图上的点是由输入图像中感受野大小区域的计算得到的。 感受野并非越大越好&#xff0c;反而可能因为过大而过于发散梯度下降&#xff08;Gradient Descent GD&am…

负载均衡的原理及算法简介

负载均衡&#xff08;Load Balancing&#xff09;是一种用于在多台服务器之间分配网络流量的技术&#xff0c;旨在优化系统资源利用率、提高服务可用性、增强系统的伸缩性和容错能力。其基本原理是将来自客户端的请求分散到一个服务器集群中的各个服务器上&#xff0c;而不是让…

python--4函数def,本质、值传递、引用传递、默认值参数、*参数名、**变量、lambda [参数]: 函数、偏函数、递归、递归练习

学习目标&#xff1a; 函数def,本质、值传递、引用传递、默认值参数、*参数名、**变量、lambda [参数]: 函数、偏函数、递归、 递归练习 学习内容&#xff1a; 函数def,本质、值传递、引用传递、默认值参数、*参数名、**变量、lambda [参数]: 函数、偏函数、递归、 递归练习 …

PyCharm 2024.1 发布:全面升级,助力高效编程!

PyCharm 2024.1 发布&#xff1a;全面升级&#xff0c;助力高效编程&#xff01; 文章目录 PyCharm 2024.1 发布&#xff1a;全面升级&#xff0c;助力高效编程&#xff01;摘要引言 Hugging Face&#xff1a;模型和数据集的快速文档预览针对 JavaScript 和 TypeScript 的全行代…

HTML表格标签、列表标签和表单标签

目录 表格标签 表格的主要作用 表格的基本语法 表头单元格标签 表格外观属性 设计表格的思路 表格标签结构 合并单元格属性 列表标签 无序列表 有序列表 自定义列表 表单标签 表单域 表单控件 button与text password reset与submit checkbox与radio label标…

Jmeter 简单的压力测试

今天我们一起利用Apache Jmeter&#xff08;一种接口测试工具&#xff09;来进行压力测试学习。压力测试主要目的是测试负载均衡的实现效果。 安装Jmeter这里就不做阐述了&#xff0c;上网下载个最新版就可以了&#xff0c;因为Jmeter是由JAVA语言开发的&#xff0c;所以安装之…

2.3 iHRM人力资源 - 路由、左侧菜单栏、处理token失效、退出登录、修改密码

iHRM人力资源 - 处理token失效、退出登录、修改密码 文章目录 iHRM人力资源 - 处理token失效、退出登录、修改密码一、退出登录1.1 处理token失效1.2 调整下拉菜单1.3 退出登录 二、修改密码2.1 弹出层dialog2.2 表单结构2.3 表单校验2.4 表单提交 三、路由3.1 清理多余组件和路…

6本期刊直接被踢!!最新4月SCI/SSCI期刊目录更新,请查收~

又到了一月一度的科睿唯安官网更新Web of Science核心期刊目录的时候&#xff0c;小编今天带大家一起来看看最新的SCI/SSCI期刊目录有哪些变化吧。 继上次SCI期刊目录和SSCI期刊目录更新之后&#xff0c;本次4月更新共有9本期刊发生变动&#xff1a; • SCIE&#xff1a;有5本期…

【Python基础】—— scipy.spatial.KDTree、matplotlib.pyplot、imageio

scipy.spatial参考博客&#xff1a;Python点云处理——建立KDtree 1 KDtree算法原理 KDtree构建出了一种类似于二叉树的树形数据存储结构&#xff0c;每一层都对应原始数据中相应的维度&#xff0c;以K层为一个循环&#xff0c;因此被称为KDtree。 每一层的左右子树的划分依据…
最新文章