经典算法(三) 单链表 反转 & 是否相交/成环 & 求交点 等
参考文章:
判断链表是否相交: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 ...
随机推荐
- 分析一个UBOOT的方法
1. 编译完成后可以看到在主目录下生成了uboot.bin文件,为了方便分析,使用如下命令将其反汇编:arm-linux-objdump -D -m arm u-boot > u-boot.as ...
- 【爬虫】随机获取UA
使用模块 fake-useragent https://github.com/hellysmile/fake-useragent 1.安装模块 2.配置 # settings.py '''下载器中间 ...
- Odoo MRP模块
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10825963.html 一:MRP MRP:产品制造管理. 产品制造业务设计到以下几个关键概念: 1)BOM ...
- qt5.12 解决显示中文乱码问题
在菜单栏 文件->选项,找到文本编辑器 文件编码设置如图 在cpp文件中加入 #pragma execution_character_set("utf-8") 之后就可以 ...
- flask通过nginx代理后base_url拿不到正确的url_scheme2016-04-14 12:31
http://www.axiaoxin.com/article/210/ Nginx配置了https请求后,用户发起https请求时首先和Nginx建立连接,完成SSL握手,而后Nginx作为代理是以 ...
- 浅谈flask源码之请求过程
更新时间:2018年07月26日 09:51:36 作者:Dear. 我要评论 这篇文章主要介绍了浅谈flask源码之请求过程,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随 ...
- OneDrive,在云端
应用场景 1.一份文档下班后还没编辑好,发送到自己的QQ/微信回家后继续编辑: 2.由于来回拷贝同一份文件,导致版本太多,忘记那个是最新版本了: 3.出门在外,客户突然需要一份重要文档,这份文件放在办 ...
- 目标检测论文解读4——Faster R-CNN
背景 Fast R-CNN中的region proposal阶段所采用的SS算法成为了检测网络的速度瓶颈,本文是在Fast R-CNN基础上采用RPN(Region Proposal Networks ...
- 源码查看Thread.interrupted()和Thread.currentThread().isInterrupted()区别
JAVA线程状态.线程START方法源码.多线程.JAVA线程池.如何停止一个线程等多线程问题 这两个方法有点容易记混,这里就记录一下源码. Thread.interrupted()和Thread.c ...
- jvm内存模型-和内存分配以及jdk、jre、jvm是什么关系(阿里,美团,京东)
参考:JVM的垃圾回收机制 总结(垃圾收集.回收算法.垃圾回收器) 1.什么是jvm?(1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的.(2) ...