1、反转单链表。
单链表结构如下图:
反转的话,那么第一个将会变成最后一个节点。那么用一个front来记录反转子链的引用。一开始没有反转,所以,front=null
然后通过p = head.next,可以找到第一个节点。很明显,然后p.next = front,很明显这一步会把p的下一步信息给覆盖掉,因为还有p后面的数据,所以这里先用一个临时节点存起来。after= p.next;所以是先temp = p.next ,然后在p.next = front。做完这一步,很明显要把字链的第一个节点给存起来备用。所以front = p;同时p指向下一个节点。即p = after;然后重复上述的操作,知道p==null停。
package com.lizhi.java8;
public class TestNode {
public static void main(String[] args) {
Node head = new Node();
Node n1 = new Node();
n1.setData(1);
head.setNext(n1);
Node n2 = new Node();
n2.setData(2);
n1.setNext(n2);
Node n3 = new Node();
n3.setData(3);
n2.setNext(n3);
Node n4 = new Node();
n4.setData(4);
n3.setNext(n4);
Node p = head.getNext();
while(p != null) {
System.out.println(p.getData());
p = p.getNext();
}
reverse(head);
System.out.println("-----");
Node p1 = head.getNext();
while(p1 != null) {
System.out.println(p1.getData());
p1 = p1.getNext();
}
}
//反转单链表
public static void reverse(Node head) {
if(head == null || head.getNext() == null) {
return;
}
Node p = head.getNext();
Node front = null;
Node after = null;
while(p != null) {
//要记录其after,不然,当p.setNext后,会找不到原来下一位的节点
after = p.getNext();
p.setNext(front);
front = p;
p = after;
}
head.setNext(front);//将头节点只想反转后最前的节点
}
}
2、双链表的反转和单链表一样,只不过循环中,多了一个设置pre的步骤。在结尾处,出来设置头结点,还有把第一个节点的上一个节点设置为头结点。
package com.lizhi.java8;
public class TestNode2 {
public static void main(String[] args) {
Node2 head = new Node2();
Node2 n1 = new Node2();
n1.setData(1);
n1.setPre(head);
head.setNext(n1);
Node2 n2 = new Node2();
n2.setData(2);
n2.setPre(n1);
n1.setNext(n2);
Node2 n3 = new Node2();
n3.setData(3);
n3.setPre(n2);
n2.setNext(n3);
Node2 n4 = new Node2();
n4.setData(4);
n4.setPre(n3);
n3.setNext(n4);
Node2 p = head.getNext();
while(p != null) {
System.out.println(p.getData());
p = p.getNext();
}
reverse(head);
System.out.println("-----");
Node2 p1 = head.getNext();
while(p1 != null) {
System.out.println(p1.getData());
p1 = p1.getNext();
}
}
//反转双链表
public static void reverse(Node2 head) {
if(head == null || head.getNext() == null) {
return;
}
Node2 p = head.getNext();
Node2 front = null;
Node2 after = null;
while(p != null) {
//要记录其after,不然,当p.setNext后,会找不到原来下一位的节点
after = p.getNext();
p.setNext(front);
p.setPre(after);//设置上一个节点。
front = p;
p = after;
}
head.setNext(front);//将头节点只想反转后最前的节点
front.setPre(head);//要设置pre
}
}
3、判断链表是不是回文链表。
回文数是指正着和反着的顺序都是一样的。
比如12321,就是回文数。1221也是。正着读反着读都是一样的。
第一种方式如果对空间复杂度没有要求的话,可以使用栈来处理,就是遍历链表依次将节点传入压如栈中。
然后再便利一遍,同时将栈中的数据弹出,进行比较。如果全部比较完都一样,那么就是回文链表,否则不是。
时间复杂读O(n),空间复杂读O(n)
package com.lizhi.java8;
import java.util.Stack;
/**
* 随便写了一个例子
* @author Admin
*
*/
public class CircleNum {
public static void main(String[] args) {
/**
* 1->2->2->1->null
*/
Node head = new Node();
Node n1 = new Node();
n1.setData(1);
head.setNext(n1);
Node n2 = new Node();
n2.setData(2);
n1.setNext(n2);
Node n3 = new Node();
n3.setData(2);
n2.setNext(n3);
Node n4 = new Node();
n4.setData(1);
n3.setNext(n4);
System.out.println( isCircleNum(head));
}
public static boolean isCircleNum(Node head) {
Stack stack = new Stack<>();
Node p = head;
//入栈过程
while(p.getNext() != null) {
stack.push(p.getNext());
p = p.getNext();
}
//出栈比较
Node p1 = head;
while(p1.getNext() != null) {
p1 = p1.getNext();
if(p1.getData() != stack.pop().getData()) {
//只要有一个不想等,那么就返回false
return false;
}
}
return true;
}
}
//这个是另外一个类,不在同一个文件中,因为同一个文件只能有一个公共类。
public class Node {
private int data;
private Node next;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
第二种方式跟第一种的时间差不多,不过是对链表的后半部分入栈而已。举出下面例子。
那么这个是后面的3和2入栈。对于奇数的情况
那么是5,3,2入栈。那么如何才能找到上述的位置呢。
采用快指针和慢指针的方式。就是一个走两步,一个走一步。当快指针走到不能再走的时候,只需将慢指针移动一位便是需要的位置了。
代码如下,会有相应的注释。时间复杂读O(n),空间复杂读O(n),不过常数项都比较小。所以性能好点。
package com.lizhi.java8;
import java.util.Stack;
/**
* 随便写了一个例子
* @author Admin
*
*/
public class CircleNum {
public static void main(String[] args) {
/**
* 1->2->3->2->1->null
*/
Node head = new Node();
Node n1 = new Node();
n1.setData(1);
head.setNext(n1);
Node n2 = new Node();
n2.setData(2);
n1.setNext(n2);
Node n3 = new Node();
n3.setData(3);
n2.setNext(n3);
Node n4 = new Node();
n4.setData(2);
n3.setNext(n4);
Node n5 = new Node();
n5.setData(1);
n4.setNext(n5);
System.out.println( isCircleNum2(head));
}
public static boolean isCircleNum2(Node head) {
if(null == head.getNext()) {
//为空链表时返回false
return false;
}
if(null == head.getNext().getNext()) {
//只有一个元素的时候,返回true
return true;
}
//对应的栈
Stack stack = new Stack<>();
//快指针
Node fast = head;
//慢指针
Node slow = head;
//用于对比的指针
Node p1 = head;
//注意判断条件都是用fast,时间n/2
while(fast.getNext() != null && fast.getNext().getNext() != null) {
//快指针走两步
fast = fast.getNext().getNext();
//慢指针走一步
slow = slow.getNext();
}
//循环结束只需将慢指针走一步便是,时间n/2,空间n/2
Node mid = slow.getNext();
while(mid != null) {
stack.push(mid);
mid = mid.getNext();
}
Node temp = stack.pop();
//时间n/2
while(temp != null) {
if(temp.getData() != p1.getNext().getData()) {
//只要有一个不相等,就返回false
return false;
}
p1 = p1.getNext();
if(!stack.isEmpty()) {
temp = stack.pop();
}else {
//栈为空的时候,循环条件结束
temp = null;
}
}
return true;
}
第三种方法也是用到快指针和满指针的方式。不过这次是要把后半部分的指针转过来
偶数的情况:
public class CircleNum {
public static void main(String[] args) {
/**
* 1->2->3->2->1->null
*/
Node head = new Node();
Node n1 = new Node();
n1.setData(1);
head.setNext(n1);
Node n2 = new Node();
n2.setData(2);
n1.setNext(n2);
Node n3 = new Node();
n3.setData(1);
n2.setNext(n3);
Node n4 = new Node();
n4.setData(21);
n3.setNext(n4);
Node n5 = new Node();
n5.setData(1);
n4.setNext(n5);
System.out.println( isCircleNum3(head));
}
public static boolean isCircleNum3(Node head) {
if(null == head.getNext()) {
//为空链表时返回false
return false;
}
if(null == head.getNext().getNext()) {
//只有一个元素的时候,返回true
return true;
}
Node fast = head;
Node slow = head;
while(fast.getNext() != null && fast.getNext().getNext() != null) {
slow = slow.getNext();
fast = fast.getNext().getNext();
}
slow = slow.getNext();
Node temp = null;
Node temp2 = null;
while(slow != null) {
temp2 = slow.getNext();
//防止这一部把next的信息丢失,所以上述要先保存下一个的数据。
slow.setNext(temp);
//保存当前节点,用于下一次设置上述next的结果。
temp = slow;
slow = temp2;
}
Node p1 = head;
//当上述的循环跳出的时候,temp来到尾部
Node rear = temp;
boolean flag = true;
while(temp != null) {
if(temp.getData() != p1.getNext().getData()) {
flag = false;
break;
}
temp = temp.getNext();
p1 = p1.getNext();
}
Node temp3 = null;
Node temp4 = null;
//将链表调整回来
while(rear != null) {
//也是防止下一个节点的信息丢失
temp3 = rear.getNext();
rear.setNext(temp4);
temp4 = rear;
rear = temp3;
}
return flag;
}
这个的空间复杂读可以变为O(1),时间复杂读为O(n)