双向链表
单向链表的缺点分析:
1) 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。
2) 单向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除 时节点,总是找到 temp,temp 是待删除节点的前一个节点(认真体会).
对上图的说明:
分析 双向链表的遍历,添加,修改,删除的操作思路===》代码实现
1) 遍历 和 单链表一样,只是可以向前,也可以向后查找
2) 添加 (默认添加到双向链表的最后)
(1) 先找到双向链表的最后这个节点
(2) temp.next = newHeroNode
(3) newHeroNode.pre = temp;
添加:添加到链表中间去
如图:
3) 修改 思路和 原来的单向链表一样.
4) 删除
(1) 因为是双向链表,因此,我们可以实现自我删除某个节点
(2) 直接找到要删除的这个节点,比如 temp
(3) temp.pre.next = temp.next;
(4) temp.next.pre = temp.pre;
双向链表的应用实例
使用带 head 头的双向链表实现 –水浒英雄排行榜
代码实现:
1 package com.hut.linkedlist.test2; 2 3 import com.hut.linkedlist.test2.HeroNode; 4 5 /* 6 * 使用带 head 头的双向链表实现 –水浒英雄排行榜 7 * */ 8 9 public class DoubleLinkedListDemo { 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 DoubleLinkedList doubleLinkedList = new DoubleLinkedList(); 21 /* //测试添加节点到末尾 22 //添加节点到末尾 23 doubleLinkedList.add(hero1); 24 doubleLinkedList.add(hero2); 25 doubleLinkedList.add(hero3); 26 doubleLinkedList.add(hero4); 27 //显示添加后的双向链表 28 doubleLinkedList.list(); 29 */ 30 //测试根据编号插入节点 31 doubleLinkedList.add(hero1); 32 doubleLinkedList.add(hero2); 33 doubleLinkedList.add(hero4); 34 System.out.println("插入前链表显示情况:"); 35 doubleLinkedList.list(); 36 doubleLinkedList.addByOrder(hero3); 37 System.out.println("插入后链表显示情况:"); 38 doubleLinkedList.list(); 39 40 //测试修改节点 41 System.out.println("========================================="); 42 System.out.println("修改前的链表显示:"); 43 doubleLinkedList.list(); 44 doubleLinkedList.update(new HeroNode(4,"废林冲","受气包")); 45 System.out.println("修改后的链表显示:"); 46 doubleLinkedList.list(); 47 48 //测试删除节点 49 System.out.println("========================================="); 50 System.out.println("删除前的链表显示:"); 51 doubleLinkedList.list(); 52 doubleLinkedList.del(4); 53 System.out.println("修改后的链表显示:"); 54 doubleLinkedList.list(); 55 56 } 57 58 } 59 60 //定义单链表DoubleLinkedList 管理英雄 61 class DoubleLinkedList { 62 // 先初始化一个头节点, 头节点不要动, 不存放具体的数据 63 private HeroNode head = new HeroNode(0, "", ""); 64 65 public HeroNode getHead() { 66 return head; 67 } 68 69 public void setHead(HeroNode head) { 70 this.head = head; 71 } 72 73 //功能一、 添加一个节点到双向链表的最后 74 public void add(HeroNode newHeroNode) { 75 //因为head节点不能动,所以需要一个辅助变量temp来遍历链表 76 HeroNode temp = head; 77 //遍历双向链表,找到末尾 78 while(true) { 79 if(temp.next == null) { 80 break; 81 } 82 //如果没有找到则后移 83 temp = temp.next; 84 } 85 //当退出while循环时,temp就指到了最后 86 temp.next = newHeroNode; 87 newHeroNode.pre = temp; 88 } 89 90 //功能二、按照编号 no 添加节点到双向链表中去 91 public void addByOrder(HeroNode newHeroNode) { 92 //定义一个辅助变量temp来帮助遍历链表 93 HeroNode temp = head.next; 94 boolean flag = false; //标志编号是否已经存在 95 while(true) { 96 if(temp == null) { 97 break; //已经遍历结束 98 } 99 if(temp.no > newHeroNode.no) { 100 break; //位置已经找到 101 } 102 if(temp.no == newHeroNode.no) { 103 flag = true; 104 break; 105 } 106 temp = temp.next; 107 } 108 //根据flag值的情况来判断 109 if(flag) { 110 System.out.printf("编号为%d的节点已经存在,不能再次插入~~",newHeroNode.no); 111 }else { 112 //核心操作 113 temp.pre.next = newHeroNode; 114 newHeroNode.pre = temp.pre; 115 newHeroNode.next = temp; 116 temp.pre = newHeroNode; 117 } 118 119 } 120 121 // 功能三、修改一个节点的内容, 可以看到双向链表的节点内容修改和单向链表一样 122 public void update(HeroNode newHeroNode) { 123 //判断双向链表是否为空 124 if(head.next == null) { 125 System.out.println("双向链表为空,无法修改~~"); 126 return; 127 } 128 //根据编号no找到要修改的节点 129 HeroNode temp = head.next; 130 boolean flag = false;//标志是否找到该节点 131 while(true) { 132 if(temp == null) { 133 break; //已经遍历完链表 134 } 135 if(temp.no == newHeroNode.no) { 136 flag = true; 137 break; 138 } 139 temp = temp.next; 140 } 141 //当while循环结束后,就会找到要修改的节点 142 if(flag) { 143 temp.name = newHeroNode.name; 144 temp.nickname = newHeroNode.nickname; 145 }else { 146 System.out.printf("没有找到编号为%d的节点,无法修改~~",newHeroNode.no); 147 } 148 } 149 150 // 功能四、从双向链表中删除一个节点 151 //根据编号 no来删除 152 public void del(int no) { 153 //判断链表是否为空 154 if(head.next == null) { 155 System.out.println("双向链表为空,无法删除~~"); 156 return; 157 } 158 HeroNode temp = head.next; 159 boolean flag = false;//标志是否找到要删除的节点 160 while(true) { 161 if(temp == null) { 162 break; //链表遍历完毕 163 } 164 if(temp.no == no) { 165 flag = true; 166 break; 167 } 168 temp = temp.next; 169 } 170 //当遍历完毕就会找到要删除的节点 171 if(flag) { 172 temp.pre.next = temp.next; 173 // 如果是最后一个节点,就不需要执行下面这句话,否则出现空指针 174 if(temp.next != null) { 175 temp.next.pre = temp.pre; 176 } 177 }else { 178 System.out.println("没有找到要被删除的该节点~~"); 179 } 180 } 181 182 //功能五、 遍历双向链表的方法 183 // 显示双向链表【遍历】 184 public void list() { 185 //判断双向链表是否为空 186 if(head.next == null) { 187 System.out.println("双向链表为空~~"); 188 return; 189 } 190 //定义一个辅助变量来帮忙遍历双向链表 191 HeroNode temp = head.next; 192 while(true) { 193 if(temp == null) { 194 break; 195 } 196 //输出节点信息 197 System.out.println(temp); 198 temp = temp.next; //节点后移,一定要注意 199 } 200 } 201 202 } 203 204 //定义HeroNode节点,每个HeroNode对象就是一个节点 205 class HeroNode { 206 public int no; // 编号 207 public String name; // 姓名 208 public String nickname; // 绰号 209 public HeroNode next; // 指向下一个节点 210 public HeroNode pre; // 指向上一个节点 211 212 // 构造器 213 public HeroNode(int no, String name, String nickname) { 214 this.no = no; 215 this.name = name; 216 this.nickname = nickname; 217 } 218 219 // 为了显示,重写toString方法 220 @Override 221 public String toString() { 222 return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]"; 223 } 224 225 }