LeetCode(剑指offer) DAY2

news2024/11/18 10:27:19

1.题目 从尾到头打印链表

解法一:先统计链表有多少节点,然后创建数组,再次遍历链表将值存储至数组,然后反转数组。这种解法思路简单,但是时间复杂度较高。

class Solution {
    int a = 0,b=0;
    public int[] reversePrint(ListNode head) {
        ListNode first = head;
        while (head != null) {
            head = head.next;
            a++;
        }
        int[] arr = new int[a];
        while (first!= null) {
            arr[b] = first.val;
            first = first.next;
            b++;
        }
        for (int j = 0; j < arr.length/2; j++) {
            int temp = arr[j];
            arr[j] = arr[arr.length - j - 1];
            arr[arr.length - j - 1] = temp;

        }
        return arr;
    }
}

 解法二:利用递归,先走至链表末端,回溯时依次将节点值加入列表 ,这样就可以实现链表值的倒序输出。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    int a = 0, b = 0;
    int[] arr = null;
    public int[] reversePrint(ListNode head) {
        flag(head);
        return arr;
    }
    public void flag(ListNode node) {
        if (node == null) {
            arr = new int[a];
            return;
        }
        a++;
        flag(node.next);
        arr[b] = node.val;
        b++;
    }
}

解法三: 题目要求倒序输出节点值,这种先入后出的需求可以借助栈来实现。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    
    public int[] reversePrint(ListNode head) {
       Stack<Integer> s = new Stack<>();
        while (head != null) {
            s.push(head.val);
            head = head.next;
        }
        int[] arr = new int[s.size()];
        for (int j = 0; j < arr.length; j++) {
            arr[j] = s.pop();
        }
        return arr;
    }
}

2.题目二:反转链表

解法一:

在遍历链表时,将当前节点的 next\textit{next}next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    ListNode last,current,next=null;
    public ListNode reverseList(ListNode head) {
        current=head;
        while(current!=null){
            next=current.next;//保存当前节点指向的下一个节点的指针
            current.next=last;//当前节点指针反转,使当前节点的指针指向前一个节点
            last=current;//移动指针:当前节点为前一个节点
            current=next;//移动指针:下一个节点为当前节点
        }
        return last;
    }
}

解法二:使用递归的方法,这种解法思路不易想到

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}

解法三:利用栈先进后出的特点,反转链表,这种解法思路简单,但是算法时空复杂度都较高。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null){//若不判断为空,可能会引起栈为空而继续出栈的情况,后面代码报错
            return null;
        }
        Stack<ListNode> stack=new Stack<>();
        while(head!=null){
            stack.push(head);
            head=head.next;
        }
        ListNode flag=stack.pop();//弹出新的头结点作为已反转链表,其中flag为该反转链表的尾节点
        ListNode newHead=flag;//复制一份头结点用以保存
        ListNode current=null;
        while(!stack.isEmpty()){
            current=stack.pop();
            flag.next=current;//将当前节点链接到反转链表的尾节点,成为反转链表的一个成员
            current.next=null;//断掉当前节点与其他节点的链接
            flag=current;//将当前节点作为新的反转链表的尾节点
        }
        return newHead;
    }
}

3.题目三 复杂链表的复制

解法一:该解法没有太多的思路技巧,比较容易想到,但是算法的时空复杂度都比较差。具体思路是:先将原链表每一个节点一一添加到ArrayList中 

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        ArrayList<Node> oldList = new ArrayList<>();
        while (head != null) {
            oldList.add(head);
            head = head.next;
        }
        int size=oldList.size();
        ArrayList<Node> newList = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            newList.add(new Node(oldList.get(i).val));
        }
        for (int j = 0; j < size; j++) {
            newList.get(j).random =oldList.get(j).random == null ? null
                    : newList.get(oldList.indexOf(oldList.get(j).random));
            if (j != newList.size() - 1) {
                newList.get(j).next = newList.get(j + 1);
            }
        }
        return newList.get(0);
    }
}

解法二:这是官方给出的思路。我们利用回溯的方式,让每个节点的拷贝操作相互独立。对于当前节点,我们首先要进行拷贝,然后我们进行「当前节点的后继节点」和「当前节点的随机指针指向的节点」拷贝,拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值。具体地,我们用哈希表记录每一个节点对应新节点的创建情况。遍历该链表的过程中,我们检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,我们都立刻递归地进行创建。当我们拷贝完成,回溯到当前层时,我们即可完成当前节点的指针赋值。注意一个节点可能被多个其他节点指向,因此我们可能递归地多次尝试拷贝某个节点,为了防止重复拷贝,我们需要首先检查当前节点是否被拷贝过,如果已经拷贝过,我们可以直接从哈希表中取出拷贝后的节点的指针并返回即可。在实际代码中,我们需要特别判断给定节点为空节点的情况。

class Solution {
    private Map<Node,Node> map = new HashMap<>();
    public Node copyRandomList(Node head) {
        // 终止条件next给过来的node为null;
        if(head == null){
            return null;
        }
        // 中间处理方法
        // 1. 递归创建节点到最后一个节点,并吧所有创建的节点保存在map中;
        Node newNode = new Node(head.val);
        map.put(head,newNode);
        newNode.next = copyRandomList(head.next);
        // 2. 因为递归过程中会创建所有的节点,所以回溯过程中直接进行newNode.random赋值
        newNode.random = map.get(head.random);
        return newNode;
    }
}

解法三:这是官方给出的思路。我们首先将该链表中每一个节点拆分为两个相连的节点,例如对于链表 A→B→C,我们可以将其拆分为 A→A′→B→B′→C→C′ 。对于任意一个原节点 S,其拷贝节点 S′ 即为其后继节点。这样,我们可以直接找到每一个拷贝节点 S′的随机指针应当指向的节点,即为其原节点 S的随机指针指向的节点 T 的后继节点 T′。需要注意原节点的随机指针可能为空,我们需要特别判断这种情况。当我们完成了拷贝节点的随机指针的赋值,我们只需要将这个链表按照原节点与拷贝节点的种类进行拆分即可,只需要遍历一次。同样需要注意最后一个拷贝节点的后继节点为空,我们需要特别判断这种情况。

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    /*
    原地拼接+拆分:
    1.构建"旧->新->旧->新"交替的链表
    2.构建新链表的random指向
    3.将链表拆分恢复现场
    */
    public Node copyRandomList(Node head) {
        // 阴间案例
        if(head == null) {
            return null;
        }
        // 1.创建新旧节点交替的链表
        Node cur = head;
        while(cur != null) {
            // 创建新的链表节点
            Node tmp = new Node(cur.val);
            // tmp右边连接
            tmp.next = cur.next;
            // tmp左边连接
            cur.next = tmp;
            // cur指针移动
            cur = tmp.next;
        }
        // 2.构建新链表的random指向
        // cur指针指向旧链表头结点
        cur = head;
        while(cur != null) {
            // 新的链表random指向旧的链表的random的下一个
            // 这里记住要判断一下cur.random是否为空,防止空指针
            // 如示例1:head.random==null,因此head.next也指向null,也就是默认值
            if(cur.random != null) {
                cur.next.random = cur.random.next;
            }
            // cur后移两位
            cur = cur.next.next;
        }
        // 3.将链表拆分恢复现场
        // pre指向旧链表的节点,res存储新链表头结点
        Node pre = head, res = head.next;
        // cur指针指向新链表的节点
        cur = head.next;
        // 这里进入循环的条件是cur.next!=null,表明不是最后一个cur,也防止了下面空指针
        while(cur.next != null) {
            // 这里注意顺序:防止目标丢失
            pre.next = pre.next.next;
            cur.next = cur.next.next;
            // pre和cur指针各移动一位即可,因为已经改变指向
            pre = pre.next;
            cur = cur.next;
        }
        // 最后一个pre的指针还粘着cur(画图看看就知道),需要手动去除一下
        pre.next = null;
        // 返回新链表头结点
        return res;
    }
}

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

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

相关文章

Routability-Driven Macro Placement with Embedded CNN-Based Prediction Model

Routability-Driven Macro Placement with Embedded CNN-Based Prediction Model 2019 Design, Automation & Test in Europe Conference & Exhibition (DATE) DOI: 10.23919/DATE.2019.8715126 目录Abstract一、Introduction二、PROBLEM FORMULATION AND PRELIMINARIE…

java分析插入排序

首先查看一张经典的插入排序的图片 有图片可知&#xff0c;插入排序其字面的意思找到小的插入到前面就行 插入排序的基本思想就是分治&#xff1a;将数组分为两个区域&#xff0c;分别是已排序区和没有排序的区域 已排序区&#xff1a;假定一边的数组是都排序好的 wei排序区&…

软件测试选Python还是Java?

目录 前言 1、先从一门语言开始 2、两个语言的区别 3、两个语言的测试栈技术 4、如何选择两种语言&#xff1f; 总结 前言 对于工作多年的从业者来说&#xff0c;同时掌握java和Python两门语言再好不过&#xff0c;可以大大增加找工作时的选择范围。但是对于转行的人或者…

Vue2之Vue-cli应用及组件基础认识

Vue2之Vue-cli应用及组件基础认识一、Vue-cli1、单页面应用程序2、vue-cli介绍3、安装和使用4、创建项目4.1 输入创建项目4.2 选择第三项&#xff0c;进行自主配置&#xff0c;按回车键即可4.3 选择自己需要的库4.4 选择Vue的版本4.5 选择CSS选择器4.6 选择Babel、ESLint、etc等…

Sitara™处理器的产品开发路线图

Sitara™处理器的产品开发路线图概述Evaluation Phase(评估阶段)Board Development Phase(硬件发展阶段&#xff0c;硬件设计人员应重点关注这个阶段)Software Development Phase(软件发展阶段)Product Phase/SW Lifecycle概述 一般情况下&#xff0c;会存在四个主要的发展阶段…

从0到1一步一步玩转openEuler--15 openEuler使用DNF管理软件包

文章目录15.1 搜索软件包15.2 列出软件包清单15.3 显示RPM包信息15.4 安装RPM包15.5 下载软件包15.6 删除软件包DNF是一款Linux软件包管理工具&#xff0c;用于管理RPM软件包。DNF可以查询软件包信息&#xff0c;从指定软件库获取软件包&#xff0c;自动处理依赖关系以安装或卸…

书籍《金字塔原理》读后感

上周读完了书籍《金字塔原理》这本书&#xff0c;这本书在管理学中&#xff0c;比较有名的书籍了&#xff0c;之前没怎么读过跟管理学相关书籍&#xff0c;这本算是第一本&#xff0c;是上级推荐给自己的&#xff0c;自己首先查了下&#xff0c;推荐度还是挺好的&#xff0c;看…

uniapp实现app检查更新与升级-uni-upgrade-center详解

app检查更新与升级 参考链接&#xff1a; 升级中心uni-upgrade-center - App uni-admin h5 api App资源在线升级更新 uni-app使用plus注意事项 关于在线升级&#xff08;WGT&#xff09;的几个疑问 什么是升级中心uni-upgrade-center uniapp官方开发的App版本更新的插件&#…

结构体的不定长数组,用起来就是这么爽

结构体的不定长数组 结构体数组不定长的做法&#xff0c;有两种 第一种&#xff1a; 指针 第二种&#xff1a;长度为0的数组 1. 结构体的数组指针 特点&#xff1a; 结构体初始化时&#xff0c;可以是结构体指针&#xff0c;如struct tag_info *pInfo NULL; 也可以是结构体变量…

Python文件和数据格式化(教程)

文件是一个存储在副主存储器的数据序列&#xff0c;可包含任何数据内容。 概念上&#xff0c;文件是数据的集合和抽象&#xff0c;类似的&#xff0c;函数是程序的集合和抽象。 用文件形式组织和表达数据更有效也更加灵活。 文件包括两种形式&#xff0c;文本文件和二进制文…

推荐几款市面上常用的免费CMS建站系统

小编在网站建设行业从业十几年&#xff0c;很多客户或者朋友找我做网站的时候&#xff0c;都喜欢开发一个完全熟悉自己的网站系统&#xff0c;但是小编这里很不推荐。从0到1全新开发&#xff0c;成本&#xff0c;效率和成熟度这些和主流的cms建站系统比起来&#xff0c;完全没有…

文件与IO

一.文件的定义什么是文件&#xff1f;文件分为狭义上的文件和广义上的文件 狭义上的文件&#xff1a;文件夹中的文件&#xff1a;包括视频、图片、文本、可执行文件等等......其中有些文件是有后缀名的&#xff0c;而有一些文件是没有后缀名的广义上的文件&#xff1a;在Linux系…

Unix Linux、MAC、Window 如何安装配置环境?都在这里啦~

嗨害大家好鸭&#xff01;我是小熊猫~ 这次将向大家介绍如何在本地搭建Python开发环境。 Python可应用于多平台包括 Linux 和 Mac OS X。 你可以通过终端窗口输入 “python” 命令来查看本地是否已经安装Python以及Python的安装版本。 源码资料电子书:点击此处跳转文末名片获…

PHP代码审计神器——RIPS个人汉化版(2017年老文)

一、RIPS简介 RIPS是一款PHP开发的开源的PHP代码审计工具&#xff0c;由国外的安全研究者Johannes Dahse开发&#xff0c;目前开源的最新版本是0.55。 程序小巧玲珑&#xff0c;仅有不到500kb&#xff0c;其中的PHP语法分析非常精准&#xff0c;可以实现跨文件变量和函数追踪…

Virtualbox安装Windows11教程,提供虚机专用镜像下载。

微软在Windows11安装过程中增加了TPM2.0安全验证&#xff0c;所以一些老旧的电脑或者不带TPM认证协议的虚拟机都无法安装系统镜像。 这给我们体验尝鲜带来了一些小小的困扰。其实有2种解决方法可以在虚拟机中体验到Windows11带来的变化。 方法一 虚拟机启动时先加载PE系统镜…

Python 版本的常见算法模板(一)

文章目录前言排序模板排序算法归并排序KMP图邻接表Floyd 算法DijkstraBellMan-Ford 算法SPFA 算法Prim 算法Kruskra 算法染色法Hunger算法前言 翻了翻自己以前写的一些博文&#xff0c;发现的话&#xff0c;还是有一些误区没有写好的&#xff0c;所以的话这里的重新写一下&…

年薪40W的测试工程师被裁,回怼的一番话,令人沉思...

腾讯一位测试工程师被炒&#xff0c;回怼到:“反正我有技术&#xff0c;在哪不一样” 在腾讯上班的朋友给我分享了今天在他公司遇到的事情&#xff0c;他部门一位测试工程师被炒&#xff0c;具体原因好像就是跟上司闹矛盾&#xff0c;部门的都觉得非常可惜&#xff0c;因为他算…

本地修改的文件,使用git stash暂存之后不小心将暂存区清空,重新找回之前被暂存的文件

概述 问题 日常使用git 时&#xff0c;将本地所做的修改使用git stash暂存&#xff0c;使用git pull拉取代码之后&#xff0c;之间用git stash clear将git stash暂存的内容删除掉了。本文讲述如何恢复git stash clear掉的暂存区代码。 解决方法 执行指令 git log --graph -…

银行存取款程序设计(JAVA基础案例教程-黑马程序员编著-第三章-课后作业)

【案例3-2】银行存取款程序设计 【案例介绍】 案例描述 银行存取款的流程是人们非常熟悉的事情&#xff0c;用户可在银行对自己的资金账户进行存款、取款、查询余额等操作&#xff0c;极大的便利了人民群众对资金的管理。 本任务要求&#xff0c;使用所学知识编写一个银行存…

spring项目中使用策略模式

小白看了Java设计模式一书&#xff0c;晕晕乎乎&#xff0c;好像懂了&#xff0c;又好像啥都没懂。对照着书本一行行手打代码&#xff0c;完毕&#xff0c;执行&#xff0c;ok&#xff0c;设计模式已掌握。但到了项目中却又茫然。 小白我啊&#xff0c;今天尝试一下在自己的dem…