链表

链表

1. 链表(Linked List)介绍

链表是有序的列表,但是它在内存中的存储如下图所示:

 

 

 

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

2)每个节点包含data域,next域:指向下一个节点

3)参照上图,发现链表的各个节点不一定是连续存储的

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

 

单链表(带头节点)逻辑结构示意图如下:

 

 

 

2. 单链表的应用实列

使用带 head 头的单向链表实现 –水浒英雄排行榜管理完成对英雄人物的增删改查操作。注意:只介绍增加、删除和修改,查询后面再介绍。

1)第一种方法:在添加英雄时,直接添加在链表的尾部。

思路示意图:

 

 

2) 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

思路分析示意图:

 

 

3) 修改节点

分析思路:

(1) 先找到该节点,通过遍历,

(2)   temp.name = newHeroNode.name ;

   temp.nickname= newHeroNode.nickname

4) 删除节点

思路分析示意图

 

 

5)代码实现

  1 package com.hut.linkedlist;
  2 
  3 import com.hut.linkedlist.HeroNode;
  4 
  5 /*
  6  * 使用带 head 头的单向链表实现 –水浒英雄排行榜管理完成对英雄人物的增删改查操作。
  7  * */
  8 
  9 public class SingleLinkedListDemo {
 10 
 11     public static void main(String[] args) {
 12         //进行测试
 13         //先创建节点
 14         HeroNode hero1 = new HeroNode(1,"宋江","及时雨");
 15         HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");
 16         HeroNode hero3 = new HeroNode(3,"吴用","智多星");
 17         HeroNode hero4 = new HeroNode(4,"林冲","豹子头");
 18         
 19         //创建要给的链表
 20         SingleLinkedList  singleLinkedList = new SingleLinkedList();
 21         
 22 /*        //加入,不会按照编号大小排列
 23         singleLinkedList.add(hero1);
 24         singleLinkedList.add(hero2);
 25         singleLinkedList.add(hero3);
 26         singleLinkedList.add(hero4);
 27         //显示链表
 28         singleLinkedList.list();
 29 */
 30     
 31         //加入和插入数据节点
 32             //会按照编号的大小排列
 33         singleLinkedList.addByOrder(hero1);
 34         singleLinkedList.addByOrder(hero4);
 35         singleLinkedList.addByOrder(hero3);
 36         singleLinkedList.addByOrder(hero2);
 37         //显示链表
 38         singleLinkedList.list();
 39         
 40         //根据编号删除节点    
 41         System.out.println("=======================");
 42         System.out.println("删除前的链表情况:");
 43         singleLinkedList.list();
 44         
 45         singleLinkedList.del(2);
 46         System.out.println("删除后的链表情况:");
 47         singleLinkedList.list();
 48 
 49     }
 50 
 51 }
 52 
 53 
 54 //定义单链表SingleLinkedList 管理英雄
 55 class SingleLinkedList{
 56     //先初始化一个头节点, 头节点不要动, 不存放具体的数据
 57     private HeroNode head = new HeroNode(0,"","");
 58     
 59     //添加节点到单链表尾部
 60             //思路,当不考虑编号顺序时,
 61             //1、找到当前链表的最后一个节点;2、将最后节点的next指向新的节点
 62     public void add(HeroNode newHeroNode) {
 63         //因为head节点不能动,因此需要一个辅助变量遍历  temp
 64         HeroNode temp = head;
 65         //遍历链表的最后
 66         while(true) {
 67             //找到链表的最后
 68             if(temp.next == null) {
 69                 break;
 70             }
 71             //如果没有找到,temp后移
 72             temp = temp.next;
 73         }
 74         //当while循环结束后,temp就指向了链表的最后
 75             //将最后节点的next指向新的节点
 76         temp.next = newHeroNode;
 77     }
 78     
 79     //添加节点到单链表中
 80             //在添加英雄时,根据编号no将英雄插入到指定的位置
 81             //(如果有这个排名,则添加失败,并给出提示)
 82     public void addByOrder(HeroNode newHeroNode) {
 83         //因为头节点不能动,因此通过一个辅助指针(变量)来帮助找到添加的位置
 84         //因为单链表,因为我们找的 temp 是位于 添加位置的前一个节点,否则插入不了
 85         HeroNode temp = head;
 86         boolean flag = false;  //flag标志添加的编号是否存在,默认为false
 87         while(true) {
 88             if(temp.next == null) {  //说明已经在链表的最后
 89                 break;
 90             }
 91             if(temp.next.no > newHeroNode.no) {  //位置找到,就在temp的后面插入
 92                 break;
 93             }else if(temp.next.no == newHeroNode.no) {  //说明要添加的编号已经存在
 94                 flag = true;
 95                 break;
 96             }
 97             temp = temp.next;
 98         }
 99         //判断flag的值
100         if(flag) {  //不能添加,编号已经存在
101             System.out.printf("插入英雄的编号%d已经存在,不能再次添加了。\n",newHeroNode.no);
102         }else {
103             //插入链表中,在temp后面
104             newHeroNode.next = temp.next;
105             temp.next=newHeroNode;
106         }
107     }
108     
109     //修改节点信息
110             //根据编号no来修改,即no不能改变
111         public void update(HeroNode newHeroNode) {
112             //判断是否为空
113             if(head.next == null) {
114                 System.out.println("链表为空~~");
115                 return;
116             }
117             //找到需要修改的节点,根据编号no
118                 //定义一个辅助变量
119             HeroNode temp = head.next;
120             boolean flag = false;//表示是否找到该节点
121             while(true) {
122                 if(temp == null) {
123                     break;  //已经遍历完毕
124                 }
125                 if(temp.no == newHeroNode.no) {   //找到要修改的节点
126                     flag = true;
127                     break;
128                 }
129                 temp = temp.next;
130             }
131             //根据flag判断是否找到要修改的节点
132             if(flag) {
133                 temp.name = newHeroNode.name;
134                 temp.nickname = newHeroNode.nickname;
135             }else {
136                 System.out.printf("没有找到编号为%d的节点,无法修改~~",newHeroNode.no);
137             }
138         }
139         
140         //删除节点
141                 //思路:1. head 不能动,因此我们需要一个 temp 辅助节点找到待删除节点的前一个节点
142                 // 2. 在比较时,是 temp.next.no 和 需要删除的节点的 no 比较
143             public void del(int no) {
144                 HeroNode temp = head;
145                 boolean flag = false;//标志是否找到待删除的节点
146                 while(true) {
147                     if(temp.next == null) { //已经到了链表的最后
148                         break;
149                     }
150                     if(temp.next.no == no) {  //找到待删除的前一个节点temp
151                         flag = true;
152                         break;
153                     }
154                     temp = temp.next;  //temp后移,遍历
155                 }
156                 //判断flag是否找到待删除的前一个节点
157                 if(flag) {
158                     temp.next = temp.next.next;
159                 }else {
160                     System.out.printf(" 要删除的%d节点不存在~~",no);
161                 }
162             }
163     
164     //显示链表(遍历)
165     public void list() {
166         //判断链表是否为空
167         if(head.next == null) {
168             System.out.println("链表为空~~");
169             return;
170         }
171         //因为头节点,不能动,因此我们需要一个辅助变量来遍历
172         HeroNode temp = head.next;
173         while(true) {
174             //判断链表是否到最后
175             if(temp == null) {
176                 break;
177             }
178             //输出节点信息
179             System.out.println(temp);
180             //将temp后移,一定要小心
181             temp = temp.next;
182         }
183     }
184 }
185 
186 
187 //定义HeroNode节点,每个HeroNode对象就是一个节点
188 class HeroNode{
189     public int no;   //编号
190     public String name;    //姓名
191     public String nickname;    //绰号
192     public HeroNode next;  //指向下一个节点
193     
194     //构造器
195     public HeroNode(int no,String name,String nickname) {
196         this.no = no;
197         this.name = name;
198         this.nickname = nickname;
199     }
200 
201     //为了显示,重写toString方法
202     @Override
203     public String toString() {
204         return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
205     }
206     
207     
208 }

 

3. 单链表面试题

单链表的常见面试题有如下:

1)求单链表中有效节点的个数

核心代码如下:

 1 //求单链表中有效节点的个数
 2     public static int getLength(HeroNode head) {
 3         if(head.next == null) {  //空链表
 4             return 0;
 5         }
 6         int length = 0;
 7         //定义一个辅助变量,这里我们没有统计头节点
 8         HeroNode cur = head.next;
 9         while(cur != null) {
10             length ++;
11             cur = cur.next;   //遍历
12         }
13         return length;
14     }

2)查找单链表中的倒数第K个节点

核心代码如下:

 

 1 //查找单链表中的倒数第K个节点
 2         //思路:
 3         //1、编写一个方法,接收head节点,同时接收一个index
 4         //2、index表示倒数第index节点
 5         //3、先把链表从头到尾遍历,得到链表的总长度getLength
 6         //4、得到size后,我们从链表的第一个节点开始遍历(size - index)个,就可以得到
 7         //5、如果找到了,则返回该节点,否则返回null
 8     public static HeroNode findLastIndexNode(HeroNode head,int index) {
 9         //判断如果链表为空,返回null
10         if(head.next == null) {
11             return null;//没有找到
12         }
13         //遍历得到的链表长度
14         int size = getLength(head);
15         //第二次遍历size - index位置,就是我们要找的第K个节点 
16         //先做一个index校验
17         if(index <=0 || index > size) {
18             return null;
19         }
20         //定义一个辅助变量,for循环定位倒数的index位置
21         HeroNode cur = head.next;
22         for(int i =0;i< size - index;i ++) {
23             cur = cur.next;
24         }
25         return cur;
26     }

 

3)单链表的反转

思路分析图解:

 

 

 

 核心代码如下:

 1 //链表的反转
 2         //思路:
 3         //1、先定义一个节点reverseHead = new HeroNode();
 4         //2、从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
 5         //3、原来链表的head.next = reverseHead.next
 6     public static void reversetList(HeroNode head) {
 7         //如果当前链表为空,或者只有一个节点,则根本不需要反转,直接返回
 8         if(head.next == null || head.next.next == null) {
 9             return;
10         }
11         //定义一个辅助的变量,帮助我们遍历原来的链表
12         HeroNode cur = head.next;
13         HeroNode next = null;//指向当前节点【cur】的下一个节点
14         //定义一个新的头节点
15         HeroNode reverseHead = new HeroNode(0,"","");
16         //遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表 reverseHead 的最前端
17         while(cur != null) {
18             next = cur.next;   //被取出节点的下一个节点
19             cur.next = reverseHead.next;//将 cur 的下一个节点指向新的链表的最前端
20             reverseHead.next = cur; //将 cur 连接到新的链表上
21             cur = next;//让 cur 后移遍历链表
22         }
23         //将 head.next 指向 reverseHead.next , 实现单链表的反转
24         head.next = reverseHead.next;
25     }

4)从尾到头打印单链表 【百度,要求方式 1:反向遍历 。 方式 2:Stack 栈】

思路分析图解:

 

 核心代码如下:

 1 //单链表的逆序打印代码
 2         //可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果
 3     public static void reversePrint(HeroNode head) {
 4         //判断链表是否为空
 5         if(head.next == null) {
 6             return;  
 7         }
 8         //创建一个栈,将各个节点压入栈中
 9         Stack<HeroNode> stack = new Stack<HeroNode>();
10         HeroNode cur = head.next;
11         //将链表的所有节点压入栈中
12         while(cur != null) {
13             stack.push(cur);
14             cur = cur.next;    //cur后移,压入下一个节点
15         }
16         //将栈中的所有节点进行打印,pop出栈
17         while(stack.size() > 0) {
18             System.out.println(stack.pop());   //栈的特点时先进后出
19         }
20     }

 

posted @ 2021-02-03 22:04  安逸的坐姿  阅读(116)  评论(0编辑  收藏  举报