【Hello Algorithm】链表相关算法题

本篇博客介绍: 介绍下链表相关的算法题

链表相关算法题

    • 快慢指针
    • 回文结构链表
    • 将单向链表按某值划分为左边小,中间相等,右边大的形式
    • 复制带随机指针的链表

链表相关的算法题其实都算不上难
我们真正要考虑的是一些边界问题 事实上链表题就是在锻炼我们的处理边界能力

其次我们要强调的一点是 在笔试和面试中 我们的解题思路是不同的

在笔试中我们一般追求快速解题 只需要考虑时间复杂度 (一般空间复杂度上不会卡你)

但是在面试中 我们必须要在保证时间复杂度的情况下考虑空间复杂度 因为面试官会根据你写出的代码来判断你对于这个问题真正理解多少 有没有达到要求

快慢指针

比如说现在题目要求我们找出一个链表的中点

常规的解法就是 我们数一下链表的长度是多少

数出链表的长度之后除以二 之后使用指针一步步遍历到中点位置即可

快慢指针法解决中点问题

我们可以设计两个指针 快指针和慢指针

慢指针每次走一步

快指针每次走两步

我们可以通过调整快慢指针谁先走来调整中点

当然其中也有一些边界问题 比如说空指针的引用需要注意

lc上原题的连接如下

链表的中间节点

C++代码如下

class Solution {
public:ListNode* middleNode(ListNode* head) {if (head -> next == nullptr){return head;}ListNode* slow = head;ListNode* fast = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;} return slow;}
};

此外 快慢指针还能解决链表第K个节点之类的问题 思路同上

回文结构链表

正读和反读都相同的字符序列为回文

比如说下面的这一行字符

1  2  3  2  1  

这就是一个回文结构

但是如果我们改变下最后节点的值 变成

1  2  3  2  2 

这就不是一个回文结构了

判断它是不是一个回文结构我们也有多种做法

如果是在笔试中 我们可以选择最简单的 使用一个栈结构去解

具体解法为

  • 我们首先定义一个栈结构
  • 从头开始遍历所有的链表值 将所有的链表值放到栈结构中
  • 这样子我们就得到了一个逆序的链表值
  • 于是我们只要再次从头开始遍历整个链表 并且与栈中的逆序值做对比即可

题目连接和代码表示如下

回文链表

class Solution {
public:bool isPalindrome(ListNode* head) {if (head->next == nullptr){return true;}stack<int> st;ListNode* cur = head;while(cur){st.push(cur->val);cur = cur->next;}cur = head;while(!st.empty()){if (st.top() == cur->val){st.pop();cur = cur->next;}else {return false;}}return true;}
};

但是上面的解法仅限于笔试中 如果在面试中我们遇到了这个问题肯定是面试官想考查我们空间复杂度为O1的解法

其实思路也很简单

  • 找到链表的左中点 并且指向空
  • 翻转整个链表的右半部分(以左中点为结束位置)
  • 设置两个指针 分别从左右开始对比值 如果全部相同 最后返回true 反之返回false
  • 最后一定要记得将链表复原

代码表示如下

class Solution {
public:bool isPalindrome(ListNode* head) {if (head->next == nullptr){return true;}// 0 设置返回值bool ret = true;// 1 找出链表的左中点ListNode* slow = head;ListNode* fast = head;while(fast->next && fast->next->next){fast = fast->next->next;slow = slow->next;}// 2 设置左右中点ListNode* leftmidpoint = slow; ListNode* rightmidpoint= slow->next; // 排除掉了一个节点的情况 不可能为空// 3 开始翻转ListNode* n1 = leftmidpoint;ListNode* n2 = rightmidpoint;ListNode* n3 = rightmidpoint->next; // 有可能为空leftmidpoint->next = nullptr;while(1){n2 ->next = n1;n1 = n2;n2 = n3;if (n3){n3 = n3->next;}else {break;}}ListNode* righthead = n1;ListNode* cur1 = head;ListNode* cur2 = righthead;while (cur1 != nullptr){if (cur1->val == cur2->val){cur2 = cur2->next;cur1 = cur1->next;}else{ret = false;break;}}// 4 还原链表 leftmidpoint->next = rightmidpoint;n1 = righthead;n2 = righthead->next;righthead->next = nullptr;if (n2 == leftmidpoint){return ret;}n3 = n2->next;while(1){   n2->next = n1;n1 = n2;n2 = n3;if (n3 == leftmidpoint){break;}else {n3 = n3->next;}}return ret;;}
};

将单向链表按某值划分为左边小,中间相等,右边大的形式

题目要求如下

链接:https://www.nowcoder.com/questionTerminal/04fcabc5d76e428c8100dbd855761778
来源:牛客网

给定一个链表,再给定一个整数 pivot,请将链表调整为左部分都是值小于 pivot 的节点,中间部分都是值等于 pivot 的节点, 右边部分都是大于 pivot 的节点。
除此之外,对调整后的节点顺序没有更多要求。

解题思路上其实没有难点

  • 我们设置六个指针 分别是小头 小尾 等头 等尾 大头 大尾即可
  • 之后遍历链表 更新上面的指针
  • 最后让这些指针相连
  • 有一个难点就是我们要考虑有区域为空的情况

代码表示如下

# include <bits/stdc++.h>
using namespace std;struct list_node{int val;struct list_node * next;
};
#define Node list_node
int pivot;list_node * input_list(void)
{int n, val;list_node * phead = new list_node();list_node * cur_pnode = phead;scanf("%d%d", &n, &pivot);for (int i = 1; i <= n; ++i) {scanf("%d", &val);if (i == 1) {cur_pnode->val = val;cur_pnode->next = NULL;}else {list_node * new_pnode = new list_node();new_pnode->val = val;new_pnode->next = NULL;cur_pnode->next = new_pnode;cur_pnode = new_pnode;}}return phead;
}
void insertHead(Node *head, Node* x) {if(x==NULL || head==NULL) return;Node* last = head->next;head->next = x;x ->next = last;
}list_node * list_partition(list_node * head, int pivot)
{//在下面完成代码if(head==NULL || head->next ==NULL)return head;Node dummy_head;list_node *first = head;list_node lt,gt,eq;Node *l = &lt,*g = &gt, *e = &eq;lt.next = gt.next = eq.next = NULL;while(first) {if(first ->val == pivot) {e ->next = first,first = first->next,e = e->next,e->next = NULL;}else if(first->val > pivot) {g ->next = first,first = first->next, g = g->next,g->next = NULL;}else {l->next = first,l = l->next,first = first->next, l->next = NULL;}}e->next = gt.next;l->next = eq.next;first = lt.next;while(first) {printf("%d ", first->val);first = first->next;}return lt.next;}int main ()
{list_node * head = input_list();list_partition(head, pivot);return 0;
}

复制带随机指针的链表

题目描述可以直接参考leetcode

我这里就不水字数了

复制随机指针的链表

这个题目在我刚刚接触C++的时候其实也做过一次 博客连接如下

复制带随机指针的链表

这个题目的难点其实只有一个

如何确定随机指针指向的位置 如果随机指针指向的位置一定是向后的话我们倒是可以使用数步数的方式来实现 可要是随机指针指向的是前面呢? 通过遍历去找嘛?

这样子时间复杂度直接上N方了 肯定是过不了的

那么我们解决这个难点的思路有两个

  1. 使用哈希表 哈希表的KEY值对应原节点 VALUE值对应复制的节点 这样子我们只要找到原来的节点的随机指针 再到哈希表中找对应的VALUE值即可
  2. 因为随机指针本质上就是指向该链表中的某一个节点 所以说只要我们能够确定该链表中任意一个节点的相对位置 我们就能够找到复制后的随机指针应该指向哪里 于是我们可以选择在原链表的每个节点后面加上一个复制的节点 这样子我们通过原链表的随机指针找到对应的节点后下一个节点就是我们复制节点的随机指针应该指向的节点了

这两种方法的时间复杂度都是O(N) 但是第一种方法的空间复杂度要高一些

所以说我们在面试中要选用第二种方法 在笔试中可以选择第一种方法

代码表示如下

class Solution {
public:Node* copyRandomList(Node* head) {if (head == nullptr){return nullptr;}// 1 复制节点到原节点后Node* cur = head;Node* NEXT = nullptr; // null?while (cur){NEXT = cur -> next;cur -> next = new Node(cur->val); cur -> next -> next = NEXT;cur = NEXT;}// 2 随机指针开始拷贝 Node* cur2 = head->next;cur = head;while(cur){cur2 = cur->next;if (cur->random){cur2->random = cur->random->next;}else {cur2->random = nullptr;}cur = cur->next->next;}// 3 分离出复制的节点cur2 = head->next;cur = head;NEXT = nullptr;Node* NEXT2 = nullptr;head = cur2;while(cur){NEXT = cur->next->next;if (NEXT== nullptr){NEXT2 = nullptr;}else {NEXT2 = NEXT->next;}cur->next = NEXT;cur2->next = NEXT2;cur = NEXT;cur2 = NEXT2;}return head;}
};

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

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

相关文章

【大数据】数据湖:下一代大数据的发展趋势

数据湖&#xff1a;下一代大数据的发展趋势 1.数据湖技术产生的背景1.1 离线大数据平台&#xff08;第一代&#xff09;1.2 Lambda 架构1.3 Lambda 架构的痛点1.4 Kappa 架构1.5 Kappa 架构的痛点1.6 大数据架构痛点总结1.7 实时数仓建设需求 2.数据湖助力于解决数据仓库痛点问…

5 大虚拟数字人工具:视频内容创作的未来

人工智能&#xff08;AI&#xff09;给视频内容创作领域带来了一场革命。这一领域的显着进步之一是人工智能生成的会说话的化身的出现&#xff0c;它已经成为制作高质量视频的游戏规则改变者&#xff0c;而无需专业演员或昂贵的视频编辑软件。在这篇博文中&#xff0c;我们将深…

python用 xlwings库对Excel进行 字体、边框设置、合并单元格, 版本转换等操作

xlwings 其他的一些单元格读取写入操作网上很多&#xff0c; 下面就写些如何设置单元格的 字体对齐&#xff0c;字体大小、边框&#xff0c; 合并单元格&#xff0c; 这些设置。 import xlwings as xwapp xw.App(visibleTrue, add_bookFalse) app.display_alerts False #…

leetcode455. 分发饼干 【贪心】

题目&#xff1a; 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并且每块饼干 j&#xff0c…

Latex-遇到的各种公式

用的是OverLeaf。 Sigmoid的写法 \begin{equation} \sigma(x) \frac{1}{1 e^{-x}} \end{equation} Softmax的写法 \begin{equation} \sigma(t)_i \frac{e^{t_i}}{\sum\limits_{j1}^{N}e^{t_j}} \end{equation} 下标 用_符号 例子&#xff1a;argmax_y 上标 用^符号…

一次性重复性采购供应商

在企业采购的活动中&#xff0c;有很多物料的采购都是通过一次性的采购进行的&#xff0c;此类的采购活动基本不会重复的进行。这种类型的采购&#xff0c;就叫做一次性采购。提供此种采购的供应商&#xff0c;企业一般不需要对他进行一个复杂的认证工作&#xff0c;而应该以效…

算法系列-力扣876-求链表的中间节点

# 求链表中间节点&#xff0c;如果有两个中间节点取后面那个 链表定义 // lc codestart /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * …

java八股文面试[多线程]——一个线程两次调用start()方法会出现什么情况

典型回答&#xff1a; Java 的线程是不允许启动两次的&#xff0c;第二次调用必然会抛出 IllegalThreadStateException&#xff0c;这是一种运行时异常&#xff0c;多次调用 start 被认为是编程错误。 通过线程的状态图&#xff0c;在第二次调用 start() 方法的时候&#xff…

推荐Java开发常用的工具类库google guava

Guava Guava是一个Google开源的Java核心库&#xff0c;它提供了许多实用的工具和辅助类&#xff0c;使Java开发更加简洁、高效、可靠。目前和hutool一起&#xff0c;是业界常用的工具类库。shigen也比较喜欢使用&#xff0c;在这里列举一下常用的工具类库和使用的案例。 参考…

发表于《自然》杂志:语音转文本BCI的新突破实现62字/分钟的速度

语音脑机接口&#xff08;BCI&#xff09;是一项创新技术&#xff0c;通过用户的大脑信号在用户和某些设备之间建立通信通道&#xff0c;它们在恢复残疾患者的言语和通信能力方面具有巨大潜力。 早期的研究虽然很有希望&#xff0c;但尚未达到足够高的精度来解码大脑活动&…

【斗罗Ⅱ】演员阵容曝光,张予曦披发惊艳,奥斯卡选角出新变革

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫&#xff01; 由周翊然和张予曦主演的《斗罗大陆2》电视剧&#xff0c;目前还在拍摄中。和第一部相比&#xff0c;主演阵容来了个全员大换血。服化道有参考动漫&#xff0c;但是做出来的质感&#xff0c;却引起了很多…

Mycat之前世今生

如果我有一个32核心的服务器&#xff0c;我就可以实现1个亿的数据分片&#xff0c;我有32核心的服务器么&#xff1f;没有&#xff0c;所以我至今无法实现1个亿的数据分片。——MyCAT ‘s Plan 话说“每一个成功的男人背后都有一个女人”&#xff0c;自然MyCAT也逃脱不了这个诅…

Flutter的未来与趋势,23年还学吗?

随着移动应用市场的不断扩大&#xff0c;跨平台开发框架的需求也越来越大。Flutter框架可以帮助开发者在不同平台上快速开发高质量的移动应用程序&#xff0c;这种趋势将进一步推动Flutter的发展和普及。 作为一名前端开发工程师&#xff0c;学习Flutter框架是非常有必要的。因…

五种永久免费 内网穿透傻瓜式使用

方法一(使用qydev) 官网&#xff1a;点击访问 1、官网 页面&#xff1a;找到客户端下载 2、找到自己电脑或者运行平台对应的版本(我的是windows 64位) 3、下载完成后解压到 自己熟悉的文件内保存&#xff0c;解压后&#xff0c;暂时不管她&#xff0c;继续第4步 4、登录官网…

链表OJ练习(1)

一、移除链表元素 本题为力扣原题203 题目介绍&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 列表中的节点数目范围在 0~10000内 1<Node.val<50 0<val<50 …

【 OpenGauss源码学习 —— 列存储(analyze)(二)】

列存储&#xff08;analyze&#xff09; 概述analyze_get_relation 函数VacuumStmt 结构体Relation 结构体代码段解读try_relation_open 函数ConditionalLockRelationOid 函数 analyze_rel_internal 函数BufferAccessStrategy 结构体GBLSTAT_HDFS_SAMPLE_ROWS 结构体do_analyze…

构建简单的Node.js HTTP服务器,发布公网远程访问的快速方法

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

Golang 中的 archive/zip 包详解(二):常用类型

Golang 中的 archive/zip 包用于处理 ZIP 格式的压缩文件&#xff0c;提供了一系列用于创建、读取和解压缩 ZIP 格式文件的函数和类型&#xff0c;使用起来非常方便。 zip.File 类型 定义如下&#xff1a; type File struct {FileHeaderzip *Readerzipr io…

【附安装包】Substance3D 2022安装教程

软件下载 软件&#xff1a;Substance3D版本&#xff1a;2022语言&#xff1a;简体中文大小&#xff1a;4.0G安装环境&#xff1a;Win11/Win10&#xff08;1809版本以上&#xff09;硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff0c;不支持7代以下CPU&#xff09;下载通…

thinkphp6 入门(2)--视图、渲染html页面、赋值

修改模板引擎 config/view.php // 模板引擎类型使用Think type > php, 2. 新建一个控制器 本文app的名称为test&#xff0c;在其下新建一个控制器User app/test/controller/User.php 注意&#xff1a;需要引用think\facade\View来操作视图 <?phpnamespace app\te…