算法 (九)链表相关:判断链表是不是不回文结构

本文探讨了三种判断链表是否为回文结构的方法。第一种方法使用栈辅助,对比原链表与弹栈链表;第二种方法利用快慢指针找到中间节点,将后半部分压栈后再对比;第三种方法在常数空间内完成,通过反转后半部分链表进行比较,并在完成后恢复原始结构。此外,还提及了反转链表的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、判断链表是不是回文结构

1.1 描述:

回文就是123321或者12321,也就是正着反着读都一样

1.2 思路:

有点复杂,三个阶段难度,适合和面试官唠嗑(下面还有两个拓展)

  1. 第一种:需要N额外空间,也就是说要用一个辅助结构,这里是Stack就是把链表压入到栈中,然后用原来的链表和弹栈的链表逐个比对
  2. 第二种:需要N/2额外空间,我们先用快慢指针找到中间节点,然后把后半部分压栈,然后再向上面一样弹栈做比较
  3. 第三种:需要常数O(1)额外空间,即不用外部辅助结构,只用有限变量,难点就在于反转后半部分链表然后再转回来我们先用快慢指针找到中间节点,然后把后半部分转向,然后一边从右向左,另一边从左向右,逐个比较,最后,切记,要把后半部分已经转向的部分再转回来(不能改变原始结构)
  4. 还有就是直接反转链表,这个思路和上面第一种压栈相似,不过也只需要常数O(1)额外空间(重建了一个链表应该算O(N)),直接套用上一篇博文的反转链表函数就行了,在此不表
  5. 还可以用数组存储链表的value,然后再比较,这个需要预先计算出链表的长度(因为数组是固定长度的,静态初始化内存),太啰嗦,不表
package cn.nupt;

import static org.hamcrest.CoreMatchers.nullValue;

import java.util.Stack;

/**
 * @Description: 判断链表是不是回文结构(三种方式)
 *
 * @author PizAn
 * @Email pizan@foxmail.com
 * @date 2019年2月18日 下午7:28:59
 * 
 */
public class IsPalindromeList2 {

	// 链表结构
	public static class Node {
		public int value;
		public Node next;

		public Node(int data) {
			this.value = data;
		}
	}

	// 第一种:需要N额外空间,也就是说要用一个辅助结构,这里是Stack
	// 就是把链表压入到栈中,然后用原来的链表和弹栈的链表逐个比对
	public static boolean isPalindrome1(Node head) {

		Stack<Node> stack = new Stack<Node>();
		// 为什么要用curNode代替head?因为这里的head = head.next改变了head指针
		Node curNode = head;

		// 将链表压栈,因为泛型是Node(不是value的类型),所以这里要压入Node,而不是Node.value
		while (curNode != null) {
			stack.push(curNode);
			curNode = curNode.next;

		}

		while (head != null) {
			if (head.value != stack.pop().value) {
				return false;
			}
			head = head.next;

		}

		return true;

	}

	// 第二种:需要N/2额外空间,我们先用快慢指针找到中间节点,然后把后半部分压栈,然后再向上面一样弹栈做比较
	public static boolean isPalindrome2(Node head) {
		// 空链表或只有一个节点的链表就不用比了,直接输出
		if (head == null || head.next == null) {
			return true;
		}

		// 从head出设置快慢指针,慢指针一次走一步,快指针一次走两步,快慢指针这里的作用就是找到中点
		Node slow = head;
		Node fast = head;

		// 当快指针不能走了的时候(其实下面只需要fast.next.next != null就可以了),慢指针指在中点(奇数在中点,偶数在中点偏左)
		while (fast.next != null && fast.next.next != null) {
			slow = slow.next;
			fast = fast.next.next;
		}
		// 这里慢指针往右走一下,在回文结构的右边(偶数就在右边,奇数重点不管,还是在右边)
		// 比如 1 2 2 1,这里的slow指向右边的2
		slow = slow.next;

		// 将右边的节点压栈
		Stack<Node> stack = new Stack<Node>();
		while (slow != null) {
			stack.push(slow);
			slow = slow.next;
		}

		// 将栈里的元素弹出来和链表进行比较(这里是以栈非空为标准,其实上面一个方法也可以以栈非空为标准)
		while (!stack.isEmpty()) {
			if (head.value != stack.pop().value) {// 这里是已经把弹出了一个数用来比较了
				return false;
			}
			head = head.next;
		}

		return true;

	}

	// 第三种:需要常数O(1)额外空间,即不用外部辅助结构,只用有限变量,难点就在于反转后半部分链表然后再转回来
	// 我们先用快慢指针找到中间节点,然后把后半部分转向,然后一边从右向左,另一边从左向右,逐个比较,
	// 最后,切记,要把后半部分已经转向的部分再转回来(不能改变原始结构)
	public static boolean isPalindrome3(Node head) {
		// 空链表或只有一个节点的链表就不用比了,直接输出
		if (head == null || head.next == null) {
			return true;
		}

		// 从head出设置快慢指针,慢指针一次走一步,快指针一次走两步,快慢指针这里的作用就是找到中点
		Node slow = head;
		Node fast = head;

		// 当快指针不能走了的时候(其实下面只需要fast.next.next != null就可以了),慢指针指在中点(奇数在中点,偶数在中点偏左)
		while (fast.next != null && fast.next.next != null) {
			slow = slow.next;
			fast = fast.next.next;
		}

		// 1>2>3>3>2>1>null,这里的slow指向左边的3(这里就用偶数个做示范,因为奇数个相对简单一点)
		// 我们要做到左3指空,后面的链表反转3>3>2>1>null 变成
		// 1>2>3>3>null(其实感觉可以调前面的反转链表的函数,因为那个函数只要输入head表头就行了,这里重新写一下吧)

		Node pre = null;
		Node next = null;

		while (slow != null) {
			next = slow.next;
			slow.next = pre;
			pre = slow;
			slow = next;

		}

		// 这样我们就得到了1>2>3(>null)<3<2<1,然后两边比较,注:这里右边的1就是我们的pre(具体看前面的链表反转的细节)

		Node head1 = head;// 开始比较,这里再用一个变量来表示head,因为head最好不要变
		Node lastNode = pre;// 这里我存储一下最后一个节点,下面反转的时候要用
		// 这个地方为什么要弄个res,因为如果直接return
		// false,下面还要把后面转向的部分再转回来,也就是说还要往下运行,不能让程序在这个地方断了
		boolean res = true;

		while (pre != null && head1 != null) {
			if (head1.value != pre.value) {
				res = false;
				break;
			}

			head1 = head1.next;
			pre = pre.next;
		}

		// 比较完之后还要把后面的链表反转回去,也就是说不能改变原有的结构
		// 注:上面的head1就是左3,pre是右3,lastNode是最后一个节点,我们从最后一个节点开始反转

		Node pre2 = null;// 这里为了反转,再创建这两个辅助变量
		Node next2 = null;

		while (lastNode != null) {
			next2 = lastNode.next;
			lastNode.next = pre2;
			pre2 = lastNode;
			lastNode = next2;
		}

		return res;

	}

	// 打印链表
	public static void printLinkedList(Node node) {
		System.out.print("Linked List: ");
		while (node != null) {
			System.out.print(node.value + " ");
			node = node.next;
		}
		System.out.println();
	}

	// 主函数测试
	public static void main(String[] args) {
		Node head = null;
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

		head = new Node(1);
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

		head = new Node(1);
		head.next = new Node(2);
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

		head = new Node(1);
		head.next = new Node(1);
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

		head = new Node(1);
		head.next = new Node(2);
		head.next.next = new Node(3);
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

		head = new Node(1);
		head.next = new Node(2);
		head.next.next = new Node(1);
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

		head = new Node(1);
		head.next = new Node(2);
		head.next.next = new Node(3);
		head.next.next.next = new Node(1);
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

		head = new Node(1);
		head.next = new Node(2);
		head.next.next = new Node(2);
		head.next.next.next = new Node(1);
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

		head = new Node(1);
		head.next = new Node(2);
		head.next.next = new Node(3);
		head.next.next.next = new Node(2);
		head.next.next.next.next = new Node(1);
		printLinkedList(head);
		System.out.print(isPalindrome1(head) + " | ");
		System.out.print(isPalindrome2(head) + " | ");
		System.out.println(isPalindrome3(head) + " | ");
		printLinkedList(head);
		System.out.println("=========================");

	}

}


输出:


Linked List: 
true | true | true | 
Linked List: 
=========================
Linked List: 1 
true | true | true | 
Linked List: 1 
=========================
Linked List: 1 2 
false | false | false | 
Linked List: 1 2 
=========================
Linked List: 1 1 
true | true | true | 
Linked List: 1 1 
=========================
Linked List: 1 2 3 
false | false | false | 
Linked List: 1 2 3 
=========================
Linked List: 1 2 1 
true | true | true | 
Linked List: 1 2 1 
=========================
Linked List: 1 2 3 1 
false | false | false | 
Linked List: 1 2 3 1 
=========================
Linked List: 1 2 2 1 
true | true | true | 
Linked List: 1 2 2 1 
=========================
Linked List: 1 2 3 2 1 
true | true | true | 
Linked List: 1 2 3 2 1 
=========================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值