Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >数据结构界的“六脉神剑”:数组、链表、哈希表、栈、队列、树的终极解析与实战演练

数据结构界的“六脉神剑”:数组、链表、哈希表、栈、队列、树的终极解析与实战演练

原创
作者头像
疯狂的KK
发布于 2024-05-06 07:15:38
发布于 2024-05-06 07:15:38
3000
举报
文章被收录于专栏:AI绘画AI绘画Java项目实战
引言

在编程的世界里,数据结构是构建高效算法的基石。它们就像是武侠小说中的武功秘籍,掌握了它们,就能在代码的江湖中游刃有余。今天,我们就来深入探讨数据结构界的“六脉神剑”——数组、链表、哈希表、栈、队列和树。这六种数据结构,每一种都有其独特的运行原理和应用场景,它们是编程高手的必备技能。

一、数组:数据的连续存储

运行原理:数组是最基本的数据结构,它将数据元素连续存储在内存中,通过下标直接访问。

应用场景:适用于需要快速随机访问数据的场合。

源码及解析

代码语言:markdown
AI代码解释
复制
public class ArrayDemo {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        System.out.println(numbers[0]); // 直接访问第一个元素
    }
}

数组的简单和高效使其成为处理大量数据的首选。

二、链表:数据的非连续存储

运行原理:链表中的每个元素包含数据部分和指向下一个元素的指针。

应用场景:适用于频繁插入和删除数据的场合。

源码及解析

代码语言:markdown
AI代码解释
复制
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
        val = x;
        next = null;
    }
}

public class LinkedListDemo {
    public static void main(String[] args) {
        ListNode head = new ListNode(1);
        head.next = new ListNode(2);
        head.next.next = new ListNode(3);
        // 遍历链表
        ListNode current = head;
        while (current != null) {
            System.out.println(current.val);
            current = current.next;
        }
    }
}

链表提供了比数组更灵活的内存使用方式。

三、哈希表:快速查找的利器

运行原理:哈希表通过哈希函数将键映射到表中一个索引上,以支持快速的数据访问。

应用场景:适用于需要快速查找、插入和删除数据的场合。

源码及解析

代码语言:markdown
AI代码解释
复制
import java.util.HashMap;

public class HashTableDemo {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("one", 1);
        map.put("two", 2);
        System.out.println(map.get("one")); // 快速获取键对应的值
    }
}

哈希表的快速访问能力使其在数据库和缓存系统中大放异彩。

四、栈:后进先出的集合

运行原理:栈遵循后进先出(LIFO)原则,最后一个进入的元素将是第一个被移除的。

应用场景:适用于需要逆序处理数据的场合,如括号匹配、函数调用的顺序控制。

源码及解析

代码语言:markdown
AI代码解释
复制
import java.util.Stack;

public class StackDemo {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(1);
        stack.push(2);
        System.out.println(stack.pop()); // 2, 后进先出
    }
}

栈在处理递归和回溯算法时尤为重要。

五、队列:先进先出的集合

运行原理:队列遵循先进先出(FIFO)原则,第一个进入的元素将是第一个被移除的。

应用场景:适用于需要保持数据处理顺序的场合,如任务调度。

源码及解析

代码语言:markdown
AI代码解释
复制
import java.util.LinkedList;
import java.util.Queue;

public class QueueDemo {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(1);
        queue.add(2);
        System.out.println(queue.poll()); // 1, 先进先出
    }
}

队列在消息队列和缓冲区管理中扮演着重要角色。

六、树:层次化的数据结构

运行原理:树是由节点组成的层次结构,每个节点有零个或多个子节点。

应用场景:适用于表示具有层次关系的数据,如文件系统、组织结构。

源码及解析

代码语言:markdown
AI代码解释
复制
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) {
        val = x;
    }
}

public class TreeDemo {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        // 遍历树
        inorderTraversal(root);
    }

    public static void inorderTraversal(TreeNode node) {
        if (node != null) {
            inorderTraversal(node.left);
            System.out.println(node.val);
            inorderTraversal(node.right);
        }
    }
}

树结构在索引和搜索算法中非常关键,如二叉搜索树。

七、对象字段校验非空

在实际开发中,对象字段的非空校验是非常重要的。以下是使用Java进行非空校验的简单示例:

代码语言:markdown
AI代码解释
复制
public class ValidationDemo {
    public static void main(String[] args) {
        try {
            User user = new User(null, "John");
            validateUser(user);
            // 其他逻辑...
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }

    public static void validateUser(User user) {
        if (user == null) {
            throw new IllegalArgumentException("User object cannot be null.");
        }
        if (user.getName() == null || user.getName().isEmpty()) {
            throw new IllegalArgumentException("User name cannot be null or empty.");
        }
        // 其他字段校验...
    }
}

class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}

通过异常处理,我们可以确保对象在使用前满足必要的非空条件。

八、实战演练:设计一个简单的缓存系统

在了解了上述数据结构之后,让我们通过一个实战演练来加深理解。我们将设计一个简单的缓存系统,它将使用哈希表来存储数据,并使用双向链表来处理数据的过期和替换。

运行原理:缓存系统通常使用最近最少使用(LRU)算法来确定哪些数据应该被替换。我们使用哈希表来快速定位数据,使用双向链表来维护数据的顺序。

源码及解析

代码语言:java
AI代码解释
复制
import java.util.HashMap;
import java.util.Map;

class LRUCache {

    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        DLinkedNode(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    private void addNode(DLinkedNode node) {
        // Always add the new node right after head.
        node.prev = head;
        node.next = head.next;

        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(DLinkedNode node) {
        // Remove an existing node from the linked list.
        DLinkedNode prev = node.prev;
        DLinkedNode next = node.next;

        prev.next = next;
        next.prev = prev;
    }

    private void moveToHead(DLinkedNode node) {
        // Move certain node in between to the head.
        removeNode(node);
        addNode(node);
    }

    private DLinkedNode popTail() {
        // Pop the current tail.
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }

    private Map<Integer, DLinkedNode> cache = new HashMap<>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;

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

    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) return -1;
        
        // Move the accessed node to the head.
        moveToHead(node);
        
        return node.value;
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        
        if (node == null) {
            DLinkedNode newNode = new DLinkedNode(key, value);
            addNode(newNode);
            cache.put(key, newNode);
            size++;

            if (size > capacity) {
                // pop the tail
                DLinkedNode tail = popTail();
                cache.remove(tail.key);
                size--;
            }
        } else {
            // update the value.
            node.value = value;
            moveToHead(node);
        }
    }
}

public class CacheDemo {
    public static void main(String[] args) {
        LRUCache lruCache = new LRUCache(2);
        lruCache.put(1, 1); // cache is {1=1}
        lruCache.put(2, 2); // cache is {1=1, 2=2}
        System.out.println(lruCache.get(1)); // returns 1, cache is {2=2, 1=1}
        lruCache.put(3, 3); // LRU key 2 is removed, cache is {3=3, 1=1}
        System.out.println(lruCache.get(2)); // returns -1 (not found)
        lruCache.put(4, 4); // LRU key 3 is removed, cache is {4=4, 1=1}
        System.out.println(lruCache.get(1)); // returns 1, cache is {4=4, 1=1}
        System.out.println(lruCache.get(3)); // returns -1 (not found)
        System.out.println(lruCache.get(4)); // returns 4, cache is {1=1, 4=4}
    }
}

上述代码演示了如何使用哈希表和双向链表实现一个简单的LRU缓存淘汰机制。

结语

通过上述的详细解析和代码示例,我们深入了解了数组、链表、哈希表、栈、队列和树这六种基础数据结构的运行原理和应用场景。每种数据结构都有其独特的优势和适用场景,掌握它们对于解决实际编程问题至关重要。

在文章的最后,我想邀请各位读者分享你们在使用这些数据结构时的经验和心得。你们是否遇到过特别棘手的问题,又是如何巧妙解决的?欢迎在评论区留下你们的足迹,让我们一起交流学习,共同进步!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
实现 LRU 缓存算法
LRU 算法全称是最近最少使用算法(Least Recently Use),是一种简单的缓存策略。顾名思义,LRU 算法会选出最近最少使用的数据进行淘汰。
Se7en258
2022/06/24
1K0
实现 LRU 缓存算法
☆打卡算法☆LeetCode 146. LRU 缓存 算法解析
请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类:
恬静的小魔龙
2022/08/07
3230
☆打卡算法☆LeetCode 146. LRU 缓存 算法解析
经典算法之链表篇(三)
ma布
2024/10/21
1110
经典算法之链表篇(三)
【LeetCode】146. LRU 缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。 实现 LRUCache 类:
韩旭051
2021/01/07
4680
LRU算法简介
LRU(Least Recently Used)算法是一种缓存淘汰算法,常用于缓存系统中,通过保留最近使用的数据而淘汰最久未使用的数据,以提高缓存的命中率。LRU算法的核心思想是基于时间局部性原理:最近访问的数据在未来会被再次访问。
孟斯特
2024/01/28
6990
LRU算法简介
lru_cache分析
在计算机软件领域,缓存(Cache)指的是将部分数据存储在内存中,以便下次能够更快地访问这些数据,这也是一个典型的用空间换时间的例子。一般用于缓存的内存空间是固定的,当有更多的数据需要缓存的时候,需要将已缓存的部分数据清除后再将新的缓存数据放进去。需要清除哪些数据,就涉及到了缓存置换的策略,LRU(Least Recently Used,最近最少使用)是很常见的一个,也是 Python 中提供的缓存置换策略。
Yerik
2021/08/22
6600
Leetcode LRU 缓存机制
缓存是一种提高数据读取性能的技术,在计算机中cpu和主内存之间读取数据存在差异,CPU和主内存之间有CPU缓存,而且在内存和硬盘有内存缓存。当主存容量远大于CPU缓存,或磁盘容量远大于主存时,哪些数据应该被应该被清理,哪些数据应该被保留,这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略FIFO(First In,First Out)、最少使用策略LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used)。
用户10384376
2023/02/25
3290
Leetcode LRU 缓存机制
LRU 缓存机制实现:哈希表 + 双向链表
LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。
一个会写诗的程序员
2021/03/23
2K0
LRU 缓存机制实现:哈希表 + 双向链表
146. LRU 缓存机制
要在O(1)时间复杂度完成这两种操作,我们想到的使用HashMap来进行操作,而且参考LRUCache的特性,需要对元素进行移动或者删除,首选的是双向链表。
用户7447819
2021/07/23
2990
2020-09-18:LRU手撸,说下时间复杂度和空间复杂度。
空间复杂度:O(capacity),因为哈希表和双向链表最多存储 capacity+1 个元素。
福大大架构师每日一题
2020/09/18
1.1K0
2020-09-18:LRU手撸,说下时间复杂度和空间复杂度。
搞定大厂算法面试之leetcode精讲15.链表
搞定大厂算法面试之leetcode精讲15.链表 视频讲解(高效学习):点击学习 目录: 1.开篇介绍 2.时间空间复杂度 3.动态规划 4.贪心 5.二分查找 6.深度优先&广度优先 7.双指针 8.滑动窗口 9.位运算 10.递归&分治 11剪枝&回溯 12.堆 13.单调栈 14.排序算法 15.链表 16.set&map 17.栈 18.队列 19.数组 20.字符串 21.树 22.字典树 23.并查集 24.其他类型题 链表操作如下图: 动画过大,点击查看 时间复杂度: prepend: O(1
全栈潇晨
2021/12/02
4540
Leetcode模块训练2
1. 两数之和(1)# 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。 示例 1: 输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。 示例 2: 输入:nums = [3,2
素履coder
2022/11/16
3420
十道腾讯算法真题解析!
大家好,我是捡田螺的小男孩。收集了腾讯常考的十道算法题(真题)。在金三银四,希望对大家有帮助呀。
捡田螺的小男孩
2022/04/06
9000
十道腾讯算法真题解析!
LRU缓存
LRU 算法就是一种缓存淘汰策略,原理不难,但是面试中写出没有 bug 的算法比较有技巧,需要对数据结构进行层层抽象和拆解,本文就带你写一手漂亮的代码。
狼啸风云
2023/11/18
2260
LeetCode-146-LRU缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
benym
2022/07/14
3160
实现LRU算法
计算机的缓存容量有限,如果缓存满了就要删除一些内容给新的内容腾出位置,而删除哪些内容,就有不同的策略,LRU算法是其中一种策略。
Defu Li
2020/09/08
8740
【设计数据结构】实现一个 LRUCache
这是 LeetCode 上的 「146. LRU 缓存机制」 ,难度为 「中等」。
宫水三叶的刷题日记
2021/06/23
7010
数据结构与算法思想
分治法是基于多项分支递归的一种很重要的算法范式。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
知一
2021/12/07
4410
数据结构与算法思想
漫画:什么是LRU算法?
用户信息当然是存在数据库里。但是由于我们对用户系统的性能要求比较高,显然不能每一次请求都去查询数据库。
AI科技大本营
2019/05/16
5980
Python中的collections
在 Python 中,collections是一个非常有用的内置模块,它提供了一些高性能的数据类型,可以帮助你更高效地处理数据。
宅蓝三木
2024/10/09
1190
相关推荐
实现 LRU 缓存算法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档