java 单链表及面试题

1.链表(Linked List)介绍
链表是有序的列表,但是它在内存存储结构如下:
在这里插入图片描述
2.特点:

  • 链表是以节点的方式来存储,是链式存储
  • 每个节点包含 data 域, next 域:指向下一个节点.
  • 链表的各个节点不一定是连续存储.
  • 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

3.单链表介绍

单链表(带头结点) 逻辑结构示意图如下:
在这里插入图片描述
4.应用示例:

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

第一种方法在添加英雄时,直接添加到链表的尾部

思路分析

添加(创建)

  • 先创建一个head头节点,表示单链表的头
  • 在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

在这里插入图片描述
遍历

  • 通过一个辅助变量遍历,帮助遍历整个链表。

删除节点
在这里插入图片描述
链表数据结构实现:

class HeroNode{
    public int id; //唯一标志
    public String name; //名字
    public String nickName; //昵称

    HeroNode next; //下一个节点

    public HeroNode(int id, String name, String nickName) {
        this.id = id;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "HeroNode{id: "+ id+ ",name: "+ name +", nickName: "+ nickName +"}";
    }
}

辅助管理链表类:

需要注意,链表维护的表头节点head 不能动,因此处理时需要一个temp辅助节点找到待删除节点的前一个节点

扫描二维码关注公众号,回复: 9663171 查看本文章
//链表类
class SingleLinkedList{
   //声明一个头结点
   private HeroNode head = new HeroNode(0,"","") ;

   /**
    * @description: 返回头结点
    * @param
    * @return:
    * @author: sukang
    * @date: 2020/1/2 14:28
    */
   public HeroNode getHead(){
       return head;
   }

   /**
    * @description: 从链表尾处添加节点
    * @param heroNode
    * @return: void
    * @author: sukang
    * @date: 2019/12/30 14:18
    */
   public void addHeroNode(HeroNode heroNode){
      /* 思路:用一个辅助节点遍历链表,找到节点next为空及最后一个节点,
       将其next赋给新节点*/
        //申明一个过渡节点,因为head节点不能后移
       HeroNode temp = head;
       while (true){
           //如果下一个节点为空的话,那么就在这个节点上添加新节点
           if(temp.next == null){
               break;
           }

           //如果没有到最后一个节点,那么节点往后移
           temp = temp.next;
       }

       //把最后一个节点的下一个节点赋给新节点
       temp.next = heroNode;
   }

   /**
    * @description: 按照节点的顺序添加节点
    * @param heroNode
    * @return: void
    * @author: sukang
    * @date: 2019/12/30 14:31
    */
   public void addHeroNodeByOrder(HeroNode heroNode){
      /* 思路:同样因为head节点不能移动,所以我们也是先声明一个辅助节点temp,
       那么辅助节点temp就应该为插入节点的上一个节点,另外如果链表中存在相同
       大小的节点,那么将插入失败*/
      //声明一个辅助节点
       HeroNode temp = head;
       while (true){
           //链表已经遍历到最后
           if(temp.next == null){
               break;
           }

           if(temp.next.id == heroNode.id){
               System.out.printf("编号为%d的节点已经存在,不能添加", heroNode.id);
               return;
           }else if(temp.next.id > heroNode.id){
               break;
           }

           temp = temp.next;
       }

       heroNode.next = temp.next;
       temp.next = heroNode;
   }

   /**
    * @description: 删除节点
    * @param id
    * @return: void
    * @author: sukang
    * @date: 2019/12/30 15:19
    */
   public void delHeroNode(int id){
        /*思路:同样因为head节点不能移动,所以我们也是先声明一个辅助节点temp,
        那么如果辅助节点的下一个节点的id存在,那么就删除这个节点*/
       HeroNode temp = head;
       boolean flag = false;//是否找到删除节点标志
       while (true){
           //链表已经遍历到最后
           if(temp.next == null){
               break;
           }

           if(temp.next.id == id){
               flag = true;
               break;
           }

           temp = temp.next;
       }

       if(flag){
           temp.next = temp.next.next;
       }else{
           System.out.printf("要删除的节点%d,不存在", id);
       }
   }

   /**
    * @description: 更新节点
    * @param heroNode
    * @return: void
    * @author: sukang
    * @date: 2019/12/30 16:04
    */
   public void updateHeroNode(HeroNode heroNode){
       //思路:同样是通过辅助节点temp遍历找到和更新节点相同id的节点,然后进行更新
       //声明辅助节点
       HeroNode temp = head;
       boolean flag = false; //判断是否找到更新的节点,默认为false
       while (true){
           if(temp == null){
               break;
           }

           if(temp.next.id == heroNode.id){
               flag = true;
               break;
           }

           temp = temp.next;
       }

       if(flag){
           temp.next.name = heroNode.name;
           temp.next.nickName = heroNode.nickName;
       }else{
           System.out.printf("没有找到编号为 %d 的节点, 不能更新", heroNode.id);
       }
   }

   /**
    * @description: 遍历链表
    * @param
    * @return: void
    * @author: sukang
    * @date: 2019/12/30 16:15
    */
   public void list(){
       HeroNode temp = head;
       if(temp.next == null){
           System.out.println("链表为空!");
           return;
       }

       while (true){
           if(temp.next == null){
               break;
           }

           System.out.println(temp.next);

           temp = temp.next;
       }
   }
}

【面试题】单链表的反转
在这里插入图片描述
在这里插入图片描述
思路:

  • 先定义一个节点reverseHead = new HeroNode();
  • 从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
  • 原来的链表的head.next = reverseHead.net

代码实现

 /**
     * @description: 面试题链表反转
     * @param
     * @return: void
     * @author: sukang
     * @date: 2020/1/3 8:37
     */
    public static void linkedReversal(HeroNode head){
        //思路:首先申明一个新链表的头节点,然后遍历旧链表,按顺序插入新链表头节点之后
        if(head.next == null || head.next.next == null){
            System.out.println("链表为空或则链表的有效长度为1,不需要进行反转");
            return;
        }

        //声明一个辅助节点,遍历单链表来用
        HeroNode temp = head.next;
        //声明一个当前节点指向temp节点
        HeroNode cur = null;
        //声明新链表的头节点
        HeroNode newHead = new HeroNode(0,"","");
        while (true){
            //链表遍历到了最后一个节点
            if(temp == null){
                break;
            }

            cur = temp; //将当前节点指向单链表的辅助节点
            temp = temp.next; //将辅助节点后移一个节点
            cur.next = newHead.next;//然后将当前节点的后一个节点指向新链表的后一个节点
            newHead.next = cur;//新链表的头结点的后一个节点指向当前节点
        }

        head.next = newHead.next;
    }

【面试题】从尾到头打印单链表(要求方式1:反向遍历,方式2:Stack栈)
在这里插入图片描述
思路:

  • 方式1:先将单链表进行反转操作,然后再遍历即可,这样做的问题是会破坏原来的单链表结构,不建议
  • 方式2:可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果。

方式2代码实现

 /**
     * @description: 面试题:单链表反向打印
     * @param head
     * @return: void
     * @author: sukang
     * @date: 2020/1/3 9:38
     */
    public static void linkedReversalPrint(HeroNode head){
        //思路:将单链表压入栈中,然后从栈中取出打印,利用了栈的新进后出的特性
        if(head.next == null){
            return;
        }

        Stack<HeroNode> stack = new Stack<>();
        HeroNode temp = head;
        while (true){
            if(temp.next == null){
                break;
            }
            stack.add(temp.next);
            temp = temp.next;
        }

        while (stack.size() > 0){
            System.out.println(stack.pop());
        }
    }
发布了78 篇原创文章 · 获赞 32 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/suchahaerkang/article/details/103815470