【数据结构&算法】单链表理论与实现

前言

链式存储结构的特点

  • 通过指针域表示数据元素间的逻辑关系
  • 不需要连续的存储空间
  • 存储空间是动态的随时申请随时用
  • 和数组比较插入和删除不要移动数据元素

单链表简介

单链表是链表的其中一种也是最基础的数据结构,链表中的数据是用结点来表示的。
每个结点的构成:数据域+指针域(如图)
在这里插入图片描述
一个完整的链表:
在这里插入图片描述
头结点说明:
本文在链表的第一个结点前额外增加了一虚拟头结点,只是为了方便后面的操作,不是必须的。虚拟头数据域一般不放数据

代码实现

类结构

public class SinglyLinkedList<T> {
    private static class Node<T> {
        T data;
        Node<T> next;
        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }
        public Node(T data) {
            this(data, null);
        }
    }
    private Node<T> header;//链表头
    private int size = 0;//链表大小
    //构造虚拟头
    public SinglyLinkedList() {
        this.header = new Node<>(null, null);
    }
}

基本操作

插入指定位置
    /**
     * 找到index的前一个结点
     * @param index
     * @return
     */
    private Node<T> getNodeByIndex(int index) {
        if (index < 0 || index > this.size) {
            throw new IllegalArgumentException("index is error");
        }
        Node<T> node = this.header;
        for (int i = 0; node != null && i <= index - 1; i++) {
            node = node.next;
        }
        return node;
    }
    /**
     * 添加到链表的指定位置
     * @param data
     * @param index
     */
    public void insert(T data, int index) {
        //待插入结点的前驱结点
        Node<T> preNode = getNodeByIndex(index);
        Node<T> node = new Node<>(data);
        //当前结点
        Node<T> currNode = preNode.next;
        //新结点的后继结点指向当前结点
        node.next = currNode;
        //待插入结点的后继结点指向新结点
        preNode.next = node;
        size++;
    }
删除指定位置的结点
    /**
     * 删除指定位置的节点
     * @param index
     * @return
     */
    public boolean remove(int index) {
        //待删除结点的前驱结点
        Node<T> preNode = getNodeByIndex(index);
        if (preNode.next == null) {
            return true;
        }
        //当前结点
        Node<T> currNode = preNode.next;
        //当前结点后继结点
        preNode.next = currNode.next;
        size--;
        return true;
    }
获取指定位置的数据
    /**
     * 获取指定位置的数据
     * @param index
     * @return
     */
    public T get(int index) {
        //结点的前驱结点
        Node<T> preNode = getNodeByIndex(index);
        if (preNode.next == null) {
            return null;
        }
        return preNode.next.data;
    }
链表反转

单链表反转是面试时常考的题目,在这里重点讲解下

原链表:
在这里插入图片描述
反转后链表:
在这里插入图片描述

     /**
     * 链表反转
     */
    public void reverse() {
        Node<T> p = this.header.next;
        this.header.next = null;//虚拟表头指针域置为空
        Node<T> temp;//临时结点,保存
        while (p != null) {
            temp = p;
            p = p.next;
            temp.next = this.header.next;
            this.header.next = temp;
        }
    }
清空链表
    /**
     * 清空链表
     */
    public void clear() {
        Node temp = this.header;
        while (temp != null) {
            Node next = temp.next;
            temp.next = null;
            temp.data = null;
            temp = next;
        }
        this.size = 0;
    }

完整代码

public class SinglyLinkedList<T> {

    private static class Node<T> {
        T data;
        Node<T> next;
        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }
        public Node(T data) {
            this(data, null);
        }
    }
    private Node<T> header;//链表头
    private int size = 0;//链表大小
    //构造虚拟头
    public SinglyLinkedList() {
        this.header = new Node<>(null, null);
    }


    /**
     * 追加到链表尾部
     *
     * @param data
     */
    public void append(T data) {
        //待插入结点的前驱结点
        Node<T> preNode = getNodeByIndex(this.size);
        preNode.next = new Node(data);
        this.size++;
    }

    /**
     * 找到index的前一个结点
     * @param index
     * @return
     */
    private Node<T> getNodeByIndex(int index) {
        if (index < 0 || index > this.size) {
            throw new IllegalArgumentException("index is error");
        }
        Node<T> node = this.header;
        for (int i = 0; node != null && i <= index - 1; i++) {
            node = node.next;
        }
        return node;
    }
    /**
     * 添加到链表的指定位置
     * @param data
     * @param index
     */
    public void insert(T data, int index) {
        //待插入结点的前驱结点
        Node<T> preNode = getNodeByIndex(index);
        Node<T> node = new Node<>(data);
        //当前结点
        Node<T> currNode = preNode.next;
        //新结点的后继结点指向当前结点
        node.next = currNode;
        //待插入结点的后继结点指向新结点
        preNode.next = node;
        size++;
    }


    /**
     * 删除指定位置的结点
     * @param index
     * @return
     */
    public boolean remove(int index) {
        //待删除结点的前驱结点
        Node<T> preNode = getNodeByIndex(index);
        if (preNode.next == null) {
            return true;
        }
        //当前结点
        Node<T> currNode = preNode.next;
        //当前结点后继结点
        preNode.next = currNode.next;
        size--;
        return true;
    }

    /**
     * 获取指定位置的数据
     * @param index
     * @return
     */
    public T get(int index) {
        //结点的前驱结点
        Node<T> preNode = getNodeByIndex(index);
        if (preNode.next == null) {
            return null;
        }
        return preNode.next.data;
    }

    /**
     * 链表翻转
     */
    public void reverse() {
        Node<T> p = this.header.next;
        this.header.next = null;//虚拟表头置为空
        Node<T> temp;
        while (p != null) {
            temp = p;
            p = p.next;
            temp.next = this.header.next;
            this.header.next = temp;
        }
    }

    public void show() {
        Node<T> temp = this.header.next;
        if (temp == null) {
            System.out.println("空链表");
            return;
        }
        while (temp != null) {
            System.out.print(temp.data);
            temp = temp.next;
        }
        System.out.println("");
    }

    /**
     * 清空链表
     */
    public void clear() {
        Node temp = this.header;
        while (temp != null) {
            Node next = temp.next;
            temp.next = null;
            temp.data = null;
            temp = next;
        }
        this.size = 0;
    }

    public boolean isEmpty() {
        return this.size == 0;
    }
    /**
     * 链表大小
     *
     * @return
     */
    public int size() {
        return this.size;
    }
}

测试结果

 public static void main(String[] args) {
        SinglyLinkedList<String> sLinkedList = new SinglyLinkedList<>();
        sLinkedList.append("a");
        sLinkedList.append("b");
        sLinkedList.append("c");
        sLinkedList.append("e");
        
        System.out.println("打印链表:");
        sLinkedList.show();
        
        System.out.println("插入到指定位置:");
        //插入到指定位置
        sLinkedList.insert("d",3);
        sLinkedList.show();

        System.out.println("删除c:");
        sLinkedList.remove(2);
        sLinkedList.show();

        System.out.println("链表翻转:");
        sLinkedList.reverse();
        sLinkedList.show();

        System.out.println("清空链表:");
        sLinkedList.clear();
        sLinkedList.show();
    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值