三、单链表

单链表

简单介绍:

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

实例分析

将水浒传中的英雄作为节点,关于单链表的增删查操作

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        //进行测试
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");

        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.addByOrder(hero4);
        singleLinkedList.addByOrder(hero2);
        singleLinkedList.addByOrder(hero1);
        singleLinkedList.addByOrder(hero3);
        singleLinkedList.list();

        System.out.println("删除一个");
        singleLinkedList.delete(1);
        singleLinkedList.list();
    }
}

//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode {
    public int no;
    public String name;
    public String nickName;
    public HeroNode next;//指向下一个节点

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

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

//定义一个SingleLinkedList 管理我们的英雄
class SingleLinkedList {
    //先初始化一个头节点,不存放任何数据
    private HeroNode head = new HeroNode(0, "", "");


    /*添加方法(不考虑编号的顺序)
     *思路:
     *  1、找到当前链表的最后节点
     *  2、将最后这个节点的next 指向 新的节点
     */
    public void add(HeroNode heroNode) {
        //定义一个辅助节点
        HeroNode temp = head;
        //遍历链表
        while (true) {
            //找到了最后一个节点
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        temp.next = heroNode;
    }


    /**
     * 添加方法(考虑编号的顺序)
     * 1、首先找到新节点添加的位置
     * 2、新的节点的next等于temp.next
     * 3、将temp.next等于新的节点
     */
    public void addByOrder(HeroNode heroNode) {
        HeroNode temp = head;
        //标识新添加的编号是否存在
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                //temp已经到链表的最后
                break;
            }
            if (temp.next.no > heroNode.no) {
                break;
            } else if (temp.next.no == heroNode.no) {
                //说明标号已经存在
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag) {
            throw new RuntimeException(heroNode.no + " 编号已经存在!");
        } else {
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    /*删除
     *  1、找到需要删除的这个节点的前一个节点
     *  2、temp.next = temp.next.next;
     */
    public void delete(int no) {
        HeroNode temp = head;
        boolean flag = false;//是否找到待删除节点
        while (true) {
            if (temp.next == null) {
                break;
            }
            if (temp.next.no == no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            temp.next = temp.next.next;
        } else {
            throw new RuntimeException("需要删除的节点不存在");
        }
    }

    /**
     * 遍历显示链表
     */
    public void list() {
        if (head.next == null) {
            throw new RuntimeException("链表为空!");
        }
        HeroNode temp = head.next;
        while (true) {
            if (temp == null) {
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }
    
    /**
     *
     * @param head 链表的头节点
     * @return 返回的就是有效节点的个数
     */
    public static int getLength(HeroNode head){
        if (head.next == null){
            return 0;
        }
        int length = 0;
        //定义一个辅助的变量
        HeroNode cur = head.next;
        while(cur!=null){
            length++;
            cur = cur.next;
        }
        return length;
    }
}

面试题

1、查找单链表中的倒数第k个节点

/**
 * 查找单链表中的倒数第k个节点
 * 思路:
 * 1、编写一个方法,接收head节点,同时接收一个index
 * 2、index 表示是倒数第index个节点
 * 3、先把链表从头到尾遍历,得到链表的总的长度
 * 4、从链表的第一个开始遍历(size-index)个
 * 5、如果找到了,则返回该节点,否则返回null
 */
public static HeroNode findLastIndexNode(HeroNode head,int index){
    if (head.next == null){
        return null;
    }
    int size = getLength(head);
    if (index<=0||index>size){
        throw new RuntimeException("传入参数范围误!");
    }
    HeroNode temp = head.next;
    for (int i = 0; i < size-index; i++) {
        temp = temp.next;
    }
    return temp;
}

2、单链表的反转

/**
 * 单链表的反转
 * 思路:
 *  1、先定义一个新的节点reverseHead
 *  2、从头到尾遍历原来的链表,每遍历一个节点,就将其取出来,并放在reverseHead的后面
 *  3、然后原来链表的head.next = reverseHead.next;
 */
public static void reverseList(HeroNode head){
    if (head.next == null || head.next.next == null){
        return ;
    }
    HeroNode cur = head.next;
    HeroNode next = null;//指向当前节点的下一个节点
    HeroNode reverseHead = new HeroNode(0,"","");
    //遍历原来的链表
    while (cur!=null){
        next = cur.next;
        cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的最前端
        reverseHead.next = cur;
        cur = next;
    }
    head.next = reverseHead.next;
}

3、逆序打印单链表

/**
 * 逆序打印单链表
 * 思路:使用栈的性质
 */
public static void reversePrint(HeroNode head){
    if (head.next == null){
        return;
    }
    HeroNode cur = head.next;
    Stack<HeroNode> stack = new Stack<HeroNode>();
    while (cur!=null){
        stack.push(cur);
        cur = cur.next;
    }
    while (stack.size()>0){
        System.out.println(stack.pop());
    }
}
posted @ 2019-12-05 19:49  ねぇ  阅读(291)  评论(0编辑  收藏  举报