数据结构:3 数组、链表、跳表原理和实现

数组、链表、跳表原理和实现

数组

  • 底层实现:
    每当申请数组时,计算机在内存中开辟了一串连续的地址。每个地址通过内存管理器访问。
    在这里插入图片描述
  • ArrayList增删改查的时间复杂度
操作时间复杂度
PrependO(1)
AppendO(1)
LookupO(1)
InsertO(n)
DeleteO(n)

链表

  • LinkedList的标准实现代码:
    https://www.geeksforgeeks.org/implementing-a-linked-list-in-java-using-class/

  • LinkedList增删改查的时间复杂度

操作时间复杂度
PrependO(1)
AppendO(1)
LookupO(n)
InsertO(1)
DeleteO(1)
  • 如何给链表加速

  • 以空间换时间

  • 工程中的应用

  • LRU Cache

    LeetCode 题目:https://leetcode-cn.com/problems/lru-cache/

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。
它应该支持以下操作: 获取数据 get 和 写入数据 put 。

1 获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。

2 写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;
如果关键字不存在,则插入该组「关键字/值」。
当缓存容量达到上限时,
它应该在写入新数据之前删除最久未使用的数据值,
从而为新的数据值留出空间。


进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?


A 先说下,以前我在项目工程中如何处理这类缓存,时间复杂度应该是没有O(1);
设置一个HashMap<数据,时间戳>,用来存储数据;一个过期时间,用来比较数据是否过期;容量,超过容量后清理HashMap。这样就导致时间复杂度不可能是O(1)

B 官方题解1
  哈希表 + 双向链表
- key,value 则需要哈希表
- 哈希表 可以实现O(1)的访问速度
- 涉及到插入,删除顺序,则联想到 栈、队列、链表
- 访问完一个数据后,将数据插到头部
- 久未使用的数据据,容量满后,删除尾部的数据,这里就涉及到双向链表
- 删除一个节点不光要得到该节点本身的指针,也需要操作其前驱节点的指针,而双向链表才能支持直接查找前驱,保证操作的时间复杂度 O(1)
总结: 哈希表快速找到key值,链表可以快速删除和插入数据


import java.util.HashMap;
import java.util.Map;

public class LRUCache {
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;

        public DLinkedNode() {
        }

        public DLinkedNode(int key, int value) {
            this.key = key;
            this.value = value;
        }


    }

    private Map<Integer,DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();

    private int size ;

    private int capacity;

    private DLinkedNode head,tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    /**
     * 时间复杂度O(1)
     * 空间复杂度:O(capacity),因为哈希表和双向链表最多存储 capacity + 1
     * @param key
     * @return
     */
    public int get(int key){
        DLinkedNode node = cache.get(key) ;
        if ( node == null ){
            return -1;
        }
        // 如果 key存在,先通过哈希表定位,再移到头部
        moveToHead(node);
        return node.value;
    }

    /**
     * 时间复杂度O(1)
     * 空间复杂度:O(capacity),因为哈希表和双向链表最多存储 capacity + 1
     * @param key
     * @param value
     */
    public void put(int key,int value) {
        DLinkedNode node = cache.get(key);
        if ( node == null) {
            DLinkedNode newNode = new DLinkedNode(key,value);
            cache.put(key,newNode);
            addToHead(newNode);
            ++size;
            //超出容量,删除双向链表的尾部节点
            if (size > capacity){
              DLinkedNode tail = removeTail();
              cache.remove(tail.key);
              --size;
            }
        }else {
            node.value = value;
            moveToHead(node);
        }
    }

    /**
     * 删除尾部节点
     * @return
     */
    private DLinkedNode removeTail() {
        DLinkedNode node = tail.prev;
        removeNode(node);
        return node;
    }

    /**
     * 移动到头部
     * @param node
     */
    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    /**
     * 添加到头部
     * @param node
     */
    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node ;
    }

    /**
     * 删除节点
     * @param node
     */
    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    public static void main(String[] args) {
        LRUCache cache = new LRUCache(2);
        cache.put(1,1);
        cache.put(2,2);
        //节点1移到头
        System.out.println(cache.get(1));
        //节点2被删除,3移到头
        cache.put(3,3);
        System.out.println(cache.get(2));
    }
}

跳表

跳跃表(skiplist)是一种有序数据结构,通过在每个节点中维持多个指向其他节点的指针,达到快速访问其他节点的目的

  • 增加索引的方式,进行查询元素

在这里插入图片描述

  • n/2,n/4,n/8,第k级索引节点的个数就是 n/(2^k)

  • 假设索引有h级,最高级的索引有2个节点。n/(2^h) = 2,则 h =log_2⁡n – 1

  • 索引的高度:log_2⁡n;

  • 查询任意数据的时间复杂度:0(logn)

  • Redis中跳跃表的实现图,暂时没有读懂,后续更新
    在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rong742954

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值