数据结构笔记--链表经典高频题

news2024/10/7 12:24:17

前言

面经:

        针对链表的题目,对于笔试可以不太在乎空间复杂度,以时间复杂度为主(能过就行,对于任何题型都一样,笔试能过就行);对于面试,时间复杂度依然处在第一位,但要力求空间复杂度最低的算法(突出亮点);

        链表题的重要技巧包括:使用额外的数据结构记录(例如哈希表等),使用快慢指针的思想;

1--反转单向链表

笔试解法:

        借助栈先进后出,可以遍历把结点存到栈中,然后不断出栈,这样结点的顺序就反转了;

        时间复杂度为O(n),空间复杂度为O(n);

#include <iostream>
#include <stack>

struct ListNode {
     int val;
     ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL) return head;
        std::stack<ListNode*> st;
        while(head != NULL){
            st.push(head);
            head = head->next;
        }
        ListNode *new_head = new ListNode(0);
        ListNode *tmp = new_head;
        while(!st.empty()){
            tmp->next = st.top();
            st.pop();
            tmp = tmp->next;
        }
        tmp->next = NULL;
        return new_head->next;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Solution S1;
    ListNode *res = S1.reverseList(Node1);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }

    return 0;
}

面试解法:

        不借助栈或递归,通过迭代将空间复杂度优化为O(1);

        利用一个额外的前驱结点 pre 来存储当前结点 cur 的前一个结点,不断更新 pre 和 cur即可;

#include <iostream>
#include <stack>

struct ListNode {
     int val;
     ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL) return head;
        ListNode *pre = NULL;
        ListNode *cur = head;
        while(cur != NULL){
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Solution S1;
    ListNode *res = S1.reverseList(Node1);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }

    return 0;
}

2--反转单向链表-II

主要思路:

        使用三个指针,指针 pre 指向反转区域外的第一个节点,即上图中的 1;指针 cur 指向当前指针,指针 next 指向 cur 的下一个指针;

        遍历链表,每次将 next 指针头插,具体过程可以参考官方题解;

#include <iostream>

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        ListNode *dummyNode = new ListNode(-1);
        dummyNode->next = head;
        ListNode *pre = dummyNode;
        ListNode *cur;
        ListNode *next;
        // 经过循环之后,pre指向反转区域前的第一个节点
        for(int i = 0; i < left - 1; i++){
            pre = pre->next;
        }
        cur = pre->next; // cur指向反转区域的第一个节点
        for(int i = 0; i < right - left; i++){
            next = cur->next;
            cur->next = next->next; // cur指向next的下一个节点,因为next节点要头插到pre节点后面
            next->next = pre->next; // next节点头插,指向原来的第一个节点
            pre->next = next; // next节点头插到pre节点后面
        }
        return dummyNode->next;
    }
};

int main(){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Solution S1;
    int left = 2, right = 4;
    ListNode *res = S1.reverseBetween(Node1, left, right);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }

    return 0;
}

3--反转双向链表

主要思路:

        与反转单向链表类似,使用 pre,cur 和 next 指向前一个节点,当前节点和后一个节点,不断遍历更新三个指针所指向的节点即可,并修改对应的前驱指针和后驱指针;

#include <iostream>
#include <stack>

struct ListNode {
     int val;
     ListNode *pre;
     ListNode *next;
     ListNode() : val(0), pre(nullptr), next(nullptr) {}
     ListNode(int x) : val(x), pre(nullptr), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), pre(nullptr), next(next) {}
};

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL) return head;
        ListNode *pre = NULL;
        ListNode *cur = head;
        while(cur != NULL){
            ListNode* next = cur->next;
            cur->next = pre;
            cur->pre = next;
            pre = cur;
            cur = next;
        }
        return pre;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Node2->pre = Node1;
    Node3->pre = Node2;
    Node4->pre = Node3;
    Node5->pre = Node4;

    Solution S1;
    ListNode *res = S1.reverseList(Node1);
    while(res != NULL){
        std::cout << res->val << " ";
        if(res->pre != NULL) std::cout << res->pre->val;
        std::cout << std::endl;
        res = res->next;
    }

    return 0;
}

4--打印两个有序链表的公共部分

        给定两个有序链表的头指针 head1 和 head2,打印两个链表的公共部分;要求时间复杂度为O(n),额外空间复杂度要求为 O(1);

主要思路:

        类似于归并排序,由于两个链表时有序的,因此可以使用两个指针 i 和 j 分别指向两个链表;

        对于小的链表节点,指针后移;

        当比较到两个指针相等时,打印节点的值,两个指针 i 和 j 同时后移;

#include <iostream>
#include <vector>

struct ListNode {
     int val;
     ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    std::vector<ListNode*> printlist(ListNode* head1, ListNode* head2) {
        std::vector<ListNode*> res;
        if(head1 == NULL || head2 == NULL) return res;
        ListNode *i = head1;
        ListNode *j = head2;
        while(i != NULL && j != NULL){
            // 小的后移
            if(i->val < j->val) i = i->next;
            else if(i->val > j->val) j = j->next;
            else{ // 相等同时后移
                res.push_back(i);
                i = i->next;
                j = j->next;
            } 
        }
        return res;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(5);

    ListNode *Node4 = new ListNode(0);
    ListNode *Node5 = new ListNode(2);
    ListNode *Node6 = new ListNode(3);
    ListNode *Node7 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;


    Node4->next = Node5;
    Node5->next = Node6;
    Node6->next = Node7;

    Solution S1;
    std::vector<ListNode *> res = S1.printlist(Node1, Node4);
    for(ListNode * node : res) std::cout << node->val << " ";
    return 0;
}

5--回文链表

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

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

相关文章

量化交易可视化(9)-热力图

热力图的含义 热力图是一种用颜色编码数据密度的二维图表。它的含义是通过不同颜色的渐变来显示数据的相对密度或值的大小。 热力图通常用于可视化矩阵或二维表格数据&#xff0c;其中每个单元格的值被映射到一个颜色&#xff0c;从而形成一个色阶。较小的值通常用较浅的颜色表…

许多智能算法并不智能(续)

许多智能算法被认为并不智能&#xff0c;主要是因为它们在某些方面仍然存在一些限制。以下是一些常见的原因&#xff1a; 缺乏常识和理解能力&#xff1a;当前的智能算法主要依赖于大量的数据和模式识别来做出决策&#xff0c;但它们通常缺乏对世界的常识和深层理解。这意味着它…

视觉大模型的全面解析

前言 本文主要围绕Foundational Models&#xff0c;即基础模型&#xff08;通过自监督或半监督方式在大规模数据上训练的模型&#xff0c;可以适应其它多个下游任务。&#xff09;这个概念&#xff0c;向大家全面阐述一个崭新的视觉系统。例如&#xff0c;通过 SAM&#xff0c;…

nbcio-boot因升级mybatis-plus到3.5.3.1和JSQLParser 到4.6引起的online表单开发的数据库导入出错解决

更多功能看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/nbcio-boot 前端代码&#xff1a;https://gitee.com/nbacheng/nbcio-vue.git 在线演示&#xff08;包括H5&#xff09; &#xff1a; http://122.227.135.243:9888 nbcio-boot因升级…

【雕爷学编程】Arduino动手做(01)---干簧管传感器模块2

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

接龙序列(14届)

对于一个长度为 K 的整数数列&#xff1a;A1,A2,...,AK&#xff0c;我们称之为接龙数列当且仅当 Ai 的首位数字恰好等于 Ai−1的末位数字 (2≤i≤K2≤i≤K)。 例如 12,23,35,56,61,11 是接龙数列&#xff1b;12,23,34,56 不是接龙数列&#xff0c;因为 56 的首位数字不等于 34…

matplotlib 笔记:plot 折线图

1 基本用法 import numpy as np import matplotlib.pyplot as plt x np.linspace(0,10,50) y np.tan(x)**2 np.cos(x)plt.plot(x, y) plt.show() 2 linestyle 折线图类型 import numpy as np import matplotlib.pyplot as pltx np.linspace(0,10,50) y np.tan(x)**2 np…

node配置Web 服务器

1、什么是 Web 服务器 Web服务器一般指网站服务器&#xff0c;是指驻留于因特网上某种类型计算机的程序&#xff0c;Web服务器的基本功能就是提供Web信息浏览服务。 它只需支持HTTP协议、HTML文档格式及URL&#xff0c;与客户端的网络浏览器配合。 大多数 web 服务器都支持服…

如何优化测试用例?

在我们日常测试工作中&#xff0c;编写测试用例往往花费较多时间&#xff0c;而且设计的测试用例冗杂和不完整&#xff0c;从而造成用例执行检查不完整&#xff0c;效率低下&#xff0c;不能及时发现项目缺陷。 因此提高测试用例编写和执行效率迫在眉睫&#xff0c;一般来说&am…

Dubbo3.0 Demo

将SpringBoot工程集成Dubbo 1.创建父工程 2.创建子工程consumer&#xff0c;provider 3.初始化工程 4.引入依赖 在provider和consumer中引入dubbo依赖 <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</a…

文心一言 VS 讯飞星火 VS chatgpt (73)-- 算法导论7.1 4题

四、如何修改 QUICKSORT&#xff0c;使得它能够以非递增序进行排序? 文心一言&#xff1a; 快速排序&#xff08;Quicksort&#xff09;是一种常用的排序算法&#xff0c;基于分治策略进行设计。默认情况下&#xff0c;快速排序会以递增序进行排序。若想修改快速排序以实现非…

基于星火和Gradio的聊天机器人

星火大模型官网&#xff1a;https://xinghuo.xfyun.cn/ 1 创建虚拟环境&#xff08;windows&#xff09; conda create -n Gradio python3.8 pip install gradio 中间遇到os报错&#xff0c;解决方案&#xff1a; pip install aiofiles23.2.1 2 代码 SparkDesk.py&#xff1a…

Android Studio跳过Haxm打开模拟器

由于公司权限限制无法安装Haxm&#xff0c;这个时候我们可以试试Arm相关的镜像去跳过Haxm运行模拟器。解决方案&#xff1a;安装API27以下的Arm Image. #ifdef __x86_64__if (sarch "arm64" && apiLevel >28) {APANIC("Avds CPU Architecture %s i…

linux_常用命令

一、日常使用命令/常用快捷键命令 开关机命令 1、shutdown –h now&#xff1a;立刻进行关机 2、shutdown –r now&#xff1a;现在重新启动计算机 3、reboot&#xff1a;现在重新启动计算机 4、su -&#xff1a;切换用户&#xff1b;passwd&#xff1a;修改用户密码 5、logou…

使用IIS服务器部署Flask python Web项目

参考文章 ""D:\Program Files (x86)\Python310\python310.exe"|"D:\Program Files (x86)\Python310\lib\site-packages\wfastcgi.py"" can now be used as a FastCGI script processor参考文章 请求路径填写*&#xff0c;模块选择FastCgiModule&…

web-xss-dvwa

目录 xss&#xff08;reflected&#xff09; low medium high xss(store) low medium high xss(dom) low medium high xss&#xff08;reflected&#xff09; low 没有什么过滤&#xff0c;直接用最普通的标签就可以了 http://127.0.0.1/DVWA-master/vulnerabili…

【神经网络手写数字识别-最全源码(pytorch)】

Torch安装的方法 学习方法 1.边用边学&#xff0c;torch只是一个工具&#xff0c;真正用&#xff0c;查的过程才是学习的过程2.直接就上案例就行&#xff0c;先来跑&#xff0c;遇到什么来解决什么 Mnist分类任务&#xff1a; 网络基本构建与训练方法&#xff0c;常用函数解析…

【C语言】数据在内存中的存储详解

文章目录 一、什么是数据类型二、类型的基本归类三、 整型在内存中的存储1.原码、反码、补码2.大小端(1)什么是大小端(2)为什么会有大小端 四、浮点型在内存中的存储1. 浮点数存储规则 五、练习1.2.3.4.5.6.7. 一、什么是数据类型 我们可以把数据类型想象为一个矩形盒子&#x…

DCMM数据管理成熟度之数据战略-数据战略规划

需要咨询加 &#xff1a;shuirunjj 标准原文 1概述 数据战略规划是在所有利益相关者之间达成共识的结果。从宏观及微观两个层面确定开展数据管理及应用的动因,并综合反映数据提供方和消费方的需求。 2 过程描述 过程描述如下: a) 识别利益相关者,明确利益相关者的需求; …

人机融合智能可化简为遥控+预先规划+重新规划过程

人机融合智能可以被简单描述为人类的遥控、机器的预先规划以及人-机器共同的动态重新规划的过程。 首先&#xff0c;人类的遥控是指人类通过指令、控制和操作来操纵机器的行为和功能。人类可以利用各种界面和输入设备&#xff0c;如键盘、鼠标、触摸屏等&#xff0c;将自己的意…