zoukankan      html  css  js  c++  java
  • 双向链表

    双向链表

      管理单向链表的缺点分析

        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的节点,不能修改
    ", 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已经存在,不能加入
    ", 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 }
  • 相关阅读:
    关于ASP.NET MVC的业务逻辑验证(validation)
    AJAX的跨域与JSONP(另送一个为文章自动添加短址的功能)
    oXite源码学习导读二:Action的返回类型与IActionInvoker
    访问需要HTTP Basic Authentication认证的资源的各种语言的实现
    ASP.NET MVC 1.0 发布了
    简单学习下Oxite的项目结构2
    制作Visual Studio项目模板
    LumaQQ.NET For Visual Studio 2005
    深入ASP.NET数据绑定(下)——多样的绑定方式
    深入ASP.NET数据绑定(上)
  • 原文地址:https://www.cnblogs.com/niujifei/p/11565886.html
Copyright © 2011-2022 走看看