反转链表
将一个链表进行反转,1->2->3->4变为4->3->2->1。
这应该是对链表最经典的操作了,在以前学过的数据结构里,使用的方法是头插法。理解上只需要两个指针,pre指针保存新链表的头,p指针保存断开以后的部分。第一步如下:
之后head指针向后移动,也就是和p指针指向同一个。进入第二步,p指针向后移动,让head指针的next指向pre,反转一个。pre向后移动,head向后移动。代码如下:
public class 反转链表 {
public static void main(String[] args) {
ListNode head = new ListNode(1);
head.add(2);
head.add(3);
head.add(4);
head.add(5);
System.out.println(head);
System.out.println(convert(head));
}
static ListNode convert(ListNode head){
ListNode pre = null; //头
ListNode p = null; //保存指针
while (head != null){
p = head.next;
head.next = pre;
pre = head; //头插
head = p;
}
return pre;
}
}
[0.110s][warning][os,thread] Attempt to allocate stack guard pages failed.
[1->2->3->4->5]
[5->4->3->2->1]
回文链表
判断一个链表是否是回文,就像回文数一样:12321、123321都是,链表形式也是这样。
首先的思路是借用栈结构,先进后出,12321入栈后,如果出栈还是12321那么就满足。1234入栈以后出栈就是4321了,不满足。代码可以用两种,整体入栈和取一半入栈(后半部分入栈和前半部分比较就可以判断)。代码如下:
public static void main(String[] args) {
ListNode head = new ListNode(1);
head.add(2);
head.add(3);
head.add(2);
head.add(1);
System.out.println(head);
System.out.println(isPalindrome(head));
System.out.println(isPalindrome2(head));
}
//需要一个辅助空间
public static boolean isPalindrome(ListNode head){
Stack<ListNode> stack = new Stack<>();
ListNode cur = head; //记录指针,取当前节点
while (cur != null){
stack.push(cur);
cur=cur.next;
}
while (head != null){ //从头节点开始
if (head.data != stack.pop().data){//先进后出,相当于从尾开始
return false;
}
head=head.next;
}
return true;
}
//需要1/2个辅助空间
public static boolean isPalindrome2(ListNode head){
if (head == null || head.next == null){
return true;
}
//快慢指针
ListNode s = head.next;
ListNode f = head;
while (f.next != null && f.next.next != null){//慢指针走到了mid
s = s.next;
f = f.next.next;
}
//定义栈存放后半部分元素
Stack<ListNode> stack = new Stack<>();
while (s != null){
stack.push(s);
s = s.next;
}
while (!stack.isEmpty()){
if (head.data != stack.pop().data){
return false;
}
head = head.next;
}
return true;
}
}
在使用1/2个辅助栈的时候用到了快慢指针的概念,用于链表的求中点(链表没有长度不同于线性表的求种点),这个方法常用于链表问题。因为可以求的链表的中点,可以尝试不用栈辅助,将后部分反转,然后用两个头尾指针向中点遍历,判断回文结构,就类似了线性表的回文判断了,代码如下:
//不需要辅助空间
public static boolean isPalindrome3(ListNode head){
if (head == null || head.next == null){
return true;
}
ListNode s = head;
ListNode f = head;
while (f.next != null && f.next.next != null){
s = s.next; //慢
f = f.next.next; //快
}
f = s.next; //保存后半部分
s.next = null; //断开
//反转以f开头的链表
ListNode p = null;
while (f != null){
p = f.next; //保存next后续节点
f.next = s; //convert
s = f; //s后移
f = p; //f后移
}
p = s; //p保存最后一个节点
f = head; //从头节点开始
while (s != null && f != null){
if (s.data != f.data){ //比较
return false;
}
s = s.next;
f = f.next;
}
return true;
}
三种方法测试:
[1->2->3->2->1]
true
true
true