【Java】HOT100 链表

代码随想录的链表题在这里:

链表1、链表2做过总结


目录

HOT 100:链表

LeetCode160:相交链表

LeetCode206:反转链表

LeetCode206:回文链表

LeetCode141:环形链表

LeetCode142:环形链表ii

LeetCode21:合并两个有序列表

LeetCode2:两数相加

LeetCode2:两数相加

LeetCode24:两两交换链表中的节点

LeetCode25:K个一组反转链表

LeetCode138:随机链表的复制

LeetCode148:排序链表

LeetCode23:合并K个升序链表

LeetCode146:LRU(最近最少使用)缓存


HOT 100:链表

LeetCode160:相交链表

思路:先计算两个链表的长度,让两个指针cur1,cur2指向对齐的位置,然后再遍历比较直到cur1==cur2,返回cur1。秒了。

public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {int num1 = 0,num2 = 0;ListNode cur1 = headA;ListNode cur2 = headB;while(cur1 != null){cur1 = cur1.next;num1++;}while(cur2 != null){cur2 = cur2.next;num2++;}cur1 = headA;cur2 = headB;if(num1>num2){for(int i=0;i<Math.abs(num1-num2);i++){cur1 = cur1.next;}}else{for(int i=0;i<Math.abs(num1-num2);i++){cur2 = cur2.next;}}while(cur1 != null){if(cur1 == cur2){return cur1;}else{cur1 = cur1.next;cur2 = cur2.next;}}return cur1;}
}

LeetCode206:反转链表

思路:第一步肯定是看cur是否为空;接着利用pre和cur双指针来反转链表;其中用到一个临时指针tmp来记录cur的下一个节点。

class Solution {public ListNode reverseList(ListNode head) {ListNode pre = null;ListNode cur = head;if(cur == null){return pre;}while(cur!=null){ListNode tmp = cur.next;cur.next = pre;pre = cur;cur = tmp;}return pre;}
}

LeetCode206:回文链表

思路:用栈来写比较巧妙,前半部分入栈,比较出栈和下半部分即可;

注意要区分奇数和偶数,奇数个时,要跳过中间的数再比较

class Solution {public boolean isPalindrome(ListNode head) {int len = 0;ListNode cur = head;Stack<ListNode> stack = new Stack<>();while(cur != null){cur = cur.next;len++;}cur = head;for(int i=0;i<len/2;i++){stack.push(cur);cur = cur.next;}if(len%2 != 0)  cur = cur.next;for(int i=0;i<len/2;i++){if(cur.val != stack.pop().val){return false;}cur = cur.next;}return true;}
}

LeetCode141:环形链表

思路:判断是否有环:快慢指针法快指针走两步,慢指针走一步,判断是否会相遇

public class Solution {public boolean hasCycle(ListNode head) {ListNode fast = head;ListNode slow = head;while(fast.next!=null && fast!=null){fast = fast.next.next;slow = slow.next;if(fast == slow){return true;}}return false;  }
}

LeetCode142:环形链表ii

思路:分两步,秒了

(1)判断是否有环:快慢指针法快指针走两步,慢指针走一步,判断是否会相遇

(2)如果有环,从哪里开始入环。根据一些数学运算可知,当n=1时,x=z,即从头结点和相遇节点出发,以同样每次走一个节点的速度,二者将在环形出口节点相遇。

public class Solution {public ListNode detectCycle(ListNode head) {ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;if(fast == slow){ListNode index1 = head;ListNode index2 = slow;while(index1 != index2){index1 = index1.next;index2 = index2.next;}return index1;}}return null;        }
}

LeetCode21:合并两个有序列表

用到虚拟头结点、pre和cur1、cur2对比,分为三种情况

(1)cur1、cur2都不为null时,选择较小的一个作为下一个节点

(2)cur1为null,把pre的下一个节点设为cur2;反之也是一样

class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {//为了不单独考虑头结点的情况,创建虚拟头结点ListNode dummy = new ListNode(-1);ListNode pre = dummy;ListNode cur1 = list1;ListNode cur2 = list2;while(cur1 != null && cur2 != null){if(cur1.val>cur2.val){pre.next = cur2;pre = cur2;cur2 = cur2.next;}else{pre.next = cur1;pre = cur1;cur1 = cur1.next;}            }if(cur1 == null){pre.next = cur2;}if(cur2 == null){pre.next = cur1;}return dummy.next;}
}

LeetCode2:两数相加

思路:自己写的,不一定最优

class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode cur1 = l1;ListNode cur2 = l2;int num1 = 0, num2 = 0;int sum1 = 0, sum2 = 0;while(cur1 != null){sum1 += cur1.val*Math.pow(10, num1);cur1 = cur1.next;num1++;}while(cur2 != null){sum2 += cur2.val*Math.pow(10, num2);cur2 = cur2.next;num2++;}int sum = sum1 + sum2;ListNode dummy = new ListNode(-1);ListNode pre = dummy;if(sum==0)  return new ListNode(0);while(sum != 0){int n = sum % 10;sum = sum/10;ListNode tmp = new ListNode(n);pre.next = tmp;pre = tmp;}return dummy.next;}
}

LeetCode2:两数相加

思路:删除倒数第n个节点,利用快慢指针的差值;要删除倒数第n个节点,就要让指针指向它的前一个节点(倒数第n+1个节点)。秒。

class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummy = new ListNode(-1,head);ListNode slow = dummy;ListNode fast = dummy;for(int i=0;i<=n;i++){fast = fast.next;}while(fast != null){fast = fast.next;slow = slow.next;}//当fast指向null时,slow指向倒数第n+1个节点slow.next = slow.next.next;return dummy.next;}
}

LeetCode24:两两交换链表中的节点

思路:还是pre当dummy,设cur1,cur2两两交换,记得设置临时指针tmp用于记录第三个节点

class Solution {public ListNode swapPairs(ListNode head) {ListNode dummy = new ListNode(-1,head);ListNode pre = dummy;ListNode cur1;ListNode cur2;ListNode tmp;while(pre.next!= null && pre.next.next != null){cur1 = pre.next;cur2 = pre.next.next;tmp = pre.next.next.next;pre.next = cur2;cur2.next = cur1;cur1.next = tmp;pre = cur1;}return dummy.next;}
}

LeetCode25:K个一组反转链表

思路:两个递归

class Solution {public ListNode reverseKGroup(ListNode head, int k) {if(head == null || head.next == null)   return head;ListNode tail = head;for(int i=0;i<k;i++){if(tail == null){//不翻转直接返回return head;}tail = tail.next;   //这里可以看出是左闭右开区间}ListNode newHead = reverse(head,tail);head.next = reverseKGroup(tail,k);return newHead;}public ListNode reverse(ListNode head, ListNode tail){ListNode pre = null;ListNode cur = head;while(cur != tail){ListNode tmp = cur.next;cur.next = pre;pre = cur;cur = tmp;}return pre;}
}

LeetCode138:随机链表的复制

思路:三个步骤:复制-random-拆分

class Solution {public Node copyRandomList(Node head) {if(head==null){return null;}Node cur1=head;//复制, 相当于1->2->3  ==>  1->1'->2->2'->3->3'while(cur1!=null){Node temp1=new Node(cur1.val);temp1.next=cur1.next;cur1.next=temp1;cur1=temp1.next;}//随机指向Node cur2=head;while(cur2!=null){Node temp2=cur2.next;   //tmp2即复制节点if(cur2.random!=null){temp2.random=cur2.random.next;  //random节点的next就是复制random}cur2=temp2.next;}//拆分原链表和拷贝链表Node cur3=head;Node result=head.next;while(cur3.next!=null){Node temp3=cur3.next;cur3.next=temp3.next;cur3=temp3;}return result;}
}

LeetCode148:排序链表

链表排序和数组排序最天然的不同在于访问元素与交换元素的成本,类似冒泡等排序方法就基本用不起来了,因为访问和交换的开销太大。链表排序方法主要有以下三种:

归并排序:链表最佳排序方法,注意要先递归链表的右半部,对链表进行截断,再递归链表的左半部。

快速排序:元素无法直接进行交换,可以每次遍历将链表分为大小两个部分分别存储到两个子链表中(实际上只需要构建一个新链表),再进行合并。

堆排序:简单粗暴,当空间复杂度不符合要求。

思路:

1. 先用fast和slow双指针法找到链表的中点(奇数链表为中点,偶数链表为中点的左面一个)

2. 然后从中点处递归断链,直到终止条件:一个节点指向nul

3. 合并(先while比较,再接剩下的多余的链)

class Solution {public ListNode sortList(ListNode head) {if(head == null || head.next == null) return head;ListNode slow = head;ListNode fast = head;//快慢指针找中点,奇数找中点,偶数找中点左边的一个while(fast.next != null && fast.next.next != null){fast = fast.next.next;slow = slow.next;}//slow.next就是第二段的头结点ListNode l1 = sortList(slow.next);slow.next = null;ListNode l2 = sortList(head);//接着合并连接新链表ListNode newHead = new ListNode(-1);ListNode cur = newHead;while(l1 != null && l2 != null){if(l1.val<l2.val){cur.next = l1;l1 = l1.next;}else{cur.next = l2;l2 = l2.next;}cur = cur.next;}cur.next = l1 == null? l2:l1;return newHead.next;}
}

LeetCode23:合并K个升序链表

思路:利用PriorityQueue构建小根堆,把节点加进去会自动升序排列好

类似的题目还有:347. 前K个高频元素,解析见链表02

相比于只加入头结点之后再比较,总感觉全部加入更加快速些。

class Solution {public ListNode mergeKLists(ListNode[] lists) {if(null == lists || lists.length == 0){return null;}ListNode newHead = new ListNode(-1);ListNode cur = newHead;//构建小根堆,PriorityQueue<ListNode> pq =  new PriorityQueue<>((o1,o2)->(o1.val - o2.val));//先把节点全部加入到小根堆中for(ListNode head : lists){while(head!= null){pq.add(head);head = head.next;}}while(!pq.isEmpty()){ListNode tmp = pq.poll();//弹出的第一个 就是最小的头节点。cur.next = tmp;//指针不断移动。cur = cur.next;}return newHead.next;}
}


LeetCode146:LRU(最近最少使用)缓存

思路:双向链表(每个节点包括key,value和前后指针prev和next)+哈希map(key-node)

首先对于LRUCache类,

要先定义双向链表的节点,以及以capacity初始化LRU缓存

Map<Integer,ListNode>

get函数:如果存在则从map里获得node.value,把node移到链表最前方(刚刚使用过)

put函数:如果存在则node.val = value,然后移动node到最前方

不存在则新建node加入map,移动node到最前方,size++;

(当size>capacity,再size--,删去链表末段的节点,并从map移除节点)

public class LRUCache {//定义双向链表,靠近头的是最近使用的,靠近尾的是最久未使用的。//定义HashMap,存储链表节点信息class LinkNode {int key;int value;LinkNode prev;LinkNode next;public LinkNode() {}public LinkNode(int key, int value) {this.key = key;this.value = value;}}private Map<Integer, LinkNode> map = new HashMap<>();private int size;private int capacity;private LinkNode head, tail;public LRUCache(int capacity) {this.capacity = capacity;this.size = 0;head = new LinkNode();tail = new LinkNode();head.next = tail;tail.prev = head;}public int get(int key) {LinkNode node = map.get(key);if (node == null) {return -1; //不存在返回-1}//存在需移动位置至链表头部moveNodeToHead(node);return node.value;}public void put(int key, int value) {LinkNode node = map.get(key);if (node != null) {node.value = value;//node移动位置至链表头部moveNodeToHead(node);} else {LinkNode n = new LinkNode(key, value);map.put(key, n);//新node添加至链表头部addHead(n);size++;if (size > capacity) {//逐出最久未使用的nodermTailNode();size--;}}}private void moveNodeToHead(LinkNode node) {removeNode(node); //删除节点addHead(node); //添加至头节点}private void rmTailNode() {map.remove(tail.prev.key);removeNode(tail.prev);}private void removeNode(LinkNode node) {node.prev.next = node.next;node.next.prev = node.prev;}private void addHead(LinkNode node) {head.next.prev = node;node.prev = head;node.next = head.next;head.next = node;}}

 

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

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

相关文章

mixins

mixins Mixins的基本概念: 什么是mixins&#xff0c;它们用于解决什么问题&#xff1f; Mixins是Vue.js的一个功能&#xff0c;允许你创建可重用的代码片段&#xff0c;包括数据、生命周期钩子、方法、计算属性等&#xff0c;可以混入到单个Vue组件中。它们用于解决代码重用和…

大数据概述:大数据时代的发展与挑战

随着互联网、物联网、云计算等技术的飞速发展&#xff0c;大数据作为一种新兴产业&#xff0c;已经渗透到了各个领域。大数据时代&#xff0c;带来了前所未有的发展机遇&#xff0c;也带来了诸多挑战。本文将从大数据的概念、大数据的影响、大数据的应用、大数据关键技术、大数…

如何使用CANoe进行LINstress测试

1.创建Stress测试工程 依次按照1-3的步骤建立工程 4部分&#xff0c;主要是Description of the sample configurations&#xff08;对示例工程的描述&#xff09; 5部分主要是显示示例工程的位置和简单描述 工程打开后如下图所示 重点关注红框标注的地方&#xff0c;重新截一…

《由浅入深学习SAP财务》:第2章 总账模块 - 2.6 定期处理 - 2.6.5 年末操作:维护新财政年度会计凭证编号范围

2.6.5 年末操作&#xff1a;维护新财政年度会计凭证编号范围 财务系统的维护者要在每年年末预先设置好下一年度的会计凭证编号范围&#xff08;number range&#xff09;&#xff0c;以便下一年度会计凭证能够顺利生成。这一操作一定要在下一年度1月1日以前预先完成。 …

vue 组件通信的几种方法

vue是js一个非常热门的框架&#xff0c;组件之间的通信是vue基础也是重要的一部分。 1.props, 可以实现父子组件通信&#xff0c;但其数据是只读&#xff0c;不可修改 &#xff08;使用child之前需先接受一下&#xff0c;已下同理&#xff09; //父组件 <script setup la…

C++中STL迭代器如何使用

1.概念 迭代器是一种检查容器内元素并遍历元素的数据类型。 C 更趋向于使用迭代器而不是下标操作&#xff0c;因为标准库为每一种标准容器&#xff08;如vector &#xff09;定义了一种迭代器类型&#xff0c;而只用少数容器&#xff08;如 vector &#xff09;支持下标 操作访…

半导体材料(二)——半导体导电特性

本篇为西安交通大学本科课程《电气材料基础》的笔记。 本篇为这一单元的第二篇笔记&#xff0c;上一篇传送门。 半导体导电特性 载流子的迁移 外电场下电子和空穴定向位移产生电流。电流密度可写作&#xff1a; J e ( μ n n μ p p ) E σ E Je(\mu_n n\mu_p p)E\sigm…

Swift中的运算符

Swift中的运算符可以分为以下几种&#xff1a; 算术运算符&#xff1a;用于执行基本的数学运算&#xff0c;如加法&#xff08;&#xff09;、减法&#xff08;-&#xff09;、乘法&#xff08;*&#xff09;、除法&#xff08;/&#xff09;和取余&#xff08;%&#xff09;等…

行式存储VS列式存储对比

行式存储&#xff1a; 一行代表一个记录的所有字段。 可以快速读取和写入单条记录。 如果要检索一条数据&#xff0c;数据库会读取or写入整条记录&#xff0c;包含所有相关字段。 列式存储&#xff1a; 表中每一列的数据连续存放。这种方式在需要对某一列进行大量运算或分析时…

「 典型安全漏洞系列 」14.NoSQL注入漏洞详解

NoSQL注入是一个漏洞&#xff0c;攻击者能够干扰应用程序对NoSQL数据库进行的查询&#xff0c;本文我们将研究如何测试一般的NoSQL漏洞&#xff0c;然后重点研究如何利用MongoDB中的漏洞&#xff08;MongoDB是最流行的NoSQL数据库&#xff09;。 1. 什么是NoSQL注入 NoSQL注入…

【C++语言】初步认识面向对象编程类和对象(上)

文章目录 前言一.初步认识面向过程和面向对象编程1.面向过程编程初步认识2.面向对象编程初步认识 二.C类1. 类的引入&#xff1a;2. 类的定义3.类的访问限定符&&封装3.1 访问限定符3.2 封装 4.类的实例化5.如何计算类的大小 总结C语言系列学习目录 前言 面向对象编程 类…

linux服务器配置conda和torch环境踩坑记录

anaconda环境安装torch时候报错 CondaValueError: Malformed version string ~: invalid character(s) 网上所有方法都试过,包括重新设置.condarc文件,换清华源 尝试更新conda conda update -n base conda,无法更新,还是报错上面的错 推测是版本过低导致 conda --version ,结…

什么数据集成(Data Integration):如何将业务数据集成到云平台?

说到数据集成&#xff08;Data Integration&#xff09;&#xff0c;简单地将所有数据倒入数据湖并不是解决办法。 在这篇文章中&#xff0c;我们将介绍如何轻松集成数据、链接不同来源的数据、将其置于合适的环境中&#xff0c;使其具有相关性并易于使用。 数据集成&#xff1…

今年消费新潮流:零元购商业模式

今天给大家推荐一种极具创新的电子商务模式&#xff1a;零元购商业模式 这个模式支持消费者以零成本或极低成本购买商品。这种模式主要通过返现、积分、优惠券等方式来减少支付金额&#xff0c;使消费者实现“零成本”购物的目标。 人民网在去年发表了一篇文章。 总结了一下&a…

设计模式学习笔记(知识点与代码实践)

文章目录 0 背景1 设计模式 0 背景 设计模式其实很早就想学习了&#xff0c;但是由于懒 &#xff0c;所以一直拖到现在。之前写项目也接触过一些零散的设计模型&#xff0c;却一直没有系统的学习过&#xff0c;这次就是系统的学习这方面的知识。 本文就是学习心得和代码实践的…

【基础物理实验】【AFM虚拟实验】基于AFM的物质表面微观结构及力学性质表征仿真实验(上)【北京航空航天大学】

基于AFM的物质表面微观结构及力学性质表征仿真实验 说明&#xff1a; 本次实验为本科生《基础物理实验》课程中的虚拟实验部分&#xff0c;在虚拟实验平台中进行。 一、实验目的&#xff1a; 1. 掌握AFM的基本成像原理及系统结构&#xff1b; 2. 掌握AFM的基本操作技巧及操…

stable diffusion本地部署教程

Stable Diffusion是一种生成模型&#xff0c;用于根据给定的文本输入生成图像。要在本地部署Stable Diffusion&#xff0c;您需要完成以下步骤&#xff1a; 安装依赖项 首先&#xff0c;确保您的计算机上已安装了Python&#xff08;推荐使用3.8或更高版本&#xff09;和pip。然…

EDA重新成为热点,中国正在成为参与者

EDA正在从一个沉淀已久的领域转变为一个热门的市场&#xff0c;这得益于市场中对定制设计的呼声&#xff0c;以及人工智能等先进技术的推出&#xff0c;这些工具将需要开发具有更高性能的芯片架构。 因为市场更需要定制芯片&#xff0c;这意味着更多的芯片设计工作正在发生&…

Semaphore

Semaphore 翻译&#xff1a; 信号量 解释&#xff1a; 信号量通常用于限制线程数&#xff0c;而不是访问某些&#xff08;物理或逻辑&#xff09;资源。 例如&#xff0c;这是一个使用信号量来控制对一个项目池的访问的类 用法 可以限制线程的使用次数 public static vo…

使用 Tranformer 进行概率时间序列预测实战

使用 Transformers 进行概率时间序列预测实战 通常&#xff0c;经典方法针对数据集中的每个时间序列单独拟合。然而&#xff0c;当处理大量时间序列时&#xff0c;在所有可用时间序列上训练一个“全局”模型是有益的&#xff0c;这使模型能够从许多不同的来源学习潜在的表示。…