问题描述
如图所示,给定一个链表的 头节点 head ,请判断其是否为回文链表。如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。详见leetcode。
方法一 通过数组实现
方法介绍
遍历链表将链表中的值存储到数组中,利用数组下标从两端向中间比较。如果对应下标(和为n-1)对应的值全部相同,则链表是回文链表,否则不是回文链表。(不建议用,有点逃避链表操作的意思)
代码实现
public static boolean isHuiWen(LinkedNode head){
ArrayList<Integer> arrayList = new ArrayList<>();
int count = 0;
while (head !=null){
arrayList.add(count,head.data);
count++;
head = head.next;
}
for (int i = 0; i < count/2; i++) {
if (arrayList.get(i) != arrayList.get(count-1-i)){
return false;
}
}
return true;
}
方法二 通过栈实现
方法介绍
按照回文序列的定义,从前往后和从后往前是一样的,可以先遍历链表,将链表元素压入栈中,然后在遍历链表,每遍历一个元素,就出栈一个元素,比较是否相同。如全部相同,则是回文序列,否则不是。(遍历链表相当于从前向后,出栈元素相当于从后向前)
代码实现
public static boolean isHuiWen(LinkedNode head){
Stack<LinkedNode> stack = new Stack<>();
LinkedNode iter = head;
while (iter !=null){
stack.push(iter);
iter = iter.next;
}
while (head !=null){
LinkedNode node = stack.pop();
if (head.data!=node.data){
return false;
}
head = head.next;
}
return true;
}
方法三 方法二优化
方法介绍
第一次遍历链表,获取链表的长度,第二次遍历,前一半链表元素压栈,后一半链表元素出栈与后一半遍历元素比较,如全部相同,则是回文链表,否则不是。与方法二相比,节约了一半空间。(相当于栈中的前一半与链表的后一半比较)
代码实现
public static boolean isHuiWen(LinkedNode head) {
Stack<LinkedNode> stack = new Stack<>();
LinkedNode iter = head;
int count = 0;
while (iter != null) {
count++;
iter = iter.next;
}
for (int i = 0; i < count / 2; i++) {
stack.push(head);
head = head.next;
}
if (count % 2 != 0) {
head = head.next;
}
while (head != null) {
LinkedNode node = stack.pop();
if (node.data != head.data) {
return false;
}
head = head.next;
}
return true;
}
方法四 方法二优化
方法介绍
第一次遍历,获取链表长度,并将链表元素压栈,第二次遍历一半元素,出栈一半元素比较,如果对应元素全部相等,则是回文序列,否则不是,相当于少了1/2次遍历(相当于栈中的后一半与链表的前一半比较)
代码实现
public static boolean isHuiWen(LinkedNode head) {
Stack<LinkedNode> stack = new Stack<>();
LinkedNode iter = head;
int count = 0;
while (iter != null) {
count++;
stack.push(iter);
iter = iter.next;
}
for (int i = 0; i < count / 2; i++) {
LinkedNode node = stack.pop();
if (node.data != head.data) {
return false;
}
head = head.next;
}
return true;
}
方法五 通过链表实现
方法介绍
上面提到的栈本质上是存储了链表的逆序,所以,我们同样可以新建一个链表,遍历原链表,采用头插法,将原链表逆序存储为新链表在进行比较,之后也可据此进行优化,优化方式大致形同,遍历两次,存储一半元素,或存储全部元素,遍历一次半,只是数据结构的使用不同,方法没有本质区别。这里并不是赘述方法,而是在学习数据结构时,可以尝试用不同的数据结构来解决相同的问题,既能有助于对基本数据结构的了解,又能提升解决问题的灵活性,重要的是让我们的大脑保持活跃,更好的思考
代码实现
public static boolean isHuiWen(LinkedNode head) {
LinkedNode newHead = null;
LinkedNode iter = head;
while (iter != null) {
LinkedNode node = new LinkedNode(iter.data);
node.next = newHead;
newHead = node;
iter = iter.next;
}
while (head != null) {
if (head.data != newHead.data){
return false;
}
head = head.next;
newHead = newHead.next;
}
return true;
}
方法六 通过双指针(快慢指针)实现
方法介绍
可以通过设置 slow和fast两个指针,遍历链表,slow每次移动一个节点,fast每次移动两个节点,当fast到达链表尾部时,slow正好到链表的中间位置,在加一个指针iter遍历链表,相当于slow遍历链表的后一半,反转链表的后一半,(反转链表后面会更,更完补充本部分的代码实现),iter遍历链表的前一半,比较对应元素是否相同,全部相同则是回文序列,否则不是。
代码实现
public static boolean isHuiWen(LinkedNode head) {
LinkedNode slow = head;
LinkedNode fast = head;
while (fast!=null&&fast.next!=null){
slow = slow.next;
fast = fast.next.next;
}
LinkedNode reverseHead = reverse(slow);
while (head!=slow){
if (head.data != reverseHead.data){
return false;
}
head = head.next;
reverseHead = reverseHead.next;
}
return true;
}
public static LinkedNode reverse(LinkedNode head){
LinkedNode pre = null;
LinkedNode current = head;
LinkedNode post;
while (current!=null){
post = current.next;
current.next = pre;
pre = current;
current = post;
}
return pre;
}