参考文章:

判断链表是否相交: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; }

经典算法(三) 单链表 反转 & 是否相交/成环 & 求交点 等的更多相关文章

  1. 2、java数据结构和算法:单链表: 反转,逆序打印, 合并二个有序链表,获取倒数第n个节点, 链表的有序插入

    什么也不说, 直接上代码: 功能点有: 1, 获取尾结点 2, 添加(添加节点到链表的最后面) 3, 添加(根据节点的no(排名)的大小, 有序添加) 4, 单向链表的 遍历 5, 链表的长度 6, ...

  2. java 单链表反转

    最近与人瞎聊,聊到各大厂的面试题,其中有一个就是用java实现单链表反转.闲来无事,决定就这个问题进行一番尝试. 1.准备链表 准备一个由DataNode组成的单向链表,DataNode如下: pub ...

  3. 单链表反转的原理和python代码实现

    链表是一种基础的数据结构,也是算法学习的重中之重.其中单链表反转是一个经常会被考察到的知识点. 单链表反转是将一个给定顺序的单链表通过算法转为逆序排列,尽管听起来很简单,但要通过算法实现也并不是非常容 ...

  4. 单链表反转(Singly Linked Lists in Java)

    单链表反转(Singly Linked Lists in Java) 博客分类: 数据结构及算法   package dsa.linkedlist; public class Node<E> ...

  5. java实现单链表反转(倒置)

    据说单链表反转问题面试中经常问,而链表这个东西相对于数组的确稍微难想象,因此今天纪录一下单链表反转的代码. 1,先定义一个节点类. 1 public class Node { 2 int index; ...

  6. 单链表反转java代码

    据说单链表反转问题面试中经常问,而链表这个东西相对于数组的确稍微难想象,因此今天纪录一下单链表反转的代码. 1,先定义一个节点类. public class Node { int index; Nod ...

  7. Java实现单链表反转操作

    单链表是一种常见的数据结构,由一个个节点通过指针方式连接而成,每个节点由两部分组成:一是数据域,用于存储节点数据.二是指针域,用于存储下一个节点的地址.在Java中定义如下: public class ...

  8. Java单链表反转图文详解

    Java单链表反转图文详解 最近在回顾链表反转问题中,突然有一些新的发现和收获,特此整理一下,与大家分享 背景回顾 单链表的存储结构如图: 数据域存放数据元素,指针域存放后继结点地址 我们以一条 N1 ...

  9. C++单链表反转

    单链表反转笔记: #include<iostream> #include<string.h> using namespace std; struct ListNode { in ...

随机推荐

  1. 前端1-----CSS层叠样式表了解,css的引入方式,三大选择器(标签,类,id),高级选择器

    前端1-----CSS层叠样式表了解,css的引入方式,三大选择器(标签,类,id),高级选择器 一丶CSS简介    叠样式表(英文全称:Cascading Style Sheets)是一种用来表现 ...

  2. 安装vivado 2016.1时出错

    在将vivado 2016.1安装到d:\ xilinx时,发生以下错误: 提取存档D时遇到 错误:\ Xilinx_Vivado_SDK_2016.1_0409_1 \ payload \ rdi_ ...

  3. js数组与字符串类型相同方法的比较

    数组和字符串有很多相似的对方,比如数组和字符串都有以下方法: concat indexOf lastIndexOf slice includes 鉴于toString及valueOf方法基本类型都有, ...

  4. android启动时间慢的问题

     [转]对于Android的性能这方面评估,大部分都是有超级兔子去比跑分的,还是不能反映全面的问题.就我知道的而言,应用启动时间是很影响用户体验的一个性能方面问题. 最近的一个项目,别人都说应用启动慢 ...

  5. Node初识

    初识Nodejs Node.js的诞生 作者Ryan Dahl 瑞恩·达尔 2004 纽约 读数学博士 2006 退学到智利 转向开发 2009.5对外宣布node项目,年底js大会发表演讲 2010 ...

  6. 摘jmeter安装之后如何汉化

    下载完成后打开bin文件,选择jmeter.properties打开,搜索language,修改成zh_CN,汉化jmeter,记得去掉前面的#号,然后保存,修改完配置文件后需要重启jmeter 用的 ...

  7. pandas的行列显示不全的解决方法

    pd.set_option('display.max_rows', 100) # 显示的最大行数(避免只显示部分行数据) pd.set_option('display.max_columns', 10 ...

  8. sublime设置默认字体样式

    因电脑配置的不同,还有个人喜好的不同,有时候想用自己喜欢的字体来写代码,想用自己习惯的字号大小来显示代码.这些又该怎样设置呢? 本节主要介绍下如何设置字体大小和样式 (1)点菜单栏 “Preferen ...

  9. KVM-安装windows

    硬盘.网卡选择vitio 虚拟机配置2个cd-rom,分别挂载系统iso与virtio-win 开始安装无法识别硬盘,加载光驱驱动 安装完成进入系统之后,设备管理器添加驱动,识别设备

  10. springBoot 日志中关于profiles设置的源码解读

    在启动SpringBoot应用是看到到如下日志,于是出于好奇查看了下源代码: 首先,StartpInfoLogger类,采用jcl-over-slf4j[即Apache Common Log]中的Lo ...