自定义的Linked类
package com.amanda.demo03;
/*
* Linked:单向链表实现类
*/
public class Linked {
Node first; // 头结点
Node last; // 尾节点
int size; // 链表长度
// Node链表中的节点
static class Node {
int val; // 数据
Node next; // 下一个元素
public Node(int x) {
val = x;
}
}
// 添加链表元素(尾插法)
public void add(int val) {
// 获取链表的尾节点
final Node l = last;
// 创建新节点
final Node newNode = new Node(val);
// 判断原来的尾节点是否等于null
if (l != null) {
l.next = newNode; // 让原来的尾节点的next -> 新节点
} else {
first = newNode; // 首节点 -> 新节点
}
last = newNode; // 尾节点 -> 新节点
size++; // 链表长度递增
}
/*
* 获取链表长度
*/
public int size() {
int size = 0;
for (Node x = first; x != null; x = x.next) {
size++;
}
return size;
}
/*
* 遍历链表所有节点
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Node x = first; x != null; x = x.next) {
if (x.next != null) {
sb.append(x.val + "->");
} else {
sb.append(x.val);
}
}
return sb.toString();
}
}
一、反转链表
思路:先将链表中的元素存储至栈中,借助栈的特性先进后出,再将栈中的元素出栈实现链表的反转
实现过程如下:
public static void main(String[] args) {
Linked node = new Linked();
node.add(1);
node.add(2);
node.add(3);
System.out.println("顺序链表:"+node);
Linked link = reverse(node);
System.out.println("逆序链表:"+link);
}
public static Linked reverse(Linked link) {
// 用于存储链表中的Node对象
Stack<Linked.Node> stackNode = new Stack<Linked.Node>();
// 用于保存出栈的Node节点链表
Linked result = new Linked();
// 获取当前链表的头节点
Node currentNode = link.first;
// 如果头节点为null,则返回null
if (currentNode == null) {
return null;
}
// 否则将链表中的Node对象存储至栈中,并向下遍历
while (currentNode != null) {
stackNode.add(currentNode);
currentNode=currentNode.next;
}
// 出栈,将Node对象保存至新链表中
while(!stackNode.isEmpty()) {
result.add(stackNode.pop().val);
}
return result;
}
二、计算两数之和
思路:使用链表计算两数之和,打破了数据类型的有限性。由于使用单项列表,只能从头开始遍历所以头节点保存的是数字的个位,依次类推
实现过程如下:
public static void main(String[] args) {
Linked link1 = new Linked();
link1.add(2);
link1.add(4);
link1.add(7);
Linked link2 = new Linked();
link2.add(5);
link2.add(6);
link2.add(3);
Linked link3 = addTwoNumbers(link1, link2);
System.out.println("数字1:"+link1);
System.out.println("数字2:"+link2);
System.out.println("两数和:"+link3);
}
public static Linked addTwoNumbers(Linked link1, Linked link2) {
// 用于保存两束之和的链表
Linked resultLink = new Linked();
// 获取两个链表的头节点
Node n1 = link1.first;
Node n2 = link2.first;
// 用于保存进位值
int carry = 0;
// 循环相加
while (n1 != null || n2 != null) {
// 获取两个节点的值,为null则让其为0
int x = n1 != null ? n1.val : 0;
int y = n2 != null ? n2.val : 0;
// 计算两数+进位值之和
int sum = x + y + carry;
// 将余数存储值链表
resultLink.add(sum % 10);
// 获取进位数
carry = sum / 10;
// 判断n1,n2是否为null
if (n1 != null) {
n1 = n1.next;
}
if (n2 != null) {
n2 = n2.next;
}
}
// 判断两数最高位是否产生进位数
if (carry != 0) {
resultLink.add(carry);
}
return resultLink;
}
三、检查链表是否有环
思路:有两种做法,①采用Set集合的contains()方法;②采用快慢指针
Set集合
public static boolean hasCycle1(Node node) {
// 先判断链表是否为空链表
if (node == null) {
return false;
}
// 用于保存Node对象
HashSet<Node> set = new HashSet<Node>();
while (node != null) {
// 判断set集合是否包含Node节点
if (set.contains(node)) {
// 产生环
return true;
} else {
// 无环
set.add(node);
node = node.next;
}
}
return false;
}
快慢指针
public static boolean hasCycle2(Node head) {
// 判断头节点是否存在
if (head == null) {
return false;
}
// 获取头节点
Node fast = head;
Node slow = head;
while (fast != null && fast.next != null && slow != null) {
// 快指针每次走两步
fast = fast.next.next;
// 慢指针每次走一步
slow = slow.next;
// 判断两指针是否会相遇
if (fast == slow) {
return true;
}
}
return false;
}
四、检查链表是否相交
思路:①采用双重循环;②采用双指针
双重循环:采用双重遍历一一比较
public static boolean isIntersect1(Linked link1, Linked link2) {
// 双重循环
for (Node p = link1.first; p != null; p = p.next) {
for (Node q = link2.first; q != null; q = q.next) {
if (q == p) {
return true;
}
}
}
return false;
}
双指针:首先保证两条链表的长度一致,所以在链表长度不一致的情况下,让长的链表先走,直至与短链表的长度一致
public static boolean isIntersect2(Linked link1, Linked link2) {
// 判断连个链表不为空链表
if (link1 == null || link2 == null) {
return false;
}
// 获取长链表的头节点
Node p = link1.size() > link2.size() ? link1.first : link2.first;
// 获取短链表的头节点
Node q = link1.size() > link2.size() ? link2.first : link2.first;
// 计算两个链表长度差值
int diff = Math.abs(link1.size() - link2.size());
// 让长链表先移动,使其两个链表的长度相等
while (diff-- > 0) {
p = p.next;
}
// q和p同时移动
while (q != p) {
q = q.next;
p = p.next;
}
// 跳出循环的两种可能
// 1:链表遍历完毕
// 2:q和p相等
if (q != null) {
return true;
} else {
return false;
}
}