之前有写过一篇单链表的 C 语言实现,现在写一篇 Java 的实现
链表节点单独定义成一个静态内部类
public class TestListNode {
public static void main(String[] args) {
ListNode head = createListNode(7);
System.out.print("初始链表结构: ");
trverseNodeList(head.next);
// System.out.print("翻转后的链表结构: ");
// ListNode newHead = reverseNodeList(head.next);
// trverseNodeList(newHead);
// System.out.print("反转链表前 N 个节点: ");
// ListNode newHead = reverseN(head.next, 2);
// trverseNodeList(newHead);
System.out.print("反转链表的一部分 ");
ListNode newHead = reverseBetween(head.next, 3, 5);
trverseNodeList(newHead);
}
/**
* 链表的反转,递归方式
*
* @param head 原链表的首节点
* @return 返回新链表的首节点,注意翻转后的链表没有了头结点
*/
private static ListNode reverseNodeList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode last = reverseNodeList(head.next);
head.next.next = head;
head.next = null;
return last;
}
private static ListNode successor;
/**
* 反转链表前 N 个节点
*
* @param head 原链表的首节点
* @return 返回新链表的首节点,注意翻转后的链表没有了头结点
*/
private static ListNode reverseN(ListNode head, int n) {
if (n == 1) {
successor = head.next;
return head;
}
// 以 head.next 为起点,需要反转前 n - 1 个节点
ListNode last = reverseN(head.next, n - 1);
head.next.next = head;
head.next = successor;
return last;
}
/**
* 反转链表的一部分
* 给定区间[m, n]
*/
private static ListNode reverseBetween(ListNode head, int m, int n) {
if (m == 1) {
// 相当于反转前 n 个元素
return reverseN(head, n);
}
head.next = reverseBetween(head.next, m - 1, n - 1);
return head;
}
private static void trverseNodeList(ListNode head) {
if (head == null || head.next == null) return;
while (head != null) {
System.out.print(head.val + " ");
head = head.next;
}
System.out.println();
}
private static ListNode createListNode(int n) {
ListNode head = new ListNode();
ListNode last = head;
for (int i = 1; i <= n; i++) {
ListNode newNode = new ListNode(i);
last.next = newNode;
last = newNode;
last.next = null;
}
return head;
}
static class ListNode {
Integer val;
ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.val = val;
}
}
}
可以看到,这里把 ListNode 定义成一个静态内部类,否则是不能通过
head.next
这样直接访问实例变量,这种写法也是比较简洁
链表节点单独定义成一个类
定义节点类型
public class ListNode {
private Integer val;
private ListNode next;
public ListNode() {}
public ListNode(Integer val, ListNode next) {
this.val = val;
this.next = next;
}
public Integer getVal() {
return val;
}
public void setVal(Integer val) {
this.val = val;
}
public ListNode getNext() {
return next;
}
public void setNext(ListNode next) {
this.next = next;
}
}
在类中使用,这里直接贴出代码实现
public class TestListNode {
public static void main(String[] args) {
ListNode head = createListNode( 8);
// 因为创建链表的时候多了个头结点,这里跳过头结点进行遍历
traverseListNode(head.getNext());
ListNode newHead = reverseListNode(head.getNext());
traverseListNode(newHead);
}
/**
* 链表的反转
*
* @param head 原链表的首节点
* @return 返回新链表的首节点,注意翻转后的链表没有了头结点
*/
private static ListNode reverseListNode(ListNode head) {
if (head == null || head.getNext() == null) return head;
ListNode last = reverseListNode(head.getNext());
// 注意这种 getNext 方式和后续介绍的 head.next 方式区别
head.getNext().setNext(head);
head.setNext(null);
return last;
}
private static void traverseListNode(ListNode head) {
if (head == null || head.getNext() == null) return;
// 这种方式不大好,一轮循环下来改变了 head 的值
// while (head.getNext() != null) {
// System.out.print(head.getNext().getVal()+" ");
// head = head.getNext();
// }
// 推荐这种方式,定义一个临时指针
ListNode tmpNode = head;
// 跳过头结点,头结点的数据域是 null
while (tmpNode != null) {
System.out.print(tmpNode.getVal()+" ");
tmpNode = tmpNode.getNext();
}
System.out.println();
}
/**
* 创建链表
*
* @param n 链表的个数
* @return 返回头指针
*/
private static ListNode createListNode(int n) {
// 定义一个头指针,指向头结点,便于对链表的操作
ListNode head = new ListNode(null, null);
// 初始时刻,尾指针指向头结点
ListNode tail = head;
for (int i = 1; i <= n; i++) {
ListNode newNode = new ListNode();
newNode.setVal(i);
tail.setNext(newNode);
tail = newNode;
tail.setNext(null);
}
return head;
}
}