双向链表

  管理单向链表的缺点分析

    1、单向链表,查找的方法只能是一个方向,而双向链表可以向前或者向后查找

    2、单线链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除。

   应用实例:使用带 head 头的双向链表实现 — 水浒英雄排行榜  

   双向链表如何完成遍历,添加,修改和删除的思路示意图:

 

    对上图的说明:

  (1)遍历方式同单链表一样,可以向前遍历,也可以向后查找

  (2)添加(默认添加到双向链表的最后)

    ① 先找到双向链表的最后这个节点temp

    ② temp.next = newHeroNode;

    ③ newHeroNode.pre = temp;

  (3)修改思路同单向链表一样 

  (4)删除 

    ① 因为是双向链表,可以实现自我删除某个节点

    ② 直接找到要删除的这个节点,比如temp

    ③  temp.pre.next = temp.next;

    ④ temp.next.pre = temp.pre;(注意:这里需要分析看看temp是否为最后一个节点,如果则不需要这句话)

  (5)第二种添加方式,按照编号顺序添加

    ① 遍历链表,如果有这个排名,则添加失败,并给出提示

    ② 遍历链表,通过 flag 标志找到编号所在的位置

    ③ 插入链表中:heroNode.next = temp.next;heroNode.pre = temp;

    ④ 判断是否在最后插入:

if(temp != null) {
  temp.next = heroNode;
  temp.next.pre = heroNode;
}

 

     代码实现:

 

  1 public class DoubleLinkedListDemo {
  2 
  3     public static void main(String[] args) {
  4         // 测试
  5         System.out.println("双向链表的测试");
  6         // 先创建节点
  7         HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
  8         HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
  9         HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
 10         HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
 11 
 12         // 创建双向链表
 13         DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
 14         doubleLinkedList.add(hero1);
 15         doubleLinkedList.add(hero2);
 16         doubleLinkedList.add(hero3);
 17         doubleLinkedList.add(hero4);
 18 
 19         // 显示
 20         doubleLinkedList.list();
 21 
 22         // 修改
 23         HeroNode2 newHeroNode = new HeroNode2(4, "公孙胜", "入云龙");
 24         doubleLinkedList.update(newHeroNode);
 25         System.out.println("修改后的链表情况");
 26         doubleLinkedList.list();
 27 
 28         // 删除
 29         doubleLinkedList.del(3);
 30         System.out.println("删除后的链表情况");
 31         doubleLinkedList.list();
 32         
 33         // 按编号添加一个
 34         HeroNode2 newhero = new HeroNode2(8, "吴用2", "智多星2");
 35         doubleLinkedList.addByorder(newhero);
 36         System.out.println("添加后的链表情况");
 37         doubleLinkedList.list();
 38         
 39     }
 40 
 41 }
 42 
 43 // 创建双向链表的类
 44 class DoubleLinkedList {
 45     // 先初始化一个头节点,头节点不要动,不存放具体的数据
 46     private HeroNode2 head = new HeroNode2(0, "", "");
 47 
 48     // 返回头节点
 49     public HeroNode2 getHead() {
 50         return head;
 51     }
 52 
 53     public void setHead(HeroNode2 head) {
 54         this.head = head;
 55     }
 56 
 57     // 添加到链表尾部
 58     public void add(HeroNode2 heroNode) {
 59 
 60         // 因为head节点不能动,因此我们需要一个辅助指针 temp
 61         HeroNode2 temp = head;
 62         // 遍历
 63         while (true) {
 64             // 找到链表的最后
 65             if (temp.next == null) {
 66                 break;
 67             }
 68             // 如果没有找到最后,将 temp 后移
 69             temp = temp.next;
 70         }
 71 
 72         // 当退出 while 循环时,temp 就执行了链表的最后
 73         // 将最后这个节点的 next指向新的节点,新节点前一个为 temp
 74         temp.next = heroNode;
 75         heroNode.pre = temp;
 76     }
 77 
 78     // 修改节点的信息,根据 no 编号来修改,即no编号不能改
 79     // 根据newHeroNode 的 no 来修改即可,修改同单向链表一样
 80     public void update(HeroNode2 newHeroNode) {
 81         // 判断是否为空
 82         if (head.next == null) {
 83             System.out.println("链表为空");
 84             return;
 85         }
 86         // 找到需要修改的节点,根据 no 编号
 87         // 定义一个辅助变量
 88         HeroNode2 temp = head.next;
 89         boolean flag = false; // 表示是否找到该节点
 90         while (true) {
 91             if (temp == null) {
 92                 break; // 已经遍历完
 93             }
 94             if (temp.no == newHeroNode.no) {
 95                 flag = true;
 96                 break;
 97             }
 98 
 99             temp = temp.next;
100         }
101 
102         // 根据 flag,判断是否找到要修改的节点
103         if (flag) {
104             temp.name = newHeroNode.name;
105             temp.nickname = newHeroNode.nickname;
106         } else {
107             System.out.printf("没有找到编号%d的节点,不能修改\n", newHeroNode.no);
108         }
109     }
110 
111     // 删除一个节点
112     // 对于双向链表,可以直接找到要删除的节点,
113     // 找到后,自我删除即可
114     public void del(int no) {
115 
116         // 判断是否为空
117         if (head.next == null) { // 空链表
118             System.out.println("链表为空,无法删除");
119             return;
120         }
121 
122         HeroNode2 temp = head.next; // 辅助变量
123         boolean flag = false; // 标志是否找到待删除节点
124         while (true) {
125             if (temp == null) { // 已经找到链表最后节点的 next
126                 break;
127             }
128             if (temp.no == no) {
129                 // 找到了待删除节点的前一个节点 temp
130                 flag = true;
131                 break;
132             }
133             temp = temp.next; // temp 后移
134         }
135         // 判断是否找到
136         if (flag) {
137             // 找到,可以删除
138             temp.pre.next = temp.next;
139             // 代码有问题?
140             // 如果是最后一个节点,就不需要执行下面这句话,否则出现空指针
141             if (temp.next != null) {
142                 temp.next.pre = temp.pre;
143             }
144         } else {
145             System.out.printf("要删除的%d节点不存在", no);
146         }
147     }
148 
149     // 第二种方式在添加英雄时,根据排名将英雄插入到指定位置
150     // 如果有这个排名,则添加失败,并给出提示
151     public void addByorder(HeroNode2 heroNode) {
152         // 因为头节点不能动,因此需要通过一个辅助指针(变量)来帮助找到添加的位置
153         // 因为单链表,因为找到的 temp 是位于添加位置的前一个节点,否则无法插入
154         HeroNode2 temp = head;
155         boolean flag = false; // 标志添加的编号是否存在,默认为false
156         while (true) {
157             if (temp.next == null) { // 说明 temp 以及在链表最后
158                 break;
159             }
160             if (temp.next.no > heroNode.no) { // 找到位置,就在temp的后面插入
161                 break;
162             } else if (temp.next.no == heroNode.no) {
163                 flag = true; // 编号存在
164                 break;
165             }
166             temp = temp.next; // 后移,遍历当前链表
167         }
168 
169         // 判断 flag 的值
170         if (flag) {
171             System.out.printf("准备插入的英雄的编号%d已经存在,不能加入\n", heroNode.no);
172         } else {
173             // 插入到链表中
174             heroNode.next = temp.next;
175             heroNode.pre = temp;
176             if(temp != null) { // 判断是否在链表尾
177                 temp.next = heroNode;
178                 temp.next.pre = heroNode;
179             }
180             
181         }
182 
183     }
184 
185     // 遍历双向链表的方法
186     public void list() {
187         // 判断链表是否为空
188         if (head.next == null) {
189             System.out.println("链表为空");
190             return;
191         }
192 
193         // 因为头节点不能动,需要辅助变量来遍历
194         HeroNode2 temp = head.next;
195         while (true) {
196             // 判断是否到链表最后
197             if (temp == null) {
198                 break;
199             }
200             // 输出节点信息
201             System.out.println(temp);
202             // 将 next 后移
203             temp = temp.next;
204         }
205     }
206 
207 }
208 
209 //定义 HeroNode2,每个 HeroNode 对象就是一个节点
210 class HeroNode2 {
211     public int no;
212     public String name;
213     public String nickname;
214     public HeroNode2 next; // 指向下一个节点,默认为 null
215     public HeroNode2 pre; // 执行上一个节点,默认为 null
216 
217     // 构造器
218     public HeroNode2(int no, String name, String nickname) {
219         this.no = no;
220         this.name = name;
221         this.nickname = nickname;
222     }
223 
224     @Override
225     public String toString() {
226         return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
227     }
228 
229 }

 

posted on 2019-09-22 09:02  格物致知_Tony  阅读(817)  评论(0编辑  收藏  举报