Java 单链表

链表基本介绍

链表在内存中的实际存储结构

Java 单链表_第1张图片

链表的逻辑结构 

Java 单链表_第2张图片

 单链表应用实例

Java 单链表_第3张图片

代码实现

// 英雄节点,存储了英雄的信息
class HeroNode {
    public int id; // 英雄编号
    public String name; // 英雄名字
    public String nickName; // 英雄昵称
    public HeroNode next; // 指针域,指向下一个节点

    public HeroNode(int id, String name, String nickName) {
        this.id = id;
        this.name = name;
        this.nickName = nickName;
        this.next = null; // 初始指针指向 null
    }

    // 重写 toString() ,方便遍历链表的操作
    public String toString() {
        return "[" + id + " " + name + " " + nickName + "]";
    }
}

// 单链表
class SingleLinkedList {
    // 创建一个头结点并初始化,头结点用于指向一个链表,不存储数据
    HeroNode head = new HeroNode(0, "", "");

    // 向链表添加节点
    // 直接把要插入的节点放在链表的末尾
    public void add(HeroNode node) {
        // 定义一个临时变量指向链表的头结点
        HeroNode t = head;
        // 找到链表的最后一个节点的位置
        while (true) {
            // 当节点 t 的 next 为 null ,表示 t 所在位置是最后一个节点
            if (t.next == null) {
                break;
            }
            // 否则还不是最后一个节点,继续后移指向下一个节点
            t = t.next;
        }
        // 退出循环表示临时变量指向的是链表的最后一个节点
        // 在链表的末尾插入新添加的元素
        // 让最后一个节点的 next 指向新添加的节点,即可实现插入链表
        t.next = node;
    }

    // 向链表添加节点
    // 按照节点编号从小到大插入链表,即编号小的在链表前面,大的在链表后面
    public void addById(HeroNode node) {
        // 定义一个临时变量指向链表的头结点
        HeroNode t = head;
        // 找到比新节点ID值大的节点的位置
        while (true) {
            // 如果节点的 ID 值相等,表示已经存在节点,给出提示并返回
            if (t.next != null && node.id == t.next.id) {
                System.out.println("链表已经存在该节点" + node);
                return;
            }
            // 当节点 t 的 next 为 null ,表示 t 所在位置是最后一个节点
            // 找到了比新节点 ID 值大的节点或到达链表末尾
            if (t.next == null || node.id < t.next.id) {
                break;
            }
            // 没有找到合适位置的节点,继续后移指向下一个节点
            t = t.next;
        }
        // 存储 t 节点的下一个节点
        HeroNode p = t.next;
        // 让 t 节点的 next 指向新节点
        t.next = node;
        // 让新节点的 next 指向 p 节点
        node.next = p;
    }

    // 修改节点信息
    // 以节点的 id 值为查找依据,所以 id 值不能修改
    public void update(HeroNode newNode) {
        HeroNode t = head.next;
        // 首先判断列表是否为空
        if (t == null) {
            System.out.println("链表为空。。。");
            return;
        }

        // 遍历链表查找 id 值和 newNode 的 id 值相同的节点
        boolean flag = false;
        while (true) {
            if (t == null) {
                break;
            } else if (t.id == newNode.id) { // 找到了要修改的节点
                flag = true;
                break;
            }
            t = t.next;
        }

        if (flag) { // 修改对应的节点信息
            t.name = newNode.name;
            t.nickName = newNode.nickName;
        } else {
            System.out.println("链表没有节点" + newNode);
        }
    }

    // 删除节点
    public void deleteHeroNode(int id) {
        HeroNode t = head;
        boolean flag = false;
        while (t.next != null) {
            if (t.next.id == id) { // 找到要删除节点(t.next)的前一个节点 t
                flag = true;
                break;
            }
            t = t.next;
        }

        if (flag) {
            // 让要删除节点的前一个节点 t 的 next 指向要删除节点的后一个节点
            t.next = t.next.next;
        } else {
            System.out.println("链表中没有节点 id 为:" + id);
        }
    }

    // 打印链表中的节点信息,即遍历链表
    public void showList() {
        // 从第一个数据节点开始
        HeroNode t = head.next;

        // 判断链表是否为空
        if (t == null) {
            System.out.println("链表为空...");
            return;
        }

        while (t != null) {
            if (t.next != null) {
                System.out.print(t + " -> ");
            } else {
                System.out.println(t);
            }

            t = t.next;
        }
    }

}

// 单链表测试类
class SingleLinkedListTest {
    public static void main(String[] args) {
        // 创建英雄节点
        HeroNode node1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode node2 = new HeroNode(2, "吴用", "及时雨");
        HeroNode node3 = new HeroNode(3, "卢俊义", "玉麒麟");
        HeroNode node4 = new HeroNode(4, "林冲", "豹子头");

        // 创建一个链表,把上述几个节点加入链表
        SingleLinkedList linkedList = new SingleLinkedList();
        linkedList.add(node1);
        linkedList.add(node2);
        linkedList.add(node3);
        linkedList.add(node4);

        // 遍历链表
        linkedList.showList();

        // 创建一个链表,测试按编号顺序插入节点的方法
        SingleLinkedList linkedList2 = new SingleLinkedList();
        linkedList2.addById(node4);
        linkedList2.addById(node1);
        linkedList2.addById(node3);
        linkedList2.addById(node2);
        linkedList2.addById(node3);
        linkedList2.showList();

        // 测试修改节点的代码
        HeroNode newNode = new HeroNode(3, "大红", "小红红");
        linkedList2.update(newNode);
        linkedList2.showList();

        // 测试修改节点的代码
        linkedList2.deleteHeroNode(6);
        linkedList2.deleteHeroNode(1);
        linkedList2.deleteHeroNode(2);
        linkedList2.deleteHeroNode(3);
        linkedList2.deleteHeroNode(4);
        linkedList2.deleteHeroNode(5);
        linkedList2.showList();
    }
}

单链表面试题

Java 单链表_第4张图片

 问题一代码如下:

// 英雄节点,存储了英雄的信息
class HeroNode {
    public int id; // 英雄编号
    public String name; // 英雄名字
    public String nickName; // 英雄昵称
    public HeroNode next; // 指针域,指向下一个节点

    public HeroNode(int id, String name, String nickName) {
        this.id = id;
        this.name = name;
        this.nickName = nickName;
        this.next = null; // 初始指针指向 null
    }

    // 重写 toString() ,方便遍历链表的操作
    public String toString() {
        return "[" + id + " " + name + " " + nickName + "]";
    }
}

// 单链表
class SingleLinkedList {
    // 创建一个头结点并初始化,头结点用于指向一个链表,不存储数据
    HeroNode head = new HeroNode(0, "", "");

    // 向链表添加节点
    // 直接把要插入的节点放在链表的末尾
    public void add(HeroNode node) {
        // 定义一个临时变量指向链表的头结点
        HeroNode t = head;
        // 找到链表的最后一个节点的位置
        while (true) {
            // 当节点 t 的 next 为 null ,表示 t 所在位置是最后一个节点
            if (t.next == null) {
                break;
            }
            // 否则还不是最后一个节点,继续后移指向下一个节点
            t = t.next;
        }
        // 退出循环表示临时变量指向的是链表的最后一个节点
        // 在链表的末尾插入新添加的元素
        // 让最后一个节点的 next 指向新添加的节点,即可实现插入链表
        t.next = node;
    }

    // 打印链表中的节点信息,即遍历链表
    public void showList() {
        // 从第一个数据节点开始
        HeroNode t = head.next;

        // 判断链表是否为空
        if (t == null) {
            System.out.println("链表为空...");
            return;
        }

        while (t != null) {
            if (t.next != null) {
                System.out.print(t + " -> ");
            } else {
                System.out.println(t);
            }

            t = t.next;
        }
    }

    // 获取链表的有效节点数量,即如果有头结点,头结点不算有效节点数量
    public int getLength() {
        // 如果链表为空,返回 0
        if (head.next == null) {
            return 0;
        }

        int length = 1;
        HeroNode t = head.next;
        while (t.next != null) {
            length++;
            t = t.next;
        }

        return length;
    }

    // 获取链表倒数第 k 个节点
    // 先调用 getLength() 方法得到链表总长度 len
    // 然后从第一个有效数据节点开始遍历,遍历到第 (len - k) 个节点,就是所要求的那个
    public HeroNode findLastIndexNode(int k) {
        HeroNode t = head.next;

        if (t == null) {
            // 链表为空,则找不到节点,返回空
            return null;
        }
        // 获取链表的长度(有效数据的个数)
        int len = getLength();
        int count = 1;
        int flag = len - k;
        if (flag > 0) {
            while (count == flag) {
                t = t.next;
                count++;
            }
            // 使用 for 循环实现
            // for (int i = 0;i < flag; i++) {
            // t = t.next;
            // }
        } else {
            // 超出了链表的范围
            return null;
        }
        return t;
    }

    // 单链表的反转
    public void reverseLinkedList() {
        // 如果链表为空或只有一个节点,则不用反转
        if (head.next == null || head.next.next == null) {
            return;
        }
        // 创建一个新的头节点
        HeroNode newHead = new HeroNode(0, "", "");
        // 从头遍历链表,将链表中的每一个节点取出,依次将其插入新头节点的后面,其他数据节点的前面,即头插法
        // 指向当前节点
        HeroNode cur = head.next;
        // 指向当前节点的下一个节点
        HeroNode next = null;
        while (cur != null) {
            // 保存链表的下一个节点
            next = cur.next;
            // cur 是当前遍历链表取出来的节点
            // 让当前节点指向新头结点指向的第一个节点
            cur.next = newHead.next;
            // 再让头结点指向当前节点
            newHead.next = cur;
            // 遍历链表的下一个节点
            cur = next;
        }

        head = newHead;
    }

    // 反向遍历输出链表
    // 方式一:先将链表反转再输出,但是这样会破坏链表原本的结构顺序
    // 方式二:用栈先进后出的特点实现
    public void reversePrint1() {
        // 用数组模拟栈
        // 获取链表的长度(有效数据的个数)
        int len = getLength();
        if (len == 0) {
            System.out.println("链表为空...");
            return;
        }
        HeroNode[] arr = new HeroNode[len];
        HeroNode cur = head.next;
        int i = 0;
        while (cur != null) {
            arr[i++] = cur;
            cur = cur.next;
        }

        // 反向输出链表
        for (i = len - 1; i >= 0; i--) {
            System.out.println(arr[i]);
        }
    }

    public void reversePrint2() {
        // 用栈结构实现
        Stack stack = new Stack();
        HeroNode cur = head.next;
        while (cur != null) {
            stack.push(cur); // 入栈
            cur = cur.next;
        }

        // 反向输出链表,即输出栈元素
        while (stack.size() > 0) {
            System.out.println(stack.pop());
        }
    }
}

// 单链表测试类
class SingleLinkedListTest {
    public static void main(String[] args) {
        // 创建英雄节点
        HeroNode node1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode node2 = new HeroNode(2, "吴用", "及时雨");
        HeroNode node3 = new HeroNode(3, "卢俊义", "玉麒麟");
        HeroNode node4 = new HeroNode(4, "林冲", "豹子头");

        // 创建一个链表,把上述几个节点加入链表
        SingleLinkedList linkedList = new SingleLinkedList();
        linkedList.add(node1);
        linkedList.add(node2);
        linkedList.add(node3);
        linkedList.add(node4);

        // 遍历链表
        linkedList.showList();

        // 测试获取链表有效数据个数代码
        System.out.println("链表的长度为:" + linkedList.getLength());
        // 测试链表倒数第 k 个节点代码
        System.out.println("链表倒数第k个节点为:" + linkedList.findLastIndexNode(2));
        linkedList.showList();

        // 测试链表反转的代码
        linkedList.reverseLinkedList();
        linkedList.showList();

        // 测试反向输出链表的代码
        System.out.println("反向输出链表1:");
        linkedList.reversePrint1();
        System.out.println("反向输出链表2:");
        linkedList.reversePrint2();
    }
}

你可能感兴趣的:(数据结构和算法,java,开发语言)