经典算法(三) 单链表 反转 & 是否相交/成环 & 求交点 等
参考文章:
判断链表是否相交:http://treemanfm.iteye.com/blog/2044196
一、单链表反转
链表节点
public class Node {
private int record;
private Node nextNode;
public Node(int record) {
super();
this.record = record;
}
}
构建链表
public static Node creatLinkedList() {
Node head = new Node(0);
Node tmp = null;
Node cur = null;
for (int i = 1; i < 10; i++) {
tmp = new Node(i);
if (1 == i) {
head.setNextNode(tmp);
} else {
cur.setNextNode(tmp);
}
cur = tmp;
}
return head;
}
递归实现
public static Node reverse(Node head) {
if (null == head || null == head.getNextNode()) {
return head;
}
Node reversedHead = reverse(head.getNextNode());
head.getNextNode().setNextNode(head);
head.setNextNode(null);
return reversedHead;
}
循环实现
public static Node reverseCircle(Node head){
Node pre=head;
Node cur=head.getNextNode();
Node temp;
while(cur!=null){
temp=cur.getNextNode();
cur.setNextNode(pre);
pre=cur;
cur=temp;
print(pre);
}
head.setNextNode(null);
return pre;
}
二、判断单向链表是否有环
单链表有环有两种形式,整个链表是一个圆环 或者部分成环 如图1:

图1
分析:
判断链表是否带环,我们可以采用在头结点设两个指针,一个叫fast,一个叫slow,fast一下走两步,而slow一下走一步。如果链表中存在环的话,那么fast和slow必定会在环中相遇。若链表中没有环的话,那么fast必定现于slow指针先到达链表的尾节点。时间复杂度为O(n),空间复杂度为O(1)
实现:
构建上图有环链表:
public static Node creatLinkedListCircle() {
Node head = new Node(1);
Node node4 = null;
Node tmp = null;
Node cur = null;
for (int i = 2; i < 13; i++) {
tmp = new Node(i);
if (2 == i) {
head.setNextNode(tmp);
} else if(i==4){
cur.setNextNode(tmp);
node4=tmp;
}else {
if(i==12){
cur.setNextNode(node4);
}else{
cur.setNextNode(tmp);
}
}
cur = tmp;
}
return head;
}
判断是否有环:
public static boolean isCircle(Node head) {
//只有一个节点
if(head.getNextNode()==null){
return false;
}
//两个结点成环
if(head.getNextNode()==head){
return true;
}
Node slow = head.getNextNode();
Node fast = head.getNextNode().getNextNode();
while (slow != null && fast != null) {
if (slow == fast) {
return true;
}
slow=slow.getNextNode();
fast = fast.getNextNode().getNextNode();
}
return false;
}
三、判断单向链表是否相交

方案一:
若两个链表都无环且交于一点,那么最后一个节点一定是共有的。可以先遍历第一个链表,记录最后一个节点,再遍历第二个链表,将其最后一个节点与第一个链表的最后一个节点比较,若相同,则相交。时间复杂度也为O(Max(length(h1),length(h2))),空间复杂度为O(1)
方案二:
①循环遍历h1,计算每个节点的hash值并存入map中;
②循环遍历h2,顺序计算每个节点的hash值v,并用map.get(v),若返回非空,则算法结束
第①步算法时间复杂度O(length(h1)),第②步算法时间复杂度O(length(h2)),因此hash计数 算法时间复杂度为O(max(length(h1),length(h2))),复杂度降低到线性。但是由于使用了额外的map结构,空间复杂度为O(length(h1))。
方案三:
求出两个链表的长度:len_h1,len_h2,求出差值len(len为较大的减去较小的值)。让长的那个先走len步,之后两个链表一起走,直至节点相同的时候。时间复杂度为O(Max(length(h1),length(h2))),空间复杂度为O(1)
方案三:

如果两个链表都无环,则可以把第二个链表接在第一个链表后面,如果得到的链表有环,则说明这两个链表相交。这里如果有环,则第二个链表的表头一定在环上,只需要从第二个链表开始遍历,看是否会回到起点即可判断。假设两个链表长度分别为m和n,则时间复杂度为O(m+n)。
三、求环的长度
分析:
我们可以采用在头结点设两个指针,一个叫fast,一个叫slow,fast一下走两步,而slow一下走一步。slow和fast从第一次相遇到第二次相遇时所走的长度即是环的长度.参考图1
实现:
public static int getCircleLength(Node head) {
Node slow = head.getNextNode();
Node fast = head.getNextNode().getNextNode();
int length=0;
boolean flag=false;
while (slow != null && fast != null) {
if (slow == fast) {
if(flag){
return length;
}
length=0;
flag=true;
}
length++;
slow=slow.getNextNode();
fast = fast.getNextNode().getNextNode();
}
return length;
}
四、求环入口点
方案一:采用hash的方式。遍历该链表,第一个重复的node则为环入口点。时间复杂度O(n),空间复杂度O(n)
public static Node getEnterNode(Node head) {
Map<Node, String> map = new HashMap<Node, String>();
map.put(head, null);
Node next = head.getNextNode();
// 找到交点
while (next != null) {
if (map.containsKey(next)) {
return next;
}
map.put(next, null);
next = next.getNextNode();
}
return null;
}
方案二:经过推导,头结点到 入口点的距离 和 问题二的交点 到入口点的距离是相等的。所以可以设置分别从两点设置指针。第一次相遇的节点 则为环入口点。时间复杂度O(n),空间复杂度O(1)
public static Node getEnterNode(Node head) {
Node slow = head.getNextNode();
Node fast = head.getNextNode().getNextNode();
//找到交点
while (slow != null && fast != null) {
if (slow == fast) {
break;
}
slow = slow.getNextNode();
fast = fast.getNextNode().getNextNode();
}
//分别从头结点和交点出发,第一次相遇 则为环入口点
Node no = head.getNextNode();
slow = slow.getNextNode();
while (slow != no) {
no = no.getNextNode();
slow = slow.getNextNode();
}
return no;
}
五、查找单链表的中间节点
分析:采用快慢指针的方法。设两个指针,一个叫fast,一个叫slow,fast一下走两步,而slow一下走一步。fast走完时,slow恰好走到中间。
public static Node getMidNode(Node head) {
Node slow = head;
Node fast = head;
while (fast!=null&&fast.getNextNode()!=null) {
slow=slow.getNextNode();
fast=fast.getNextNode().getNextNode();
}
return slow;
}
六、求链表倒数第k个节点
分析:设置两个指针 p1、p2,首先 p1 和 p2 都指向 head,然后 p2 向前走 k 步,这样 p1 和 p2 之间就间隔 k 个节点,最后 p1 和 p2 同时向前移动,直至 p2 走到链表末尾。
实现:
//查找倒数第k个节点
public static Node getLastKNode(Node head,int k) {
Node p1 = head;
Node p2 = head;
while (k-->0) {
p2=p2.getNextNode();
}
while(p2!=null){
p2=p2.getNextNode();
p1=p1.getNextNode();
}
return p1;
}
七、合并两个有序链表,并保持有序
// 合并两个有序链表
public static Node merge(Node head1, Node head2) {
if (head1 == null) {
return head2;
}
if (head2 == null) {
return head1;
}
Node head = null;
if (head1.getRecord() < head2.getRecord()) {
head = head1;
head.setNextNode(merge(head1.getNextNode(), head2));
} else {
head = head2;
head.setNextNode(merge(head1, head2.getNextNode()));
}
return head; }
经典算法(三) 单链表 反转 & 是否相交/成环 & 求交点 等的更多相关文章
- 2、java数据结构和算法:单链表: 反转,逆序打印, 合并二个有序链表,获取倒数第n个节点, 链表的有序插入
什么也不说, 直接上代码: 功能点有: 1, 获取尾结点 2, 添加(添加节点到链表的最后面) 3, 添加(根据节点的no(排名)的大小, 有序添加) 4, 单向链表的 遍历 5, 链表的长度 6, ...
- java 单链表反转
最近与人瞎聊,聊到各大厂的面试题,其中有一个就是用java实现单链表反转.闲来无事,决定就这个问题进行一番尝试. 1.准备链表 准备一个由DataNode组成的单向链表,DataNode如下: pub ...
- 单链表反转的原理和python代码实现
链表是一种基础的数据结构,也是算法学习的重中之重.其中单链表反转是一个经常会被考察到的知识点. 单链表反转是将一个给定顺序的单链表通过算法转为逆序排列,尽管听起来很简单,但要通过算法实现也并不是非常容 ...
- 单链表反转(Singly Linked Lists in Java)
单链表反转(Singly Linked Lists in Java) 博客分类: 数据结构及算法 package dsa.linkedlist; public class Node<E> ...
- java实现单链表反转(倒置)
据说单链表反转问题面试中经常问,而链表这个东西相对于数组的确稍微难想象,因此今天纪录一下单链表反转的代码. 1,先定义一个节点类. 1 public class Node { 2 int index; ...
- 单链表反转java代码
据说单链表反转问题面试中经常问,而链表这个东西相对于数组的确稍微难想象,因此今天纪录一下单链表反转的代码. 1,先定义一个节点类. public class Node { int index; Nod ...
- Java实现单链表反转操作
单链表是一种常见的数据结构,由一个个节点通过指针方式连接而成,每个节点由两部分组成:一是数据域,用于存储节点数据.二是指针域,用于存储下一个节点的地址.在Java中定义如下: public class ...
- Java单链表反转图文详解
Java单链表反转图文详解 最近在回顾链表反转问题中,突然有一些新的发现和收获,特此整理一下,与大家分享 背景回顾 单链表的存储结构如图: 数据域存放数据元素,指针域存放后继结点地址 我们以一条 N1 ...
- C++单链表反转
单链表反转笔记: #include<iostream> #include<string.h> using namespace std; struct ListNode { in ...
随机推荐
- 由一个空工程改为SpringBoot工程
1.先创建一个空的工程,创建springboot 工程 必须继承spring-boot-stater-parent 2.导入依赖 <parent> <groupId>org. ...
- Mysql关键字Explain 性能优化神器
Explain工具介绍 使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析查询语句或是结构的性能瓶颈.在select语句之前增加explaion关键字,MySQL会在查询上设置一个标记,执行查 ...
- jQuery(五): Deferred
jQuery(五): Deferred 有啥用 通常来说,js请求数据,无论是异步还是同步,都不会立即获取到结果,通常而言,我们一般是是使用回调函数再执行,而 deferred就是解决jQuery的回 ...
- Java 之 异常基础
一.异常概念 异常:指的是程序在执行过程中,出现的非正常的情况,最终会导致 JVM 的非正常停止. 在 Java 等面向对象的编程语言中,异常本身就是一个类,产生异常就是创建异常对象并抛出了一个异常对 ...
- android黑白屏的问题
你会很奇怪,为什么有些app启动时,会出现一会儿的黑屏或者白屏才进入Activity的界面显示,但是有些app却不会如QQ手机端,的确这里要做处理一下.这里先了解一下为什么会出现这样的现象,其实很简单 ...
- FreeRTOS 任务通知模拟事件标志组
实验 //设置事件位的任务 void eventsetbit_task(void *pvParameters) { u8 key; while(1) { if(EventGroupTask_Handl ...
- Android-----实现给图片添加字体
实现给图片添加字体,图片旋转功能:xml布局文件内容如下,一个简单的ImageView布局 <com.example.hsjgapp.RotateImageView //这里存放要展示的图片 a ...
- Vue 中 $nextTick() 的应用
Vue 在更新 DOM 时是异步执行的. 只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更.如果同一个 watcher 被多次触发,只会被推入到队列中一次.这种在缓 ...
- MySQL MHA--主库故障检测
MHA主库检查参数 MHA从0.53版本开始支持ping_type参数来设置如何检查master可用性:ping_type=select: 基于一个到master的已经存在的连接执行select 1, ...
- Linux chage命令详解
原文 chage命令用于密码实效管理,该是用来修改帐号和密码的有效期限,接下来通过本文给大家介绍Linux chage命令相关知识,本文介绍的非常详细,具有参考借鉴价值,感兴趣的朋友一起学习吧 lin ...