2.单链表

1.链表介绍

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

小结:

  • 链表是以节点的方式存储
  • 每个节点包含data域和next域(指向下一个节点)
  • 如图:发现每个节点并不是一定连续的
  • 链表分带头节点的链表和没有头节点的链表,根据实际情况而定

  

    

场景:创建一个链表

  链表新增:

package cn.com.linkedList;

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
        HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
        singleLinkedList.add(heroNode1);
        singleLinkedList.add(heroNode2);
        singleLinkedList.add(heroNode3);
        singleLinkedList.add(heroNode4);
        singleLinkedList.list();
    }
}

//管理英雄
class SingleLinkedList {
    //1.定义一个头元素,只是表明头元素
    HeroNode head = new HeroNode(0, "", "");

    /**
     * 添加
     *
     * @param node
     */
    public void add(HeroNode node) {
        //定义一个临时变量,因为head不能变化,所以定义一个中间变量
        HeroNode tmp = head;
        //找到队列的最后一个元素,最后一个元素的next=null
        while (tmp.getNext() != null) {
            tmp = tmp.getNext();
        }
        tmp.setNext(node);
    }

    /**
     * 遍历输出
     */
    public void list() {
        if (head.getNext() == null) {
            System.out.println("链表为空!");
            return;
        }
        HeroNode tmp = head.getNext();
        //找到队列的最后一个元素,最后一个元素的next=null
        while (true) {
            if (tmp == null) {
                break;
            }
            System.out.println(tmp.toString());
            tmp = tmp.getNext();
        }
    }
}

class HeroNode {
    //英雄编号
    private int no;
    //英雄名称
    private String name;
    //艺名
    private String nickName;
    //下一个
    private HeroNode next;

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public HeroNode getNext() {
        return next;
    }

    public void setNext(HeroNode next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

 

package cn.com.linkedList;

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
        HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
        singleLinkedList.add(heroNode1);
        singleLinkedList.add(heroNode2);
        singleLinkedList.add(heroNode3);
        singleLinkedList.add(heroNode4);
        singleLinkedList.list();
    }
}

//管理英雄
class SingleLinkedList {
    //1.定义一个头元素,只是表明头元素
    HeroNode head = new HeroNode(0, "", "");

    /**
     * 添加
     *
     * @param node
     */
    public void add(HeroNode node) {
        //定义一个临时变量,因为head不能变化,所以定义一个中间变量
        HeroNode tmp = head;
        //找到队列的最后一个元素,最后一个元素的next=null
        while (tmp.getNext() != null) {
            tmp = tmp.getNext();
        }
        tmp.setNext(node);
    }

    /**
     * 遍历输出
     */
    public void list() {
        if (head.getNext() == null) {
            System.out.println("链表为空!");
            return;
        }
        //head一定不为空,tmpp就是第一个有效数据
        HeroNode tmp = head.getNext();
        //找到队列的最后一个元素,最后一个元素的next=null
        while (true) {
            if (tmp == null) {
                break;
            }
            System.out.println(tmp.toString());
            tmp = tmp.getNext();
        }
        
        上述判断也可以这么写:
        HeroNode tmp = head;
        //找到队列的最后一个元素,最后一个元素的next=null
        while ((tmp=tmp.getNext())!=null) {
            System.out.println(tmp.toString());
        }
    }
}

class HeroNode {
    //英雄编号
    private int no;
    //英雄名称
    private String name;
    //艺名
    private String nickName;
    //下一个
    private HeroNode next;

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public HeroNode getNext() {
        return next;
    }

    public void setNext(HeroNode next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

输出:

HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}

问题:上面的英雄没有按照no排序的功能,例如:

  public static void main(String[] args) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
        HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
        singleLinkedList.add(heroNode1);
        //重点1:将英雄4放在英雄1后面添加
        singleLinkedList.add(heroNode4);
        singleLinkedList.add(heroNode2);
        singleLinkedList.add(heroNode3);
        singleLinkedList.list();
    }

输出:发现并没有按照no排名
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    HeroNode{no=4, name='林冲', nickName='豹子头'}
    HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
    HeroNode{no=3, name='吴用', nickName='智多星'}

第二种方式:在添加英雄是,根据no将英雄插入到指定位置,如果队列中已有这个no,则添加失败,给出提示

 

/**
     * 按照顺序添加
     */
    public void addByOrder(HeroNode node) {
        HeroNode temp=head;
        //遍历得到node需要插入的前一个节点
        while (true){
            if (temp.getNext()==null){
                break;
            }
            //必须找到插入处的上一个节点,这里必须用temp.getNext.getNo和no判断,不能用temp.getno和no判断,细品!!
            if (temp.getNext().getNo()==node.getNo()){
                System.out.println("列表中红已有该no的节点,no:"+node.getNo());
                return;
            }else if(temp.getNext().getNo()>node.getNo()){
                //找到了需要插入的节点,即当前tmp节点
                break;
            }
            temp=temp.getNext();
        }
        //1.必须先设置node的后一个节点,因为如果先设置temp.setNext,这里的node.setNext(temp.getNext)获取的就是自己
        node.setNext(temp.getNext());
        temp.setNext(node);
    }

测试:
          SingleLinkedList singleLinkedList = new SingleLinkedList();
        HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
        HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
        /*singleLinkedList.add(heroNode1);
        singleLinkedList.add(heroNode4);
        singleLinkedList.add(heroNode2);
        singleLinkedList.add(heroNode3);
        singleLinkedList.list();*/
        singleLinkedList.addByOrder(heroNode1);
        //重点1:此处先+4节点
        singleLinkedList.addByOrder(heroNode4);
        singleLinkedList.addByOrder(heroNode2);
        singleLinkedList.addByOrder(heroNode3);
        singleLinkedList.list();
发现输出:依旧按照no顺序输出
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=4, name='林冲', nickName='豹子头'}

单链表的修改

 /**
     * 修改
     *
     * @param node 新节点
     */
    public void update(HeroNode node) {
        HeroNode temp = head.getNext();
        //是否找到需要更新的节点
        boolean flag = true;
        while (true) {
            if (temp == null) {
                flag = false;
                break;
            }
            if (temp.getNo() == node.getNo()) {
                break;
            }
            temp = temp.getNext();
        }
        if (flag){
            //这里可以用直接setnext的当时去替换节点,但是替换下来的节点会是垃圾对象,需要考虑下垃圾回收问题,太多是否会造成oom
            temp.setName(node.getName());
            temp.setNickName(node.getNickName());
        }
    }
测试:
        singleLinkedList.addByOrder(heroNode1);
        singleLinkedList.addByOrder(heroNode4);
        singleLinkedList.addByOrder(heroNode2);
        singleLinkedList.addByOrder(heroNode3);
        singleLinkedList.list();
        HeroNode heroNode5 = new HeroNode(4, "林冲--", "豹子头--");
        System.out.println("-----------------------");
        singleLinkedList.update(heroNode5);
        singleLinkedList.list();

输出:发现已经更新成功
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=4, name='林冲', nickName='豹子头'}
    -----------------------
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=4, name='林冲--', nickName='豹子头--'}

单链表的删除,根据no删除具体的节点:

/**
     * 根据编号删除,难点是,因为节点没有获取上一个节点的方法,所以需要定位到需要删除节点的上一个节点处
     *
     * @param no
     */
    public void delete(int no) {
        HeroNode temp = head;
        //找到需要删除的上一个节点
        boolean flag = true;
        while (true) {
            if (temp.getNext() == null) {
                flag = false;
                break;
            }
            //tmp就是需要删除节点的上一个节点
            if (temp.getNext().getNo() == no) {
                break;
            }
            temp = temp.getNext();
        }
        if (flag) {
            temp.setNext(temp.getNext().getNext());
        }else{
            System.out.println("删除失败,链表中没有该节点:"+no);
        }
    }

测试:
         //删除节点
        System.out.println("删除节点后----------------");
        singleLinkedList.delete(2);
        singleLinkedList.list();
输出:发现2节点已被删除
    删除节点后----------------
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=4, name='林冲--', nickName='豹子头--'}

 

单链表面试

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

/**
     * 获取长度
     *
     * @return
     */
    public int getLength() {
        if (head.getNext() == null) {
            return 0;
        }
        HeroNode temp = head.getNext();
        int length = 0;
        while (temp!=null) {
            temp = temp.getNext();
            length++;
        }
        return length;
    }

2.查找单链表中倒数第k个节点

分析,先得到有效节点个数size,再正向遍历size-k

    /**
     * 获取长度
     *
     * @return
     */
    public int getLength() {
        if (head.getNext() == null) {
            return 0;
        }
        HeroNode temp = head.getNext();
        int length = 0;
        while (temp != null) {
            temp = temp.getNext();
            length++;
        }
        return length;
    }

    /**
     * 获得倒数第几个节点
     *
     * @param index 倒数第几个
     * @return
     */
    public HeroNode getLastIndexNode(int index) {
        //获取有效节点个数
        int size=getLength();
        if(size==0){
            System.out.println("链表为空");
            return null;
        }
        if (index<0||index>size){
            System.out.println("index输入有误");
            return null;
        }
        int count=1;
        HeroNode temp=head.getNext();
        while (count!=(size-(index-1))){
            temp=temp.getNext();
            count++;
        }
        return temp;
    }

测试:
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=4, name='林冲--', nickName='豹子头--'}
    ------------
    有效长度:3
    倒数第2个元素是:HeroNode{no=3, name='吴用', nickName='智多星'}

3.单链表的反转

public void reserve() {
        if (head.getNext() == null || head.getNext().getNext() == null) {
            System.out.println("列表为空,或者列表只有一个元素,不需要反转!");
        }
        //定义一个新的头部
        HeroNode newHead = new HeroNode(0, "", "");
        //当前节点
        HeroNode temp = head.getNext();
        //下一个节点
        HeroNode next=null;
        while (temp != null) {
            next=temp.getNext();
            temp.setNext(newHead.getNext());
            newHead.setNext(temp);
            temp=next;
        }
        head.setNext(newHead.getNext());
    }

输出:
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=4, name='林冲', nickName='豹子头'}
    反转------------
    HeroNode{no=4, name='林冲', nickName='豹子头'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
    HeroNode{no=1, name='宋江', nickName='及时雨'}

 4.从尾到头的打印链表

   /**
     * 反转打印
     */
    public void  resevePrint(){
        if (head.getNext()==null){
            System.out.println("链表为空!");
        }
        HeroNode temp=head.getNext();
        Stack<HeroNode> stack=new Stack<HeroNode>();
        while (temp!=null){
            stack.push(temp);
            temp=temp.getNext();
        }
        while (stack.size()>0){
            System.out.println(stack.pop());
        }
    }

测试输出:
    HeroNode{no=4, name='林冲', nickName='豹子头'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    反转打印-----------
    HeroNode{no=1, name='宋江', nickName='及时雨'}
    HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
    HeroNode{no=3, name='吴用', nickName='智多星'}
    HeroNode{no=4, name='林冲', nickName='豹子头'}

 

5.合并两个有序链表,合并以后依旧有序

预留!

 

posted @ 2022-11-14 22:16  努力的达子  阅读(27)  评论(0编辑  收藏  举报