单链表通过结点存储数据,每一个数据都是一个结点
结点由当前数据的指针(这里可以理解为java的对象)、数据内容、下一个结点的指针三部分组成
单链表的结构如下图
看懂下面代码所需知识:java基础
单链表结构类:Node
public class Node{
public int id;
public String value;
public Node next;
public Node(int id,String value){
this.id = id;
this.value = value;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Node{");
sb.append("id=").append(id);
sb.append(", value='").append(value).append('\'');
if(next != null)
sb.append(", next.id=").append(next.id);
else
sb.append(", next.id=").append("null");
sb.append('}');
return sb.toString();
}
}
单链表操作类:NodeOption
import java.util.Stack;
public class NodeOption {
//尾部增加新的结点
public void add(Node head,Node newNode){
while(head.next != null){
head = head.next;
}
head.next = newNode;
}
//删除指定结点
public void delete(Node head,int delId){
while (head.next != null){
if(head.next.id == delId){
head.next = head.next.next;
return;
}
head = head.next;
}
System.out.println("未找到要删除的结点");
}
//修改替换指定结点
public void update(Node head,Node updateNode){
while (head.next != null){
if(head.next.id == updateNode.id){
updateNode.next =head.next.next;
head.next = updateNode;
}
head = head.next;
}
}
//打印链表
public void display(Node head){
while(head != null){
System.out.println(head.toString());
head = head.next;
}
}
//查找倒数第k个链表 - 双指针实现
public Node findback(Node head,int k){
Node t1 = head;
Node t2 = head;
if(k > 0){
for(int i = 0; i < k-1;i++){//t2先走k-1步
if(t2.next == null){ //k比链表长
System.out.println("未找到,k不正确");return null;
}
t2 = t2.next;
}
while (t2.next!= null){//t1和t2一起走
t1 = t1.next;
t2 = t2.next;
}
return t1;
}
System.out.println("未找到,k不正确");
return null;//k<=0
}
//逆序打印链表,不改变结构 -使用栈实现
public void backDisplay(Node head){
Stack<Node> stack = new Stack<>();
while (head != null){//不用head.next能保证最后一个元素入栈
stack.push(head);
head = head.next;
}
while (!stack.empty()){
System.out.println(stack.pop());
}
}
//反转链表并打印,头变尾,尾变头
public void changeBackNode(Node head){
Node reversed = null;//反转后的链表
Node preNode = null;//前一个结点
while (head != null){
Node tempNext = head.next;//先保存下一个结点到一个临时遍历,用于移动
if(tempNext == null)//传入的链表为null时到达最后一个结点
reversed = head;
head.next = preNode;//将【下一个结点】设置为【前一个结点】
preNode = head;//每次遍历更新【前一个结点】
head = tempNext;
}
//打印更改后的链表
while (reversed!=null){
System.out.println(reversed);
reversed = reversed.next;
}
}
//合并并打印两个有序链表 - 使用递归实现
public Node merge(Node head1,Node head2){
if(head1 == null) return head2;
if(head2 == null) return head1;
Node mergeNode = null;//保存合并后的链表
if(head1.id <= head2.id){//就把head作为头结点
mergeNode = head1;
mergeNode.next = merge(head1.next,head2);
}else{//就把head2作为头结点
mergeNode = head2;
mergeNode.next = merge(head1,head2.next);
}
return mergeNode;
}
//判断单链表是否有环 - 快慢双指针
public boolean isLoop(Node head){
if(head == null || head.next == null)//没有节点或者只有一个结点
return false;
Node fast = head;
Node slow = head;
while(true){//这里fast和slow初值一样,所以不能用while(fast!=slow)作为条件来遍历
if(fast == null || fast.next == null)//遍历到尾部说明没有环
return false;
fast = fast.next.next;
slow = slow.next;
if(fast == slow) break;//相遇结束
}
return true;//相遇说明有环
}
//找到环的入口结点 - 快慢双指针,就是判断是否有环的基础上再用一个指针从头结点开始出发
public Node findLoop(Node head){
if(head == null || head.next == null)
return null;
Node fast = head;
Node slow = head;
while(true){
if(fast == null || fast.next == null)
return null;
fast = fast.next.next;
slow = slow.next;
if(fast == slow) break;
}
//前面部分和查找环是一样的,目的是让fast和slow相遇
//相遇后fast就可以不用了,将其回到头部,把slow留在原地
//然后fast和slow同步移动,相遇则为入口节点(算法就是这么巧妙神奇)
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
测试类:Test
public class Test {
public static void main(String[] args){
NodeOption nodeoption = new NodeOption();
//相关操作
// Node headnode = new Node(0,"head Node");
// init1(headnode);//初始化
// Node newNode = new Node(6,"new Node");
// Node updateNode = new Node(3,"update Node");
//增删改
// nodeoption.add(headnode,newNode);
// nodeoption.delete(headnode,2);
// nodeoption.update(headnode,updateNode);
//遍历链表
// nodeoption.display(headnode);
//链表查询倒数第k个结点
// System.out.println("查找结果:"+nodeoption.findback(headnode,2));
//链表逆序打印
// nodeoption.backDisplay(headnode);
//链表反转
// nodeoption.changeBackNode(headnode);
//链表合并
// Node headnodeA = new Node(1,"A链表heda node");
// Node headnodeB = new Node(0,"B链表heda node");
// init2(headnodeA,headnodeB);//初始化两个链表
// Node megerNode = nodeoption.merge(headnodeA,headnodeB);
// nodeoption.display(megerNode);
//查找环的开始节点
Node headnode = new Node(0,"可能有环的结点");
init3(headnode);
System.out.println("是否有环"+nodeoption.isLoop(headnode));
System.out.println("查找结果:"+nodeoption.findLoop(headnode));
}
//初始化一个链表
public static void init1(Node headnode){
//初始化一个结点
Node node1 = new Node(1,"node1");
Node node2 = new Node(2,"node2");
Node node3 = new Node(3,"node3");
Node node4 = new Node(4,"node4");
Node node5 = new Node(5,"node5");
headnode.next = node1;
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
}
//初始化两个链表
public static void init2(Node headnodeA,Node headnodeB){
Node nodeA1 = new Node(1,"nodeA1");
Node nodeA2 = new Node(3,"nodeA2");
Node nodeA3 = new Node(5,"nodeA3");
Node nodeB1 = new Node(2,"nodeB1");
Node nodeB2 = new Node(4,"nodeB2");
Node nodeB3 = new Node(6,"nodeB3");
headnodeA.next = nodeA1;
nodeA1.next = nodeA2;
nodeA2.next = nodeA3;
headnodeB.next = nodeB1;
nodeB1.next = nodeB2;
nodeB2.next = nodeB3;
}
/**
* 初始化一个环形链表
* 0 --> 1 --> 2 --> 3 --> 4 --> 5
* 👆 👇
* 👈👈👈👈👈👈👈👈
*/
public static void init3(Node headNode){
Node node1 = new Node(1,"node1");
Node node2 = new Node(2,"node2");
Node node3 = new Node(3,"node3");
Node node4 = new Node(4,"node4");
Node node5 = new Node(5,"node5");
headNode.next = node1;
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node3;
}
}
注释中如有不解的或写错的希望留言指正