经典算法(三) 单链表 反转 & 是否相交/成环 & 求交点 等
参考文章:
判断链表是否相交: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 ...
随机推荐
- 地产propretie单词propretie财产
中文名:房产财产地产 外文名:property.propretie 释义:财产.所有物等 用法:作名词. 词汇搭配动词+-等 目录 1 英文释义 2 释义例句 3 词汇搭配 4 衍生 英文释义 1. ...
- mongo数据库的一些命令(对于新同学,按照我的步骤连一遍即可)
进入mongo mongo 查看数据库 show dbs;/show databases(); 第一个命令简单(admin和local数据库是系统自带的) 进入/创建数据库 use 数据库名字(如果有 ...
- 网络编程系统化学习(1.1.)--socket基础
大纲 学完该阶段内容,你将会很好的完成如下的面试题 socket面试试题相关: 1.写一个简单的socket程序,实现客户端发送数据,服务端收到数据后,将该内容转发给客户端 2.简要概述一下socke ...
- kuangbin专题专题四 Silver Cow Party POJ - 3268
题目链接:https://vjudge.net/problem/POJ-3268 题意:点X处开办排队,其他点的牛到X点去参加派对,然后从X点回到各自的点,通路是单向的,所有牛都要走最短路, 求出所有 ...
- MySQL利用IF查询不同条件并分别统计记录数
数据库记录如下: 现在要查询统计出每个'name'的'result'分别为'success'和'fail'的次数: 利用IF条件判断满足条件为1,不满足为0,再用SUM函数求和,最后通过'name'分 ...
- httprunner学习23-加解密
前言 有些接口的请求参数是加密的,返回的接口内容也是需要解密才能看到. 加密接口 比如当我们访问下面这个登陆的接口时,请求参数账号和密码都是需要加密,通过parms参数传过去,服务器才能识别到 没加密 ...
- thrift rpc通信
thrift rpc通信 框架 别人的简历: 负责抓取程序的开发和维护,对抓取内容进行数据提取.整理.1.定向数据抓取程序的维护和开发,了解了Sqlite数据库.Thrift服务和多线程的开发调试.2 ...
- Linux SSH 连接安全设置
一.更换端口, 可以在一定程度上防止扫描攻击 vim /etc/ssh/sshd_config 将 port 一项从 22 更改为高位端口, 然后重启 ssh 服务 systemctl restart ...
- Centos7.x for aarch64 下载地址
ARM64架构系统 CentOS7镜像下载源 http://archive.kernel.org/centos-vault/altarch/ 例如:centos7.5 http://archive.k ...
- hive的常用函数工作总结
1.concat_ws 它是一个特殊形式的 CONCAT() concat_ws(分隔符,参数1,参数2.......) as 字段 2.split 返回值为一个数组 a.基本用法: 例1:split ...