Live2D
Fork me on GitHub

4、链表-单链表

来源:https://www.bilibili.com/video/BV1B4411H76f?p=15

一、链表

1、以结点的方式进行存储;

2、每个结点包含data域和next域,data域存放数据,next域指向下一个结点;

3、链表各结点的存放不一定连续;

4、链表分有头结点的和没有头结点的,可以根据实际情况确定使用哪一种。

 

二、单链表创建思路

2.1 增加结点

2.1.1 直接添加到尾部

1、创建一个头结点(head),用来表示单链表的起始位置(不存放数据);

2、每当需要向尾部添加一个结点,找到下一个结点为空的【当前结点】,插入到当前结点的下一个位置;

3、从头结点开始,利用一个辅助变量(结点),通过遍历的方式寻找【当前结点】

2.1.2 按照编号或者说结点位置添加

 1、找到要添加位置的前一个结点,赋值给辅助变量(temp)

 2、【新结点.next】=【temp.next】

 3、【temp.next】=【新结点】

2.2 修改结点

 1、找到要修改的结点

 2、要修改的结点的内容变成新的内容

2.3 删除结点

 1、找到要删除结点的前一个结点,赋值给辅助变量(temp)

 2、【temp.next】=【temp.next.next】

 3、被删除的结点会被自动回收

 

三、实现

3.1 增加结点

3.1.1 直接添加到尾部

首先创建一个代表结点的类,类中的属性包括编号,名字,昵称。同时添加了构造器,重写了toString方法。

 1 //结点类
 2 public class Node {
 3     public int no;//编号
 4     public String name;//名字
 5     public String nickName;//昵称
 6     public Node next;
 7 
 8     public Node(int no, String name, String nickName) {
 9         this.no = no;
10         this.name = name;
11         this.nickName = nickName;
12     }
13 
14     @Override
15     public String toString() {
16         return "Node{" +
17                 "no=" + no +
18                 ", name='" + name + '\'' +
19                 ", nickName='" + nickName + '\'' +
20                 '}';
21     }
22 }

之后创建了代表单链表的类,通过调用结点,实现单链表。类中包含一个私有的头结点属性,头结点确定后不再改变,遍历时创建一个辅助变量进行循环。目前在类中实现了一个添加结点的方法(向尾部添加),同时为了便于测试,额外增加了一个展示当前链表的方法,从头结点开始依次遍历,展示当前链表的所有结点。

 1 //单链表,管理创建出来的结点
 2 public class SingleLinkedList {
 3     //初始化头结点
 4     private Node head = new Node(0,"","");
 5 
 6     //向单链表尾部添加新结点
 7     public void addNode(Node newNode){
 8         Node temp = head;//通过辅助变量temp遍历单链表,找到尾部的位置
 9         while (true){
10             if(temp.next == null){
11                 break;
12             }
13             temp = temp.next;
14         }
15         temp.next = newNode;
16     }
17 
18     //展示链表内容
19     public void show(){
20         if(head.next == null){
21             System.out.println("链表为空,无法展示");
22             return;
23         }
24         Node temp = head.next;
25         while (true){
26             if(temp == null){
27                 break;
28             }
29             System.out.println(temp);
30             temp = temp.next;
31         }
32     }
33 }

测试

 1     public static void main(String[] args) {
 2         Scanner sc = new Scanner(System.in);
 3 
 4         SingleLinkedList singleLinkedList = new SingleLinkedList();
 5 
 6         Node node1 = new Node(1, "张三", "Tom");
 7         Node node2 = new Node(2, "李四", "Jerry");
 8         Node node3 = new Node(3, "王五", "Simba");
 9         Node node4 = new Node(4, "赵六", "Shrek");
10 
11         singleLinkedList.show();
12         System.out.println();
13 
14         singleLinkedList.addNode(node1);
15         singleLinkedList.show();
16         System.out.println();
17 
18         singleLinkedList.addNode(node2);
19         singleLinkedList.show();
20         System.out.println();
21 
22         singleLinkedList.addNode(node3);
23         singleLinkedList.show();
24         System.out.println();
25 
26         singleLinkedList.addNode(node4);
27         singleLinkedList.show();
28 
29 
30     }

结果

链表为空,无法展示

Node{no=1, name='张三', nickName='Tom'}

Node{no=1, name='张三', nickName='Tom'}
Node{no=2, name='李四', nickName='Jerry'}

Node{no=1, name='张三', nickName='Tom'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=3, name='王五', nickName='Simba'}

Node{no=1, name='张三', nickName='Tom'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=3, name='王五', nickName='Simba'}
Node{no=4, name='赵六', nickName='Shrek'}

3.1.2 按照编号或者说结点位置添加

SingleLinkedList类中加入按照编号添加节点的方法(这里是按照结点中的编号属性从小到大排列结点)

 1     public void addByOrder(Node newNode){
 2         Node temp = head;
 3         //考虑到可能存在相同的结点编号,这里用一个flag表示是否已经存在相同的编号
 4         boolean flag = false;//默认链表中不存在newNode所属的编号
 5         while (true){
 6             //当前结点下一个为空
 7             if(temp.next == null){
 8                 break;
 9             }
10             //当前结点下一个结点编号大于新结点的编号,应该将新结点插入到当前节点后面(按编号从小到大排列)
11             if(temp.next.no > newNode.no){
12                 break;
13             }
14             //当前结点下一个结点编号与新结点相同,已经存在了,不再添加
15             if(temp.next.no == newNode.no){
16                 flag = true;
17                 break;
18             }
19             temp = temp.next;
20         }
21         if(flag){
22             System.out.printf("编号%d存在了",newNode.no);
23             System.out.println();
24         }else {
25             newNode.next = temp.next;
26             temp.next = newNode;
27         }
28     }

测试

 1     public static void main(String[] args) {
 2 
 3         SingleLinkedList singleLinkedList = new SingleLinkedList();
 4 
 5         Node node1 = new Node(1, "张三", "Tom");
 6         Node node2 = new Node(2, "李四", "Jerry");
 7         Node node3 = new Node(3, "王五", "Simba");
 8         Node node4 = new Node(4, "赵六", "Shrek");
 9 
10         singleLinkedList.show();
11         System.out.println();
12 
13         singleLinkedList.addByOrder(node4);
14         singleLinkedList.show();
15         System.out.println();
16 
17         singleLinkedList.addByOrder(node1);
18         singleLinkedList.show();
19         System.out.println();
20 
21         singleLinkedList.addByOrder(node3);
22         singleLinkedList.show();
23         System.out.println();
24 
25         singleLinkedList.addByOrder(node4);
26         singleLinkedList.show();
27         System.out.println();
28 
29         singleLinkedList.addByOrder(node2);
30         singleLinkedList.show();
31         System.out.println();
32     }

结果

链表为空,无法展示

Node{no=4, name='赵六', nickName='Shrek'}

Node{no=1, name='张三', nickName='Tom'}
Node{no=4, name='赵六', nickName='Shrek'}

Node{no=1, name='张三', nickName='Tom'}
Node{no=3, name='王五', nickName='Simba'}
Node{no=4, name='赵六', nickName='Shrek'}

编号4存在了
Node{no=1, name='张三', nickName='Tom'}
Node{no=3, name='王五', nickName='Simba'}
Node{no=4, name='赵六', nickName='Shrek'}

Node{no=1, name='张三', nickName='Tom'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=3, name='王五', nickName='Simba'}
Node{no=4, name='赵六', nickName='Shrek'}

3.2 修改结点

在SingleLinkedList类中加入修改结点的方法

 1     //修改节点
 2     public void update(Node newNode){
 3         if(head.next == null){
 4             System.out.println("链表为空,无法修改");
 5             return;
 6         }
 7         Node temp = head.next;
 8         //是否找到要修改的结点
 9         boolean flag = false;
10 
11         while (true){
12             if(temp == null){//没有与新结点编号一致的可以修改的结点
13                 break;
14             }
15             if(temp.no == newNode.no){
16                 flag = true;
17                 break;
18             }
19             temp = temp.next;
20         }
21         if(flag){
22             temp.name = newNode.name;
23             temp.nickName = newNode.nickName;
24         }else {
25             System.out.printf("没有找到编号为%d的结点",newNode.no);
26             System.out.println();
27         }
28     }

测试

 1     public static void main(String[] args) {
 2 
 3         SingleLinkedList singleLinkedList = new SingleLinkedList();
 4 
 5         Node node1 = new Node(1, "张三", "Tom");
 6         Node node2 = new Node(2, "李四", "Jerry");
 7         Node node3 = new Node(3, "王五", "Simba");
 8         Node node4 = new Node(1, "赵六", "Shrek");
 9         Node node5 = new Node(5, "田七", "Shrek");
10 
11         singleLinkedList.show();
12         System.out.println();
13 
14         singleLinkedList.addByOrder(node1);
15         singleLinkedList.addByOrder(node2);
16         singleLinkedList.addByOrder(node3);
17         singleLinkedList.show();
18         System.out.println();
19 
20         singleLinkedList.update(node5);
21         singleLinkedList.show();
22         System.out.println();
23 
24         singleLinkedList.update(node4);
25         singleLinkedList.show();
26         System.out.println();
27 
28     }

结果

链表为空,无法展示

Node{no=1, name='张三', nickName='Tom'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=3, name='王五', nickName='Simba'}

没有找到编号为5的结点
Node{no=1, name='张三', nickName='Tom'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=3, name='王五', nickName='Simba'}

Node{no=1, name='赵六', nickName='Shrek'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=3, name='王五', nickName='Simba'}

3.3 删除结点

在SingleLinkedList类中加入删除结点的方法

 1     public void delete(int no){
 2         if(head.next == null){
 3             System.out.println("链表为空,无法删除");
 4             return;
 5         }
 6         Node temp = head;
 7         //是否找到要删除的结点的前一个结点
 8         boolean flag = false;
 9 
10         while (true){
11             if(temp.next == null){
12                 break;
13             }
14             if(temp.next.no == no){
15                 flag = true;
16                 break;
17             }
18             temp = temp.next;
19         }
20 
21         if(flag){
22             temp.next = temp.next.next;
23         }else {
24             System.out.printf("没有找到编号为%d的结点",no);
25             System.out.println();
26         }
27     }

测试

 1     public static void main(String[] args) {
 2 
 3         SingleLinkedList singleLinkedList = new SingleLinkedList();
 4 
 5         Node node1 = new Node(1, "张三", "Tom");
 6         Node node2 = new Node(2, "李四", "Jerry");
 7         Node node3 = new Node(3, "王五", "Simba");
 8 
 9         singleLinkedList.show();
10         System.out.println();
11 
12         singleLinkedList.addByOrder(node1);
13         singleLinkedList.addByOrder(node2);
14         singleLinkedList.addByOrder(node3);
15         singleLinkedList.show();
16         System.out.println();
17 
18         singleLinkedList.delete(2);
19         singleLinkedList.show();
20         System.out.println();
21 
22         singleLinkedList.delete(3);
23         singleLinkedList.show();
24         System.out.println();
25 
26         singleLinkedList.delete(2);
27         singleLinkedList.show();
28         System.out.println();
29 
30         singleLinkedList.delete(1);
31         singleLinkedList.show();
32         System.out.println();
33 
34     }

结果

链表为空,无法展示

Node{no=1, name='张三', nickName='Tom'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=3, name='王五', nickName='Simba'}

Node{no=1, name='张三', nickName='Tom'}
Node{no=3, name='王五', nickName='Simba'}

Node{no=1, name='张三', nickName='Tom'}

没有找到编号为2的结点
Node{no=1, name='张三', nickName='Tom'}

链表为空,无法展示

 

四、附加操作

4.1 获取链表结点个数

 1     public static int getLength(Node head){
 2         if(head.next == null){
 3             return 0;
 4         }
 5         int len = 0;
 6         Node curNode = head.next;
 7         while (curNode != null){
 8             len++;
 9             curNode = curNode.next;
10         }
11         return len;
12     }

测试

 1     public static void main(String[] args) {
 2         SingleLinkedList singleLinkedList = new SingleLinkedList();
 3 
 4         Node node1 = new Node(1, "张三", "Tom");
 5         Node node2 = new Node(2, "李四", "Jerry");
 6         Node node3 = new Node(3, "王五", "Simba");
 7         Node node4 = new Node(4, "赵六", "Shrek");
 8 
 9         Node head = singleLinkedList.getHead();
10 
11         singleLinkedList.addByOrder(node1);
12         System.out.println(getLength(head));
13 
14         singleLinkedList.addByOrder(node2);
15         System.out.println(getLength(head));
16 
17         singleLinkedList.addByOrder(node3);
18         System.out.println(getLength(head));
19 
20         singleLinkedList.addByOrder(node4);
21         System.out.println(getLength(head));
22 
23     }

结果

1
2
3
4

4.2 查找倒数第k个节点

 1     public static Node findLastIndexNode(int k,Node head){
 2         if(head.next == null){
 3             return null;
 4         }
 5         int len = getLength(head);
 6         if(k <= 0 || len < k){
 7             return null;
 8         }
 9         Node curNode = head.next;
10 
11         for (int i = 0; i < len - k; i++) {
12             curNode = curNode.next;
13         }
14         return curNode;
15     }

测试

 1         Node lastIndexNode = findLastIndexNode(1, head);
 2         System.out.println("倒数第一个"+lastIndexNode);
 3 
 4         Node lastIndexNode2 = findLastIndexNode(2, head);
 5         System.out.println("倒数第二个"+lastIndexNode2);
 6 
 7         Node lastIndexNode3 = findLastIndexNode(3, head);
 8         System.out.println("倒数第三个"+lastIndexNode3);
 9 
10         Node lastIndexNode4 = findLastIndexNode(4, head);
11         System.out.println("倒数第四个"+lastIndexNode4);
12 
13         Node lastIndexNode5 = findLastIndexNode(5, head);
14         System.out.println("倒数第五个"+lastIndexNode5);

结果

倒数第一个Node{no=4, name='赵六', nickName='Shrek'}
倒数第二个Node{no=3, name='王五', nickName='Simba'}
倒数第三个Node{no=2, name='李四', nickName='Jerry'}
倒数第四个Node{no=1, name='张三', nickName='Tom'}
倒数第五个null

4.3链表反转

 1     public static void reverse(Node head){
 2         if(head.next == null || head.next.next == null){
 3             return;
 4         }
 5 
 6         //创建一个反转后的头结点
 7         Node reverseHead = new Node(0, "", "");
 8         //当前结点
 9         Node curNode = head.next;
10         //代表当前结点的下一个结点
11         Node nextNode = null;
12 
13         //以 向【反转后的头结点.next】插入结点的形式反转
14         while (curNode != null){
15             nextNode = curNode.next;//必须保证取出当前结点之后,后面的数据不丢失
16             curNode.next = reverseHead.next;//必须保证插入当前结点时,当前结点的【后面】连接的是reverseHead后的所有结点
17             reverseHead.next = curNode;//必须保证插入当前结点时,当前结点的【前面】连的是reverseHead
18             curNode = nextNode;
19         }
20         head.next = reverseHead.next;
21     }

测试

1         reverse(singleLinkedList.getHead());
2         singleLinkedList.show();

结果

Node{no=4, name='赵六', nickName='Shrek'}
Node{no=3, name='王五', nickName='Simba'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=1, name='张三', nickName='Tom'}

 

4.4链表倒序打印

不改变原有的链表

 1     //从尾到头打印链表
 2     public static void reversePrint(Node head){
 3         if(head.next == null){
 4             return;
 5         }
 6         //创建栈,将结点压入栈
 7         Stack<Node> stack = new Stack<>();
 8         Node curNode = head.next;
 9         while (curNode != null){
10             stack.push(curNode);
11             curNode = curNode.next;
12         }
13         while (stack.size() > 0){
14             System.out.println(stack.pop());
15         }
16     }

测试

1         reversePrint(singleLinkedList.getHead());
2         System.out.println();

结果

Node{no=4, name='赵六', nickName='Shrek'}
Node{no=3, name='王五', nickName='Simba'}
Node{no=2, name='李四', nickName='Jerry'}
Node{no=1, name='张三', nickName='Tom'}

 

posted @ 2020-06-14 11:58  -小二黑-  阅读(201)  评论(0编辑  收藏  举报