链表常见算法
1. Java 实现链表的数据结构
主要的实现方式是在类中设置一个 Node
的内部类,用来存储链表的节点
/**
* 链表数据结构联系
*
* @author Jiantao Yan
* @title: MyLink
* @date 2020/3/23 18:32
*/
public class MyLink {
Node head = null;
/**
* 链表数据结构
*/
class Node {
Node next = null;
int data;
public Node(int data) {
this.data = data;
}
}
/**
* 添加元素
* @param data
*/
public void add(int data) {
Node node = new Node(data);
if (head == null) {
head = node;
return;
}
Node pointer = head;
while (pointer.next != null) {
pointer = pointer.next;
}
pointer.next = node;
}
/**
* 将元素添加到指定的下标
* @param data 数据
* @param index 下标
*/
public void add(int data, int index) {
// 得到链表长度
int length = length();
if (index <0 || index > length) {
System.out.println("数组下标错误");
return;
}
Node node = new Node(data);
if (index == 0) {
node.next = head;
head = node;
return;
}
int i = 1;
Node preNode = head;
Node curNode = head.next;
while (curNode != null) {
if (i == index) {
node.next = preNode.next;
preNode.next = node;
return;
}
if (i == length+1) {
curNode.next = node;
}
preNode = curNode;
curNode = curNode.next;
i++;
}
preNode.next = node;
}
/**
* 根据链表下标删除链表
* @param index 元素的下标
* @return 是否删除成功
*/
public boolean delete(int index) {
// 得到链表长度
int length = length();
// 数组下标错误
if (index < 0 || index > length) {
System.out.println("下标错误");
return false;
}
// 删除第一个元素
if (index == 0) {
head = head.next;
return true;
}
// 删除中间末尾元素
Node preNode = head;
Node curNode = head.next;
int i = 1;
while (curNode != null) {
if (i == index) {
preNode.next = curNode.next;
return true;
}
preNode = curNode;
curNode = curNode.next;
i++;
}
preNode.next = null;
return true;
}
/**
* 计算链表的长度
* @return 链表的长度
*/
public int length() {
int length = 0;
Node pointer = head;
while (pointer != null) {
pointer = pointer.next;
length++;
}
return length;
}
@Override
public String toString() {
if (head == null) {
System.out.println("链表为空");
return "链表为空";
}
StringBuffer sb = new StringBuffer();
Node pointer = head;
while (pointer.next != null) {
sb.append(pointer.data).append("->");
pointer = pointer.next;
}
sb.append(pointer.data);
return sb.toString();
}
}
2. 链表常用的算法
以下算法均写在
MyLink
类中
2.1 单链表反转
使用数组暂存方式
/**
* 链表反转
* 将链表数据放入一个数组中
* 再次新建一个链表
*/
public void reverseLink1() {
int length = this.length();
int [] arr = new int[length];
if (head == null || head.next == null) {
return;
}
int i = 0;
Node pointer = head;
while (pointer != null) {
arr[i] = pointer.data;
pointer = pointer.next;
i++;
}
head = new Node(arr[length -1]);
Node pointer2 = head;
for (int j = 0; j < length -1; j++) {
pointer2.next = new Node(arr[length-j-2]);
pointer2 = pointer2.next;
}
}
使用链表逆转方法
/**
* 链表反转算法-2
* next 指向 head.next 的下个节点 (前移一位)
* head.next 指向 pre 上个节点 (指针反向)
* pre 指向 head 节点 (前移一位)
* head 指向 next 节点 (前移一位)
* 动图地址:https://boolean-dev.oss-cn-hangzhou.aliyuncs.com/blog/file/2020-03/v2-c434ed3a1a229820ae04b07f26896d32_b.webp
*/
public void reverseLink2() {
if (head ==null || head.next == null) {
return;
}
Node preNode = null;
Node nextNode = null;
while (head != null) {
nextNode = head.next;
head.next = preNode;
preNode = head;
head = nextNode;
}
}
翻转动图:
为链表加环
此处环为首尾相连
/**
* 为链表添加一个环
* @param data data
*/
public void addRing(int data) {
Node node = new Node(data);
if (head == null) {
head = node;
return;
}
node.next = head;
Node pointer = head;
while (pointer.next != null) {
pointer = pointer.next;
}
pointer.next = node;
}
链表环的检测
/**
* 检测是否有环
* 快慢指针方法
* @return 环的位置
*/
public int checkRing() {
if (head == null) {
System.out.println("链表为空");
return 0;
}
Node one = head;
Node two = head.next.next;
int index = 0;
while (one.next != null) {
if (one.next == two.next) {
break;
}
one = one.next;
if (two.next != null && two.next.next != null) {
two = two.next.next;
}else {
return -1;
}
index++;
}
return index;
}
2.3 两个有序的链表合并
我这写的是两个链表合并,例如1->2->3->4和2->3->4,这里合并为1->2->3->4->2->3->4。但是我觉得题目的意思是合并为1->2->2->3->3->4->4,我这里应该写错了
/**
* 合并两个链表
* @param second 待合并链表
*/
public void merge(MyLink second) {
if (head == null) {
head = second.head;
}
// 遍历到第一个链表结尾
Node pointer = head;
while (pointer.next != null) {
pointer = pointer.next;
}
pointer.next = second.head;
}
2.4 删除链表倒数第 n 个结点
/**
* 删除倒数第 N 个数据
* 使用快慢指针,快指针比慢指针先走 N 个元素
* 随后快慢指针同样的速度走,当快指针走到终点,即找到了要删除的元素
* @param reciprocalIndex 删除的倒数下标位置
* @return 删除是否成功
*/
public boolean deleteReciprocal(int reciprocalIndex) {
int length = this.length();
if (reciprocalIndex > length-1) {
System.out.println("数组下标越界");
return false;
}
Node slow = head;
Node fast = head;
for (int i = 0; i < reciprocalIndex; i++) {
fast = fast.next;
}
int index = 0;
Node preNode = null;
while (fast.next != null) {
preNode = slow;
slow = slow.next;
fast = fast.next;
index++;
}
// 第一个节点
if(preNode == null) {
head = head.next;
}else {
preNode.next = slow.next;
}
return true;
}
2.5 求链表的中间结点
/**
* 查找中间节点
* 利用快慢指针,快指针比慢指针每步多走一步
* 当快指针走到末尾,慢指针走到中间
* 注意链表奇偶长度判断,此处偶数取中间靠右节点
* @return 中间节点数据
*/
public int findMid() {
// 如果为空链表,则返回-1
if (head == null) {
return -1;
}
// 如果节点只有一个,则直接返回第一个
if (head.next == null) {
return head.data;
}
// 快慢指针查找中点
boolean isSinger = true;
Node slow = head;
Node fast = head;
while (fast.next != null) {
if (fast.next.next == null) {
isSinger = false;
break;
}
slow = slow.next;
fast = fast.next.next;
}
// 判断长度为双还是为单
if (isSinger) {
return slow.data;
}else {
return slow.next.data;
}
}