数据结构-单链表

数据结构-单链表

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

 

 

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

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

3) 如图:发现链表的各个节点不一定是连续存储.

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

 

 

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

 

 

单链表的应用实例 

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

(1)结构

public class HeroNode {
    Integer id;
    String heroName;
    String nikeName;
    HeroNode next;
}

 

节点信息

public class HeroNode {
    public int id;
    public String heroName;
    public String nikeName;
    public HeroNode next;

    public HeroNode() {
    }

    public HeroNode(int id, String heroName, String nikeName) {
        this.id = id;
        this.heroName = heroName;
        this.nikeName = nikeName;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "id=" + id +
                ", heroName='" + heroName + '\'' +
                ", nikeName='" + nikeName + '\'' +
                '}';
    }
}

 

单链表

public class HeroLink {
    //单链表的头节点,头节点不要动,不存放具体的数据
    HeroNode head = new HeroNode(0,null,null);
}

 

一、增

①不考虑顺序,直接在链表最后插入

 

 

②考虑顺序

 

二、删

三、改

四、查

五、面试题

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

2) 查找单链表中的倒数第 k 个结点 【新浪面试题】 

3) 单链表的反转【腾讯面试题,有点难度】 

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

5) 合并两个有序的单链表,合并之后的链表依然有序【课后练习.】

 

源码

public class HeroLink {
    //先初始化一个头节点, 头节点不要动, 不存放具体的数据
    HeroNode head = new HeroNode(0,null,null);

    public HeroNode getHead() {
        return head;
    }

    /**
     * 添加节点信息,第一种:不考虑编号顺序时,直接添加到链尾
     * //1. 找到当前链表的最后节点
     * //2. 将最后这个节点的 next 指向 新的节点
     * @param node
     */
    public void addHeroNode(HeroNode node) {
        //因为head不能动,所以需要一个辅助节点变量
        HeroNode temp = head;
        //标记待添加的节点是否已经存在
        boolean flag = false;
        //遍历链表,找到最后
        while (true) {
            //如果节点的next为空说明该节点就是最后一个节点
            if (temp.next == null) {
                break;
            }
            if (temp.next.id == node.id) {
                flag = true;
                break;
            }
            //否则指针下移
            temp = temp.next;
        }

        if (flag) {
            System.out.println("添加失败,需要添加id为"+node.id+"的节点已经存在了!!!");
            return;
        }
        //加入新节点
        temp.next = node;
    }

    /**
     * 添加节点信息,第二种:根据排名将英雄插入到指定位置,按照id从小到大排序
     */
    public void addHeroNodeOrderById(HeroNode node) {
        //如果还没有任何一个节点,直接插入到head后面
        if (head.next == null) {
            head.next = node;
            return;
        }
        //因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
        HeroNode temp = head;
        //标记待添加的节点是否已经存在
        boolean flag = false;
        // 因为单链表,因为我们找的 temp 是位于 添加位置的前一个节点,否则插入不了
        while (true) {
            //说明 temp 已经在链表的最后
            if (temp.next == null) {
                break;
            }
            //节点已经存在
            if (temp.next.id == node.id) {
                flag = true;
                break;
            }
            //位置找到,就在 temp 的后面插入
            if (temp.next.id > node.id) {
                break;
            }
            temp = temp.next;
        }

        if (flag) {
            System.out.println("添加失败,需要添加id为" + node.id + "的节点已经存在了!!!");
        }
        else {
            node.next = temp.next;
            temp.next = node;
        }

    }


    /**
     * 删除节点
     * //1.head 不能动,因此我们需要一个 temp 辅助节点找到待删除节点的前一个节点
     * //2. 说明我们在比较时,是 temp.next.id 和 需要删除的节点的 id 比较
     */
    public void deleteHeroNodeById(int id)
    {
        if(head.next==null)
        {
            System.out.println("链表为空");
            return;
        }
        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.println("链表中没有id为"+id+"的节点数据");
        }
    }

    /**
     * 修改链表节点数据,id不要动
     */
    public void updateHeroNode(HeroNode node)
    {
        if(head.next==null)
        {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp=head.next;
        boolean flag=false;
        while (true)
        {
            if(temp==null)
            {
                break;
            }
            if(temp.id==node.id)
            {
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if (flag)
        {
            temp.heroName=node.heroName;
            temp.nikeName=node.nikeName;
        }
        else
        {
            System.out.println("id为"+node.id+"的节点不存在!!!");
        }
    }

    /**
     * 查询节点信息
     */
    public HeroNode getHeroNodeById(int id)
    {
        if(head.next==null)
        {
            return null;
        }
        HeroNode temp=head.next;
        boolean flag=false;
        while (true)
        {
            if(temp==null)
            {
                break;
            }
            if(temp.id==id)
            {
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if(flag)
        {
            return temp;
        }
        return null;
    }

    /**
     * 打印输出链表信息
     */
    public static void showHeroLink(HeroLink link) {
        if (link.head.next == null) {
            System.out.println("链表为空!!!");
            return;
        }

        HeroNode temp = link.head.next;
        while (temp != null) {
            System.out.println(temp);
            temp = temp.next;
        }
    }

    /**
     * 试题1:求单链表中有效节点的个数
     */
    public static int getSize(HeroLink link)
    {
        HeroNode head=link.getHead();
        if(head.next==null)
        {
            return 0;
        }
        int count=0;
        HeroNode temp=head.next;
        while (temp!=null)
        {
            count++;
            temp=temp.next;
        }
        return count;
    }

    /**
     * 试题2:查找单链表中的倒数第 k 个结点 【新浪面试题】
     */
    public static HeroNode getHeroNodeByIndex(HeroLink link,int index)
    {
        HeroNode head=link.getHead();
        if(head.next==null)
        {
            return null;
        }

        int size=0;
        HeroNode temp=head.next;
        while (temp!=null)
        {
            size++;
            temp=temp.next;
        }
        if(index<=0||index>size)
        {
            return null;
        }

        temp=head.next;
        for(int i=0;i<size-index;i++)
        {
            temp=temp.next;
        }
        return temp;
    }

    /**
     *试题3:单链表的反转【腾讯面试题,有点难度】
     */
    public static void reversetList(HeroLink link)
    {
        HeroNode head=link.getHead();
        //如果当前链表为空,或者只有一个节点,无需反转,直接返回
        if (head.next==null || head.next.next==null)
        {
            return;
        }

        HeroNode newHead=new HeroNode(0,null,null);
        //定义一个辅助的指针(变量),帮助我们遍历原来的链表
        HeroNode temp=head.next;
        // 指向当前节点[temp]的下一个节点
        HeroNode next=null;
        //遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表 newHead 的最前端
        while (temp != null) {
            //先暂时保存当前节点的下一个节点,因为后面需要使用
            next = temp.next;
            //将 temp 的下一个节点指向新的链表的最前端
            temp.next = newHead.next;
            //将 temp 连接到新的链表上
            newHead.next = temp;
            //让 temp 后移
            temp = next;
        }
        //将 head.next 指向 newHead.next, 实现单链表的反转
        head.next=newHead.next;

    }


    /**
     *  试题4、从尾到头打印单链表 【百度,要求方式 1:反向遍历 。 方式 2:Stack 栈】
     *  反向遍历
     */
    public static  void printHeroNode2(HeroLink link)
    {
        reversetList(link);
        showHeroLink(link);
        reversetList(link);
    }

    /**
     * 试题4、从尾到头打印单链表 【百度,要求方式 1:反向遍历 。 方式 2:Stack 栈】
     * 可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果
     */
    public static  void printHeroNode(HeroLink link)
    {
        HeroNode head=link.head;
        if(head.next==null)
        {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp=head.next;
        //创建一个栈,将各个节点压入栈
        Stack<HeroNode> stack=new Stack<>();
        while (temp!=null)
        {
            stack.push(temp);
            temp=temp.next;
        }
        //将栈中的节点进行打印,pop 出栈
        while (stack.size()>0)
        {
            System.out.println(stack.pop());
        }
    }

    /**
     * 试题5、合并两个有序的单链表,合并之后的链表依然有序
     */
    public static HeroLink HeroLink1AndHeroLink2(HeroLink link1,HeroLink link2)
    {
        //如果有一个为空则把另一个传出去
        if(link1.head.next==null)
        {
            return link2;
        }
        if(link2.head.next==null)
        {
            return link1;
        }
        //两个都不为空
        //辅助节点变量temp
        HeroNode temp=link2.head.next;
        //辅助节点next,避免插入temp后,找不到下一个节点
        HeroNode next=null;
        while (temp!=null)
        {
            next=temp.next;
            //把链表2的节点依次插入到链表1中
            link1.addHeroNodeOrderById(temp);
            temp=next;
        }
        //返回链表1
        return link1;
    }

}

 

posted @ 2020-10-19 23:33  orz江小鱼  阅读(148)  评论(0编辑  收藏  举报