经典算法(三) 单链表 反转 & 是否相交/成环 & 求交点 等
参考文章:
判断链表是否相交: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 ...
随机推荐
- Python语言控制运算的优先级
Python语言碰上计算式同时出现在一个指令内时,除了括号"(".")"最优外,其余计算优先次序如下: 次方(**). 乘法.除法.求余数(%).求整数(//) ...
- Django-xadmin的使用介绍
Django-xadmin的介绍 Django是python的重量级web框架,写得少,做得多,非常适合后端开发,它很大的一个亮点是,自带后台管理模块,但它自带的后台管理有点丑,而Xadmin是基于b ...
- Django---Django的中间件
Django---Django的中间件 一丶中间件介绍 什么是中间件 官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于在全局范围内改变Dj ...
- 安卓、ios时间转换成时间戳的形式
将日期转换成时间戳的形式,在安卓和ios不同的系统下转正会有兼容性的问题 安卓系统下Date.parse(new Date('2018-03-30 12:00:00'))会直接转换成时间戳的形式(简单 ...
- JavaScript 调试 debug
一.错误 1.语法错误 出现错误,有提示,很容易的解决. 2.逻辑错误 不容易发现 二.调试方式 1.alert() 方式 2.console.log()/console.error() 方式 3.断 ...
- MySQL导入数据报错Got a packet bigger than‘max_allowed_packet’bytes错误的解决方法
由于max_allowed_packet的值设置过小的原因,只需要将max_allowed_packet值设置大一点就OK了.通过终端进入mysql控制台,输入如下命令可以查看max_allowed_ ...
- redis-启用命令
一.redis后端启动: 1.将redis源码包中的redis.conf配置文件复制到redis/bin/下 # cd /root/redis-3.0.0 # cp redis.conf /usr/l ...
- Centos6.5配置防火墙
1.查看防火墙状态 [root@instance-xfl1djr7 ~]# /etc/init.d/iptables status 2.启动/关闭防火墙 开启防火墙 [root@instance-xf ...
- 树莓派安装realvnc_server
先 sudo raspi-config 打开VNC. 然后去realvnc官网去下载raspberry的vncserver 已经ssh连接的前提下可以电脑下载后使用scp命令转移到树莓派上,使用以下命 ...
- github 提供的 api
api api 说明网址 举例 搜索仓库,可根据语言.stars数搜索 https://developer.github.com/v3/search/#search-repositories http ...