H__D  

一、链表的介绍

  1、链表是以节点的方式来储存,是链式储存。

  2、每个节点包含data域(存放数据)、next域(指向下一个节点)。

  3、链表的各个节点不一定是连续储存的。

  4、链表分为带头节点的链表和没有头节点的链表,根据实际的需求来确定。

二、带头节点单链表示意图

  

三、编写java代码实现带头节点的单链表

3.1 应用实例

  使用带头的单向链表实现 – 水浒英雄排行榜管理完成对英雄人物的增删改查操作。

3.2 编写HeroNode类

  定义HeroNode,每个HeroNode对象就是一个节点。

/*
   定义HeroNode,每个HeroNode对象就是一个节点
 */
public class HeroNode
{
    public int no;
    public  String name;
    public  String nickname;
    public  HeroNode next; // 指向下一个节点

    // 构造器
    public HeroNode(int no, String name, String nickname){
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    // 重写toString方法
    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
    }
}

3.3 编写SingleLinkedList类

  1 package point5;
  2 
  3 import java.util.Stack;
  4 
  5 // 定义 SingleLinkedList 管理我们的英雄
  6 public class SingleLinkedList {
  7     // 现初始化一个头节点,头节点不要动,不存放具体的数据
  8     private HeroNode head = new HeroNode(0,"","");
  9 
 10     // 一、添加节点到单向链表
 11     // 思路,当不考虑编号顺序时
 12     // 1、找到当前链表的最后节点
 13     // 2、将最后这个节点的 next 指向新的节点
 14     public void add(HeroNode heroNode){
 15         // 因为 head 节点不能动,因此我们需要一个辅助遍历 temp
 16         HeroNode temp = head;
 17         // 遍历链表,找到最后
 18         while (true){
 19             // 找到链表的最后
 20             if (temp.next == null){
 21                 break;
 22             }
 23             // 如果没有找到最后,将temp后移
 24             temp = temp.next;
 25         }
 26         // 当退出while循环时,temp就指向了链表的最后
 27         // 将最后这个节点的next指向新的节点
 28         temp.next = heroNode;
 29     }
 30 
 31     // 二、按照no从小到大的顺序添加节点到单向链表
 32     // 第二种方式在添加英雄时,根据排名将英雄插入到指定位置
 33     // 如果有这个排名,则添加失败,并给出提示
 34     public void addByOrder(HeroNode heroNode){
 35         // 因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
 36         // 因为单链表,因为我们找的 temp 是位于添加位置的前一个节点,否则插入不了
 37         HeroNode temp = head;
 38         // 标志添加的编号是否存在,默认为false
 39         boolean flag = false;
 40         while (true){
 41             // 说明 temp 已经在链表的最后
 42             if (temp.next == null){
 43                 break;
 44             }
 45             // 位置找到,就在 temp 的后面插入
 46             if (temp.next.no > heroNode.no) {
 47                 break;
 48             }
 49             // 说明希望添加的 heroNode 的编号已然存在
 50             else if (temp.next.no == heroNode.no){
 51                 //说明编号存在
 52                 flag = true;
 53                 break;
 54             }
 55             // 后移,遍历当前链表
 56             temp = temp.next;
 57         }
 58         // 判断 flag 的值
 59         // 不能添加,说明编号存在
 60         if (flag){
 61             System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入\n", heroNode.no);
 62         }
 63         else {
 64             // 插入到链表中,temp的后面
 65             heroNode.next = temp.next;
 66             temp.next = heroNode;
 67         }
 68     }
 69 
 70     // 三、修改节点的信息,根据no编号来修改,即no编号不能改
 71     // 说明
 72     // 1、根据 newHeroNode 的 no 来修改即可
 73     public void update(HeroNode newHeroNode){
 74         // 判断是否空
 75         if(head.next == null){
 76             System.out.println("链表为空");
 77             return;
 78         }
 79         // 找到需要修改的节点,根据 no 编号
 80         // 定义一个辅助变量
 81         HeroNode temp = head.next;
 82         // 表示是否找到该节点
 83         boolean flag = false;
 84         while(true){
 85             // 已遍历完链表
 86             if (temp == null){
 87                 break;
 88             }
 89             if (temp.no == newHeroNode.no){
 90                 // 找到
 91                 flag = true;
 92                 break;
 93             }
 94             temp = temp.next;
 95         }
 96         // 根据 flag 判断是否找到要修改的节点
 97         if(flag){
 98             temp.name = newHeroNode.name;
 99             temp.nickname = newHeroNode.nickname;
100         }
101         // 没有找到
102         else {
103             System.out.printf("没有找到编号%d的节点,不能修改\n", newHeroNode.no);
104 
105         }
106     }
107 
108     // 四、删除节点
109     // 思路
110     // 1、head 不能动,因此我们需要一个 temp 辅助节点找到待删除节点的前一个节点
111     // 2、说明我们在比较的时,是 temp.next.no 和需要删除的节点的 no 比较
112     public void del(int no){
113         HeroNode temp = head;
114         boolean flag = false; //标志是否找到待删除节点的no
115         while (true){
116             // 已经到链表的最后
117             if (temp.next == null) {
118                 break;
119             }
120             // 找到的待删除节点的前一个节点temp
121             if (temp.next.no == no){
122                 // 找到的待删除节点的前一个节点 temp
123                 flag = true;
124                 break;
125             }
126             // temp后移,遍历
127             temp = temp.next;
128         }
129         // 判断 flag
130         if(flag){ // 找到
131             // 可以删除
132             temp.next = temp.next.next;
133         }
134         else {
135             System.out.printf("要删除的%d节点不存在\n",no);
136         }
137     }
138 
139     // 五、显示链表
140     public void list(){
141         // 判断链表是否为空
142         if (head.next == null){
143             System.out.println("链表为空");
144             return;
145         }
146         // 因为头节点,不能动,因此我们需要一个辅助变量来遍历
147         HeroNode temp = head.next;
148         while (true){
149             // 判断是否到链表最后
150             if (temp == null){
151                 break;
152             }
153             // 输出节点的信息
154             System.out.println(temp);
155             // 将 temp 后移
156             temp = temp.next;
157         }
158     }
159 
160     // 六、反转链表
161     public  void  reverseList(){
162         // 如果当前链表为空,或者只有一个节点,无需反转,直接返回
163         if (head.next == null || head.next.next == null){
164             return;
165         }
166 
167         // 定义一个辅助的指针(变量),帮助我们遍历原来的链表
168         HeroNode cur = head.next;
169         // 指向当前节点[cur]的下一个节点
170         HeroNode next = null;
171         HeroNode reverseHead = new HeroNode(0, "", "");
172         // 遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表 reverseHead 的最前端
173         while (cur != null){
174             next = cur.next; // 先暂时保存当前节点的下一个节点,后面需要使用
175             cur.next = reverseHead.next; // 将cur的下一个节点指向新的链表
176             reverseHead.next = cur; // 将cur连接到新的链表上
177             cur = next; // 让cur后移
178         }
179         // 将 head.next 指向 reverseHead.next,实现单链表的反转
180         head.next = reverseHead.next;
181     }
182 
183     // 七、求单链表中的有效节点的个数
184     public  int  getLength(){
185         /**
186          * @description:
187          * @return: 返回的就是有效节点的个数
188          * @param: head 链表的头节点
189          * @author: hyr
190          * @time: 2020/1/13 20:11
191          */
192         if(head.next == null){ // 空链表
193             return 0;
194         }
195         int length = 0;
196         // 定义一个辅助的变量,这里我们没有统计头节点
197         HeroNode cur = head.next;
198         while (cur != null){
199             length++;
200             cur = cur.next;
201         }
202         return length;
203     }
204 
205     // 八、逆序打印链表
206     public void reversePrint() {
207         /**
208          * @description: 使用栈这个数据结构,将各个节点压入栈中,再弹出就可以实现逆序打印的效果
209          * @return:
210          * @param: head 单链表的头节点
211          * @author: hyr
212          * @time: 2020/1/13 21:21
213          */
214         if (head.next == null) {
215             return; // 空链表,不能打印
216         }
217         // 创建要给一个栈,将各个节点压入栈
218         Stack<HeroNode> stack = new Stack<>();
219         HeroNode cur = head.next;
220         // 将链表中的所有节点压入栈
221         while (cur != null) {
222             stack.push(cur);
223             cur = cur.next; //cur后移,这一就可以压入下一个节点
224         }
225         // 将栈中的节点进行打印,pop出栈
226         while (stack.size() > 0) {
227             System.out.println(stack.pop()); // 栈的特点是先进后出
228         }
229     }
230 
231     // 九、查找单链表中的倒数第k个结点
232     public HeroNode findLastIndexNode(int index){
233         // 思路
234         // 1、编写一个方法,接受 head 节点,同时接收一个index
235         // 2、index表示是倒数第 index 个节点
236         // 3、先把链表从头到尾遍历,得到链表的总的长度 getLength
237         // 4、得到 size 后,我们从链表的第一个开始遍历(size-index)个,就可以得到
238         // 5、如果找到了,则返回该节点,否则返回null
239 
240         // 判断如果链表为空,返回null
241         if (head.next == null){
242             System.out.println("没有找到");
243         }
244 
245         // 第一个遍历得到链表的长度(节点个数)
246         int size = getLength();
247 
248         // 第二次遍历 size-index 位置,就是我们倒数的第K个节点
249         // 先做一个 index 的校验
250         if(index <= 0 || index > size){
251             System.out.println("要查找的位置不存在");
252         }
253 
254         // 定义给辅助变量,for循环定位到倒数的index
255         HeroNode cur = head.next;
256         for (int i = 0; i < size - index; i++) {
257             cur = cur.next;
258         }
259 
260         return cur;
261     }
262 }

3.4 编写测试类SingleLinkedListDemo

 1 package point5;
 2 
 3 /*
 4     使用带 head 头的单向链表实现 -- 水浒英雄排行榜管理, 完成对英雄人物的增删改查操作。
 5  */
 6 public class SingleLinkedListDemo {
 7     public static void main(String[] args) {
 8         // 进行测试
 9         // 先创建节点
10         HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
11         HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
12         HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
13         HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
14 
15         // 创建链表
16         SingleLinkedList singleLinkedList = new SingleLinkedList();
17 
18 //        // 一、不按顺序添加节点,进行测试
19 //        singleLinkedList.add(hero1);
20 //        singleLinkedList.add(hero2);
21 //        singleLinkedList.add(hero4);
22 //        singleLinkedList.add(hero3);
23 //        //查看一下链表的信息
24 //        singleLinkedList.list();
25 
26         // 二、按照no顺序添加节点,进行测试
27         singleLinkedList.addByOrder(hero4);
28         singleLinkedList.addByOrder(hero2);
29         singleLinkedList.addByOrder(hero1);
30         singleLinkedList.addByOrder(hero3);
31         // 查看一下链表信息
32         System.out.println("按序插入后的链表信息为:");
33         singleLinkedList.list();
34         System.out.println();
35 
36         // 三、修改节点的信息
37         singleLinkedList.update(new HeroNode(1,"宋宋","小宋"));
38         // 修改后的链表信息
39         System.out.println("修改后的链表信息为:");
40         singleLinkedList.list();
41         System.out.println();
42 
43         // 四、删除节点
44         singleLinkedList.del(1);
45         // 删除节点后的链表信息
46         System.out.println("删除节点后的链表信息为:");
47         singleLinkedList.list();
48         System.out.println();
49 
50         // 五、显示链表
51         // 当前链表信息
52         System.out.println("当前链表信息为:");
53         singleLinkedList.list();
54         System.out.println();
55 
56         // 六、反转链表
57         System.out.println("反转后的链表信息为:");
58         singleLinkedList.reverseList();
59         singleLinkedList.list();
60         System.out.println();
61 
62         // 七、求链表中有效节点的个数
63         System.out.println("链表中有效节点的个数为:");
64         System.out.println(singleLinkedList.getLength());
65         System.out.println();
66 
67         // 八、逆序打印链表
68         System.out.println("逆序打印的链表信息为:");
69         singleLinkedList.reversePrint();
70         System.out.println();
71 
72         // 九、查找链表中的倒数第k个结点
73         System.out.println("链表中的倒数第一个节点信息为:");
74         System.out.println(singleLinkedList.findLastIndexNode(1));
75     }
76 }

3.5 代码运行结果

按序插入后的链表信息为:
HeroNode [no=1, name=宋江, nickname=及时雨]
HeroNode [no=2, name=卢俊义, nickname=玉麒麟]
HeroNode [no=3, name=吴用, nickname=智多星]
HeroNode [no=4, name=林冲, nickname=豹子头]

修改后的链表信息为:
HeroNode [no=1, name=宋宋, nickname=小宋]
HeroNode [no=2, name=卢俊义, nickname=玉麒麟]
HeroNode [no=3, name=吴用, nickname=智多星]
HeroNode [no=4, name=林冲, nickname=豹子头]

删除节点后的链表信息为:
HeroNode [no=2, name=卢俊义, nickname=玉麒麟]
HeroNode [no=3, name=吴用, nickname=智多星]
HeroNode [no=4, name=林冲, nickname=豹子头]

当前链表信息为:
HeroNode [no=2, name=卢俊义, nickname=玉麒麟]
HeroNode [no=3, name=吴用, nickname=智多星]
HeroNode [no=4, name=林冲, nickname=豹子头]

反转后的链表信息为:
HeroNode [no=4, name=林冲, nickname=豹子头]
HeroNode [no=3, name=吴用, nickname=智多星]
HeroNode [no=2, name=卢俊义, nickname=玉麒麟]

链表中有效节点的个数为:
3

逆序打印的链表信息为:
HeroNode [no=2, name=卢俊义, nickname=玉麒麟]
HeroNode [no=3, name=吴用, nickname=智多星]
HeroNode [no=4, name=林冲, nickname=豹子头]

链表中的倒数第一个节点信息为:
HeroNode [no=2, name=卢俊义, nickname=玉麒麟]

 

原文链接:https://blog.csdn.net/a1786742005/article/details/103978011

posted on 2021-03-18 01:11  H__D  阅读(96)  评论(0编辑  收藏  举报