数据结构(三)---链表1--单链表

单链表

手写单链表

(1)先写一个节点类HeroNode
有no、有name、有下一个节点next

//初始化一个单链表节点,定义一个HeroNode,每个HeroNode对象就是一个节点,这个节点里存放信息
class HeroNode {
    public int no;
    public String name;
    public String nickName;
    public HeroNode next;//指向下一个节点

    //构造器
    public HeroNode(int hNo, String hName, String hNickName) {
        //初始化一下
        this.no = hNo;
        this.name = hName;
        this.nickName = hNickName;
    }

    //为了显示方法,我们重新toString
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

(2)开始用HeroNode类来创建单链表
1-创建头结点,放在0的位置
2-getHead方法可以让外界获取这个头结点
3-添加节点的方法add:参数是一个要被添加的HeroNode的对象,辅助遍历的节点temp先指向头结点
4-遍历显示节点方法list
5-根据no修改节点方法update
6-删除节点的方法remove

//开始创建单链表,来管理上面的节点
class SingleLinkedList {
    //首先,写一个头结点,头结点不要动,头结点动了就找不到了
    private HeroNode head = new HeroNode(0, "", "");//只是用来存放头结点,不存放具体的数据

    //获取头结点
    public HeroNode getHead() {
        return head;
    }

    //(1)
    //然后,写添加的方法,方法操作的参数就是节点的对象
    //每添加一个节点,必须根据排名no找到排在前后的英雄节点,插在中间
    public void add(HeroNode heroNode) {
        //一个新节点来了,需要找位置,有几种情况:1-排在最前面,后面有前面没有,2-排在中间,前面有后面也有,3-排在最后,前面有后面没有
        //要想找节点位置,就要遍历链表,找出英雄的排名no,然后进行比较
        //因为头结点不能动,所以需要一个辅助遍历temp
        HeroNode temp = head;
        //标识添加的编号是否存在,默认为false不存在,如果存在了就不能再添加了
        boolean flag = false;
        //遍历链表,找到最后,最后一个节点的next为null
        while (true) {
            //先判断temp是不是最后一个节点
            if (temp.next == null) {
                //如果是最后一个,不管找没找到,都要break
                break;
            }
            //第一种情况,插在temp和temp.next中间
            if (temp.next.no > heroNode.no) {
                //就把新英雄节点放在temp前面,头结点后面
                // heroNode.next=temp.next;
                // head.next=heroNode;
                break;
            } else if (temp.next.no == heroNode.no) {
                flag = true;//说明编号已经存在
                break;
            }
            /*//第二种情况,当节点的排名小于,而它后面的排名大于,说明找到位置了
            if (temp.next.no < heroNode.no && temp.next.next.no>heroNode.no) {
                //就把新英雄节点放在中间
                heroNode.next=temp.next.next;
                temp.next.next=heroNode;
            }
            //第三种情况,要插入的英雄是最小的,
            if (temp.no<heroNode.no && temp.next==null)
            {
                temp.next=heroNode;
            }
            //第四种情况,遇到相同排名的了或者超出108了,就要提示出现错误了,并且终止循环
            if (temp!=null && temp.no==heroNode.no){
                System.out.println("该排名出现重复,请检查");
                break;
            }*/
            //如果当前位置不符合上面情况,就让这个temp后移遍历当前链表(这个temp就像一个游标,从head位置往后滑)
            temp = temp.next;
        }
        // 判断flag的值
        //在循环的时候不做操作,而是先取flag值,最后出循环,根据flag把相同的情况一起操作处理
        if (flag) {
            //如果flage为true,说明已经存在,不能添加
            System.out.printf("准备插入的英雄编号%d已经存在,不能重复添加\n", heroNode.no);
        } else {
            //如果flage为false,说明不存在,可以添加
            //只需要讨论两种情况,temp是最后一个,temp不是最后一个且插在中间
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    //(2)
    //然后,写一个显示方法显示所有节点,也是用到一个辅助游标节点来遍历
    public void list() {
        //先判断链表是否为空(也就是只有头结点)
        if (head.next == null) {
            System.out.println("单链表为空,没有数据");
            return;
        }
        //如果链表不为空,就开始遍历
        //因为头结点不能动,所以需要一个辅助游标变量和遍历,从头结点后面的第一个节点开始滑
        HeroNode temp = head.next;
        //
        while (true) {
            //判断是否到了链表最后
            if (temp == null) {
                //如果为空,说明到了最后一个节点,就停止下滑
                break;
            }
            //如果不为空,说明还没到最后,那就先输出节点信息,使用类里的toString来输出
            System.out.println(temp);
            //输出完以后再接着往下滑
            temp = temp.next;
        }
    }

    //(3)
    //根据排名的编号no,把已有的节点改成新的
    public void update(HeroNode newHeroNode) {
        //辅助变量
        HeroNode temp = head.next;
        //标识是否找到目标节点
        boolean flag = false;

        //先判断链表是不是空的
        if (temp == null) {
            //如果是空的,就返回空值
            System.out.println("链表为空,没有数据");
            return;
        }
        //如果不为空,那就根据no通过遍历找到要修改的节点
        //通过while循环和标识flag来实现,while循环只负责找,出了循环再根据flag修改
        while (true) {
            //先判断一种情况,找到链表最后了也没找到
            if (temp == null) {
                break;//循环结束
            }
            //通过判断no是否相等来找到目标节点
            if (temp.no == newHeroNode.no) {
                // 这里不能先操作,这里只负责找,找到了就修改flag,具体操作出了循环再说
                // temp.name=newHeroNode.name;
                // temp.nickName=newHeroNode.nickName;
                //找到了就把标识改成true
                flag = true;
                //既然找到了,那就结束循环
                break;
            }

            //如果当前节点不符合上面两种判断,那就后移一位,接着遍历
            temp = temp.next;
        }
        //当结束循环时,flag要么还为false,要么为true
        //如果flag为true,就说明找到了,就把新节点的信息赋值给目标节点
        if (flag = true) {
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
            System.out.println("修改完毕");
        } else {
            //如果没找到,那就提示说没找到
            System.out.printf("没有找到编号为%d的英雄节点\n", newHeroNode.no);
        }
    }

    //(4)
    //删除目标节点
    //先找到待删除节点前面一个节点temp,方法就是temp.next=temp.next.next
    public void delete(int no) {
        HeroNode temp = head;
        boolean flag = false;
        if (temp.next == null) {
            System.out.println("当前列表是空的,没有数据");
            return;
        }
        while (true) {
            if (temp.next == null) {
                System.out.println("已经找到最后了也没找到待删除的节点,结束");
                break;
            }
            if (temp.next.no == no) {
                System.out.printf("找到编号为%d待删除的节点了\n", temp.next.no);
                flag = true;
                break;
            }
            //当前节点也不是最后一个节点,也不是待删除节点,所以接着往下遍历
            temp = temp.next;
        }
        if (flag) {
            temp.next = temp.next.next;
            System.out.println("删除完毕");
        } else {
            System.out.printf("没有找到编号为%d的待删除节点\n", no);
        }
    }
}

(3)测试类

        HeroNode hero01 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero02 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero03 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero04 = new HeroNode(4, "林冲", "豹子头");

        //然后,把英雄对象添加到单链表里去
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.add(hero03);
        singleLinkedList.add(hero01);
        singleLinkedList.add(hero02);
        singleLinkedList.add(hero04);

几个练习题

1-求单链表中节点的个数(给一个头结点参数,找出后面链表的节点个数)

   /**(5)求单链表中节点的个数(给一个头结点参数,找出后面链表的节点个数)
     * @MethodName: getLength
     * @Author: AllenSun
     * @Date: 2019/10/31 20:34
     */
    public static int getLength(HeroNode head){
        if (head.next==null){
            return 0;
        }

        HeroNode temp=head.next;
        int length=0;
        while (temp!=null){
            length++;
            temp=temp.next;
        }
        return length;
    }

2-求单链表倒数第k个节点

    /**(6)求单链表倒数第k个节点
     * 1-接受head节点,同时接收一个index(倒数第index节点)
     * 2-先把链表从头到尾遍历,得到链表的总长度getLength
     * 3-得到size后,从链表第一个节点开始遍历,遍历(size-index)个节点,就得到了
     * 4-如果找到了,就返回这个节点,如果找不到,就返回空
     *
     * @MethodName:
     * @Author: AllenSun
     * @Date: 2019/10/31 0:49
     */

    public static HeroNode findLastIndexNode(HeroNode head, int index){
        //先判断是不是空链表
        if (head.next==null){
            System.out.println("链表为空,没有数据");
            return null;
        }
        //如果不为空,那就遍历链表,获取链表的长度
        int size=getLength(head);
        //知道了长度后,就可以根据节点index来找
        //1-索引小于0,说明索引错了
        if (index<=0 || index>size){
            System.out.println("索引错误,超出范围");
            return null;
        }
        //定义给辅助变量,for循环定位到倒数的index
        HeroNode cur=head.next;
        for (int i = 0; i < size-index; i++) {
            cur=cur.next;
        }
        return cur;
    }

3-单链表的反转

    public static void reversetList(HeroNode head){
        //如果链表是空的,或者只有一个节点,那么就不需要反转,直接返回就行了
        if (head.next==null || head.next.next==null){
            return;
        }
        //先定义一个辅助的指针,用来遍历原始的链表
        HeroNode cur=head.next;
        //定义一个next指针节点,指向当前节点cur的下一个节点
        HeroNode next=null;
        HeroNode reverseHead=new HeroNode(0,"","");

        //开始遍历原始链表,每遍历一个节点,就把这个节点取出来,放在新的链表reverseHead的最前端(也就是头结点后面)
        while (cur!=null){
            //如果为空,说明遍历到了最后一个节点
            //如果不为空,cur就接着往下遍历,
            next=cur.next;//先暂时保存当前节点cur的下一个节点next,因为后面需要使用
            cur.next=reverseHead.next;//把cur的下一个节点指向新的链表的最前端
            reverseHead.next=cur;
            cur=next;//让cur后移到next节点的位置
        }
        //把head.next指向reverseHead.next,实现单链表的反转
        head.next=reverseHead.next;
    }

4-从尾到头来打印单链表

    /**(8)从尾到头来打印单链表
     * 1-方式一:把单链表反转操作,然后遍历(改变了原来链表的结构,不推荐使用)
     * 2-方式二:可以使用栈,把各个节点压入到栈中,利用栈的先入后出特点,实现逆序打印的效果
     * @MethodName:
     * @Author: AllenSun
     * @Date: 2019/10/31 22:39
     */
    public static void reversePrint(HeroNode head){
        if (head.next==null){
            return;//空链表,不能打印
        }
        //创建一个栈,把各个节点压入栈
        Stack<HeroNode> stack=new Stack<HeroNode>();
        HeroNode cur=head.next;//cur节点用来遍历
        //把链表的所有节点利用while循环,挨个把节点压入栈
        while (cur!=null){
            stack.push(cur);//把当前不为空的节点压入栈
            cur=cur.next;//当前节点后移一位
        }
        //循环把节点全部压入栈后,就可以遍历栈来挨个打印
        while (stack.size()>0){
            System.out.println(stack.pop());
        }
    }

单链表插入节点–插在最后

    //然后,写添加的方法,方法操作的参数就是节点的对象
    //每添加一个节点,就直接加入到链表的最后位置
    public void add(HeroNode heroNode) {
        //一个新节点来了,首先要找到单链表里最后一个节点,然后让最后节点的next指向这个新节点
        //要想找到最后一个节点,就要遍历链表
        //因为头结点不能动,所以需要一个辅助遍历temp
        HeroNode temp = head;
        //遍历链表,找到最后,最后一个节点的next为null
        while (true) {
            //当节点的next为空时,说明这个就是最后一个节点
            if (temp.next == null) {
                break;
            }
            //如果没有找到,就让这个temp后移(这个temp就像一个游标,从head位置往后滑)
            temp = temp.next;
            //当退出while循环时,temp一定是指向最后一个节点的
        }
        //找到了最后一个节点,直接把新节点连上就行了
        temp.next = heroNode;
    }

单链表插入节点–根据排名插入

    //然后,写添加的方法,方法操作的参数就是节点的对象
    //每添加一个节点,必须根据排名no找到排在前后的英雄节点,插在中间
    public void add(HeroNode heroNode) {
        //一个新节点来了,需要找位置,有几种情况:1-排在最前面,后面有前面没有,2-排在中间,前面有后面也有,3-排在最后,前面有后面没有
        //要想找节点位置,就要遍历链表,找出英雄的排名no,然后进行比较
        //因为头结点不能动,所以需要一个辅助遍历temp
        HeroNode temp = head;
        //标识添加的编号是否存在,默认为false不存在,如果存在了就不能再添加了
        boolean flag=false;
        //遍历链表,找到最后,最后一个节点的next为null
        while (true) {
            //先判断temp是不是最后一个节点
            if (temp.next==null){
                //如果是最后一个,不管找没找到,都要break
                break;
            }
            //第一种情况,插在temp和temp.next中间
            if (temp.next.no>heroNode.no){
                //就把新英雄节点放在temp前面,头结点后面
                // heroNode.next=temp.next;
                // head.next=heroNode;
                break;
            }else if (temp.next.no==heroNode.no){
                flag=true;//说明编号已经存在
                break;
            }
            //如果当前位置不符合上面情况,就让这个temp后移遍历当前链表(这个temp就像一个游标,从head位置往后滑)
            temp = temp.next;
        }
        // 判断flag的值
        //在循环的时候不做操作,而是先取flag值,最后出循环,根据flag把相同的情况一起操作处理
        if (flag){
            //如果flage为true,说明已经存在,不能添加
            System.out.printf("准备插入的英雄编号%d已经存在,不能重复添加\n",heroNode.no);
        } else {
            //如果flage为false,说明不存在,可以添加
            //只需要讨论两种情况,temp是最后一个,temp不是最后一个且插在中间
            heroNode.next=temp.next;
            temp.next=heroNode;
        }
    }

单链表修改节点–根据排名no来修改

    //根据排名的编号no,把已有的节点改成新的
    public void update(HeroNode newHeroNode){
        //辅助变量
        HeroNode temp=head.next;
        //标识是否找到目标节点
        boolean flag=false;

        //先判断链表是不是空的
        if (temp==null){
            //如果是空的,就返回空值
            System.out.println("链表为空,没有数据");
            return;
        }
        //如果不为空,那就根据no通过遍历找到要修改的节点
        //通过while循环和标识flag来实现,while循环只负责找,出了循环再根据flag修改
        while (true){
            //先判断一种情况,找到链表最后了也没找到
            if (temp==null){
                break;//循环结束
            }
            //通过判断no是否相等来找到目标节点
            if (temp.no==newHeroNode.no){
                // 这里不能先操作,这里只负责找,找到了就修改flag,具体操作出了循环再说
                // temp.name=newHeroNode.name;
                // temp.nickName=newHeroNode.nickName;
                //找到了就把标识改成true
                flag=true;
                //既然找到了,那就结束循环
                break;
            }

            //如果当前节点不符合上面两种判断,那就后移一位,接着遍历
            temp=temp.next;
        }
        //当结束循环时,flag要么还为false,要么为true
        //如果flag为true,就说明找到了,就把新节点的信息赋值给目标节点
        if (flag=true){
            temp.name=newHeroNode.name;
            temp.nickName=newHeroNode.nickName;
            System.out.println("修改完毕");
        }else {
            //如果没找到,那就提示说没找到
            System.out.printf("没有找到编号为%d的英雄节点\n",newHeroNode.no);
        }
    }

发布了41 篇原创文章 · 获赞 5 · 访问量 653

猜你喜欢

转载自blog.csdn.net/weixin_44823875/article/details/104934038