链表:
是一种物理储存结构上的非连续储存结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
知道头节点便可以访问链表中的所有节点。
如下代码创建链表类,并访问:
package list;
public class List1 {
public static void main(String[] args) {
Node head = new Node(7);
head.next = new Node(3);
head.next.next = new Node(8);
head.next.next.next = new Node(5);
head.next.next.next.next = new Node(6);
Node n = head; //从头部节点开始访问
while (n != null) { //只要引用了节点就继续访问节点数据
System.out.println(n.value); //访问节点中的数据
n = n.next; //得到n后面的节点,保存到n
}
}
static class Node {//链表类
private int value;//节点数据
private Node prev;
private Node next;
public Node(int value) {
this.value = value;
}
}
}
访问中间节点:
通过两个变量,变量fast走一步,变量slow走两步以此求出中间值。
偶数情况求中间值过程:
奇数情况求中间值过程:
代码如下:
package list;
public class ZhongJianJieDian {
public static void main(String[] args) {
Node head = new Node(7);
head.next = new Node(3);
head.next.next = new Node(8);
head.next.next.next = new Node(5);
head.next.next.next.next = new Node(6);
Node mid = middleNode(head);
System.out.println(mid.value);
//Node n = head; //从头部节点开始访问
//while (n != null) { //只要引用了节点就继续访问节点数据
// System.out.println(n.value); //访问节点中的数据
// n = n.next; //得到n后面的节点,保存到n
//}
}
private static Node middleNode(Node head) {
Node fast = head; //快引用,引用头部节点
Node slow = head; //慢引用,引用头部节点
//fast在范围内并且它的下一个也不是null
while (fast != null && fast.next != null) {//两种情况
fast = fast.next.next; //fast向后引用下两个节点
slow = slow.next; //slow向后引用下一个节点
}
return slow; //循环结束slow所引用的节点,就是中间节点
}
static class Node {
private int value;
private Node prev;
private Node next;
public Node(int value) {
this.value = value;
}
}
}
环形链表:
将末位置引用链接一个位置形成一个环(注意:一个也可以形成环,自己链接自己)
同样通过两个变量,变量fast走一步,变量slow走两步,当两个值重合时便说明有环形。否则fast走到末尾结束便无环形。
有环形链表的情况:
无环形链表的情况:
package list;
public class HuanXingLianBiao {
public static void main(String[] args) {
Node head = new Node(8);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(1);
head.next.next.next.next = new Node(9);
head.next.next.next.next.next = head.next; //形成环,从末尾节点应用前面的节点
boolean b = hasCircle(head);
System.out.println("是否有环:"+b);
}
/*
思路: 用快慢指针,快指针每次移动两步,慢指针每次移动一步,
如果移动过程中,快慢指针重合,就表示有环;
如果快指针都移动结束,就表示没有环。
1. 变量fast, slow,都引用头部节点
2. 进入循环,执行条件:fast!=null,并且fast.next!=null
3. fast和slow一次次向后移动
4. fast和slow如果引用同一个节点,直接返回true
5. 循环结束,没有环,返回false
*/
private static boolean hasCircle(Node head) {
Node fast = head; //快指针
Node slow = head; //慢指针
//fast在中间位置,还可以继续向后移动
while (fast != null && fast.next != null) {
fast = fast.next.next; //fast走两步
slow = slow.next; //slow走一步
if (fast == slow) { //fast有没有绕回来,和slow重合
return true; //重合就是有环
}
}
return false; //fast不能再继续向后移动时,就是没有环
}
static class Node {
int value;
Node prev;
Node next;
Node(int value) {
this.value = value;
}
}
}
翻转链表:
翻转链表: 从头部节点开始,一个节点一个节点地拆出来,重新反向连接
代码如下,具体步骤见注释:
package list;
public class FanZhuanLianBiao {
public static void main(String[] args) {
Node head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
Node head2 = fanZhuan(head);
Node n = head2;
while (n != null) {
System.out.println(n.value);
n = n.next;
}
}
/*
翻转链表思路: 从头部节点开始,一个节点一个节点地拆出来,重新反向连接
1. 变量current,先引用头部节点
2. 变量next,表示新链表中的下一个节点,开始是null
3. 进入循环,循环条件:current!=null
4. 定义tmp变量表示当前节点的下一个节点
5. current的下一个,引用next
6. 修改next变量,引用current节点
7. 修改current变量,引用tmp节点
8. 循环结束后,next引用的节点,就是新的头部节点,直接返回next
*/
private static Node fanZhuan(Node head) {
if (head == null || head.next == null) {
return head;
}
Node current = head;
Node next = null;
while (current != null) {
Node tmp = current.next;
current.next = next;
next = current;
current = tmp;
}
return next;
}
static class Node {
int value;
Node prev;
Node next;
Node(int value) {
this.value = value;
}
}
}