数据结构-链表

1.概念

在前面的博客数据结构-数组中,可以看到数组虽然查询比较快,但是插入和删除元素的效率并不高,而且创建一个数组之后不能修改其长度,有什么数据结构可以解决数组的这些缺点吗?有,这个就是本篇需要介绍的数据结构-链表,不过它的查询效率并不高,在后面我们会介绍。

链表也是一种常见的数据结构,它不是线性存储数据,而是通过前一个元素保存后一元素的引用从而形成一条链。不过链表可以分为多种类型链表,常见的有:单向链表,双向链表等

2. 单向链表

单向链表

代码实现:

public class SingleLinkedList {
    private Node head; // 头部节点
    private Node tail; // 尾部节点(非链表最后的null)
    private int size; // 链表长度


    /**
     * 节点
     */
    public static class Node {
        private Object data;
        private Node next;

        Node(Object data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return data == null ? null : data.toString();
        }
    }

    public void add(Object o) {
        // 没有元素
        if (head == null) {
            head = new Node(o);
            tail = head;
            head.next = null;
        } else {
            Node cur = new Node(o);
            // 原来的尾节点next指向新节点
            tail.next = cur;
            // 新节点变成现在的尾节点
            tail = cur;
            // 将新尾节点的next置为null
            tail.next = null;
        }
        size++;
    }

    public Node find(Object o) {
        if (head == null) {
            return null;
        }
        Node cur = head;
        while (cur != null) {
            // 判断null
            if (cur.data == null) {
                if (o == null) {
                    return cur;
                }
            } else if (cur.data.equals(o)) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    public boolean delete(Object o) {
        if (head == null) {
            return false;
        }
        // 如果长度为1
        if (size == 1) {
            if (head.data.equals(o)) {
                head = tail = null;
                size--;
                return true;
            } else {
                return false;
            }
        }
        Node cur = head;
        Node pre = head;
        boolean deleted = false;
        while (cur != null) {
            // 判断null
            if (cur.data == null) {
                if (o == null) {
                    pre.next = cur.next;
                    deleted = true;
                    break;
                }
            } else if (cur.data.equals(o)) {
                pre.next = cur.next;
                deleted = true;
                break;
            }
            // 修改指向
            pre = cur;
            cur = cur.next;
        }
        // 如果删除成功,需要判断当前操作的节点是不是特殊节点
        if (deleted) {
            if (cur == head) {
                head = head.next;
            } else if (cur == tail) {
                tail = pre;
            }
            size--;
        }
        return deleted;
    }

    public int size() {
        return size;
    }

    public boolean empty() {
        return size == 0;
    }


    @Override
    public String toString() {
        if (head == null) {
            return null;
        }
        Node cur = head;
        StringBuffer stringBuffer = new StringBuffer("(");
        while (cur != null) {
            stringBuffer.append(cur.data);
            stringBuffer.append(",");
            cur = cur.next;
        }
        return stringBuffer.substring(0, stringBuffer.length() - 1) + ")";
    }


    public static void main(String[] args) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.add(null);
        singleLinkedList.add(12);
        singleLinkedList.add("string");
        // (null,12,string)
        System.out.println(singleLinkedList);
        // string
        Node node = singleLinkedList.find("string");
        System.out.println(node);

        boolean deleted = singleLinkedList.delete("string");
        System.out.println("deleted =  " + deleted);
        // (null,12)
        System.out.println(singleLinkedList);

    }
}
  1. 属性介绍:
  • head: 链表的首节点,链表中的最核心的字段,tail和size都是可以通过这个字段计算出来。不过为了快速实现常用方法,这两个而外的字段最好加上。
  • tail: 链表的尾节点,方便直接往最后一个节点添加元素,缺点就是需要多维护一个字段,在元素添加和删除特殊节点时要记得修改这个值。
  • size: 链表的长度
  1. 方法介绍
  • add: 往链表最后再添加一个元素,需要主要tail的指向需要改变,关键代码:
            // 原来的尾节点next指向新节点
            tail.next = cur;
            // 新节点变成现在的尾节点
            tail = cur;
            // 将新尾节点的next置为null
            tail.next = null;
  • find: 查找指定数据所对应的节点,就是通过head.next不断遍历链表节点。不过需要注意处理节点数据为null的情况,不然会出现空指针的问题。
  • delete:删除指定数据所对应的节点,利用find相同的原理查找到指定的节点,然后修改该节点前一个节点的指向。需要注意如果删除的节点是head/tail这两个特殊节点时,需要同时修改head/tail的指向。

3.双向链表

双向链表
可以看出双向链表只是在node结构上增加了一个字段pre:表示前一个节点。

    public static class Node {
        private Object data;
        private Node next;
        private Node pre;

        Node(Object data) {
            this.data = data;
        }
    }

双向链表需要在add和delete方法增加一段维护pre指向的代码,由于逻辑几乎一样,就不做赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值