算法集:快速排序、布隆过滤器、LRU算法、Dijkstra算法、字符串转整数、翻转二叉树

本文介绍了几种重要的算法实现,包括快速排序的优化技巧,布隆过滤器的误判率控制,LRU缓存淘汰策略以及Dijkstra算法寻找最短路径。同时,还探讨了字符串转int的方法和翻转二叉树的迭代解决方案。

记录几个算法:快速排序、布隆过滤器、LRU算法

快速排序

老是容易把快速排序写歪了;这里给自己定几个关键点:

  1. 选择数组最后一项作为pivot;遍历的时候就不需要遍历最后一项了;(这样的好处是index++肯定不会有越界的风险,也不需要额外判断)
  2. index = low;每次遇到比pivot小的值就交换到arr[index];最终再将arr[index]和arr[high]交换即可

通常会拆分为两个方法:partition和quickSort;partition负责对当前数组排序,返回数组分界下标;quickSort负责判断是否需要继续排序(low < high)和递归调用

    private static void quickSort(int arr[], int low, int high) {
        if (low < high) {
            int index = partition(arr, low, high);
            // 根据数组分界点,递归调用
            quickSort(arr, low, index - 1);
            quickSort(arr, index + 1, high);
        }
    }
	// 排序实现
    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int index = low;
        for (int i = low; i < high; i++) {
            // 交换
            if (arr[i] < pivot) {
                int tmp = arr[i];
                arr[i] = arr[index];
                arr[index++] = tmp;
            }
        }
        // 交换arr[high]和arr[index]
        int tmp = arr[high];
        arr[high] = arr[index];
        arr[index] = tmp;
        // 返回数组分界下标
        return index;
    }

布隆过滤器

import java.util.BitSet;

public class BloomFilter<E> {

    private final BitSet bitSet;

    private final int bitSetSize;

    private final int numOfHashFunctions;

    private static final int DEFAULT_SIZE = 1 << 25;

    private static final int DEFAULT_NUM_HASH = 1 << 3;

    /**
     * 构造函数,初始化布隆过滤器
     * 
     * @param expectedNumItems 预期插入元素的数量
     * @param falsePositiveProbability 期望的误判率
     */
    public BloomFilter(int expectedNumItems, double falsePositiveProbability) {
        // 计算 bitSet 的大小
        bitSetSize = (int) Math.ceil(
            expectedNumItems * Math.log(falsePositiveProbability) / Math.log(1.0 / (Math.pow(2.0, Math.log(2.0)))));
        // 计算 hash 函数的个数
        numOfHashFunctions = (int) Math.round((double) bitSetSize / expectedNumItems * Math.log(2.0));
        // 初始化 bitSet 和随机数生成器
        bitSet = new BitSet(bitSetSize);
    }

    /**
     * 默认无参构造函数
     */
    public BloomFilter() {
        bitSetSize = DEFAULT_SIZE;
        bitSet = new BitSet(bitSetSize);
        numOfHashFunctions = DEFAULT_NUM_HASH;
    }

    /**
     * 插入一个元素到布隆过滤器中
     * 
     * @param element 要插入的元素
     */
    public void add(E element) {
        // 对元素进行多次 hash,将对应的 bit 置为 1
        for (int i = 0; i < numOfHashFunctions; i++) {
            int hashValue = hash(element, i);
            bitSet.set(hashValue, true);
        }
    }

    /**
     * 判断一个元素是否在布隆过滤器中存在
     * 
     * @param element 要判断的元素
     * @return true 表示可能存在,false 表示一定不存在
     */
    public boolean contains(E element) {
        // 对元素进行多次 hash,判断对应的 bit 是否都为 1
        for (int i = 0; i < numOfHashFunctions; i++) {
            int hashValue = hash(element, i * 100);
            if (!bitSet.get(hashValue)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 计算元素的 hash 值
     * 
     * @param element 要计算 hash 的元素
     * @param seed 随机种子
     * @return 元素的 hash 值
     */
    private int hash(E element, int seed) {
        int h;
        int value = (element == null) ? 0 : Math.abs((h = element.hashCode()) ^ (h >>> 16));
        return (bitSetSize - 1) & (seed * value);
    }

    public static void main(String[] args) {
        BloomFilter bloomFilter = new BloomFilter<>();
        bloomFilter.add("123");

    }
}

布隆过滤器的核心在于hash函数的设计,上面的例子中使用了类似hashMap计算hash值的方式。
java的bitset作为存储结构,这种数据结构是可以自动扩容的,但是通常情况下我们应该保持bitmap结构稳定;所以在hash之后跟(bitSetSize - 1) & (seed * value)保证结果在bitmap范围内,不用扩容。该代码示例之前的hash实现是:

return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16)));

我认为这样是不合理的,结果可能超出范围,乘法跟位运算是相同优先级,随机种子选择素数,seed * (cap - 1) 就超了bitmap的长度;在进行&运算,感觉会溢出导致扩容,不知道是不是我理解的有问题,反正我觉得不合理。

LRU算法

LRU(Least Recently Used,最近最少使用)是一种常见的页面置换算法,用于解决操作系统中内存页的管理问题。

LRU 算法的基本思想是:当内存不足时,将最近最少被访问的页替换出去,以腾出空间存储新的页。

LRU 算法的实现需要用到一个数据结构,通常是一个双向链表和一个哈希表。哈希表中存储每个页的地址和对应的节点在链表中的位置,链表中按照访问时间的先后顺序存储了所有的页,最近访问的页在链表的头部,最久未访问的页在链表的尾部。

具体的实现过程如下:

当一个页被访问时,如果该页已经在链表中,则将其移动到链表头部,表示该页最近被访问过。
如果该页不在链表中,则需要将其添加到链表头部,并将其地址和对应的节点位置存储在哈希表中。
当需要替换一页时,选择链表尾部的页进行替换,并将该页从链表和哈希表中删除。
LRU 算法的时间复杂度为 O(1),因为链表和哈希表的查找和删除操作都可以在常数时间内完成。但是,由于需要维护一个链表和一个哈希表,算法的空间复杂度比较高。
LRU算法及其优化策略——算法篇

public class MyLRUCache {

    // 使用双向链表和map来维护

    private final int capacity;

    private final Map<String, Node> map;

    private final Node head;

    private final Node tail;

    public MyLRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>(capacity);
        head = new Node("HEAD");
        tail = new Node("TAIL");
        head.next = tail;
        tail.pre = head;
    }

    public void put(String value) {
        Node node = map.get(value);
        if (node != null) { // 节点已经存在
            // 判断是否为头节点,是则不需要操作
            if (node.pre == head) {
                return;
            }
            // 移到链表头
            moveToHead(node);
        } else { // 节点不存在,加入链表头
            // 判断是否需要进行移除操作
            eliminate();
            node = new Node(value);
            // 处理当前节点
            node.pre = head;
            node.next = head.next;
            // 处理head.next
            head.next.pre = node;
            // 处理head
            head.next = node;
            map.put(value, node);
        }
    }

    public String get(String value) {
        Node node = map.get(value);
        if (node == null) {
            return null;
        }
        if (node.pre != head) {
            // 移到链表前
            moveToHead(node);
        }
        return node.value;
    }

    private void moveToHead(Node node) {
        // 先断
        Node pre = node.pre;
        Node next = node.next;

        pre.next = node.next;
        next.pre = node.pre;

        // 再加入链表头
        // 处理node
        node.pre = head;
        node.next = head.next;
        // 处理head.next
        head.next.pre = node;
        // 处理head
        head.next = node;
    }

    private void eliminate() {
        if (map.size() < capacity) {
            return;
        }
        // 移除链表尾元素
        Node node = tail.pre;
        // 处理node.pre
        node.pre.next = tail;
        // 处理tail
        tail.pre = node.pre;
        map.remove(node.value);
    }

    static class Node {
        String value;

        Node next;

        Node pre;

        public Node(String value) {
            this.value = value;
            next = null;
            pre = null;
        }
    }

    public static void main(String[] args) {
        MyLRUCache myLRUCache = new MyLRUCache(3);
        myLRUCache.put("A");
        myLRUCache.put("B");
        myLRUCache.put("C");
        myLRUCache.get("A");
        myLRUCache.put("D");
    }
}

Dijkstra算法

Dijkstra算法是找某个节点到图的其他节点的最短路径,它要求边不能有负权重

public class Dijkstra {

    private int size;

    private int[][] graph; // 邻接矩阵

    private int target; // 目标节点,即求该节点到其他所有节点的最短路径

    private int[] distance; // 记录最短距离

    private boolean[] visited; // 记录是否访问过

    private char[] precursor; // 前驱

    /**
     * 初始化
     * 
     * @param graph
     * @param target
     */
    public Dijkstra(int[][] graph, int target) {
        this.graph = graph;
        this.target = target;
        size = graph.length;
        distance = new int[graph.length];
        visited = new boolean[graph.length];
        precursor = new char[graph.length];
        init();
    }

    private void init() {
        for (int i = 0; i < size; i++) {
            distance[i] = graph[target][i] >= 0 ? graph[target][i] : Integer.MAX_VALUE;
            precursor[i] = 'A';
        }
        visited[target] = true;
    }

    public void run() {
        calShortestPath(searchMinDistance());
    }

    private void calShortestPath(int index) {
        // 计算经过index到其他节点的距离,小于则更新
        visited[index] = true;
        for (int i = 0; i < size; i++) {
            if (visited[i]) {
                continue;
            }
            if (graph[index][i] == -1) {
                continue;
            }
            if (distance[index] + graph[index][i] < distance[i]) {
                distance[i] = distance[index] + graph[index][i];
                precursor[i] = (char) ('A' + index);
            }
        }
        int newIndex = searchMinDistance();
        if (newIndex < 0) {
            System.out.println(Arrays.toString(distance));
            System.out.println(Arrays.toString(precursor));
            return;
        }
        calShortestPath(newIndex);
    }

    private int searchMinDistance() {
        int min = Integer.MAX_VALUE;
        int index = -1;
        for (int i = 0; i < size; i++) {
            if (visited[i]) {
                continue;
            }
            if (min > distance[i]) {
                min = distance[i];
                index = i;
            }
        }
        return index;
    }

    public static void main(String[] args) {
        int[][] graph =
            new int[][] {{0, 4, -1, 2, -1}, {4, 0, 4, 1, -1}, {-1, 4, 0, 1, 3}, {2, 1, 1, 0, 7}, {-1, -1, 3, 7, 0}};
        int target = 0;
        Dijkstra dijkstra = new Dijkstra(graph, target);
        dijkstra.run();

    }

}

字符串转int类型

主要学习java中Integer.parseInt()方法,实现了一个简易版的字符串转int类型;并且具有一定的健壮性。

public class StringToInt {
    public static void main(String[] args) {
        // "123", "0123", "-1234", "+31231",
        String[] ss = new String[]{"-2147483649", "0123", "-123", "123", "2147483647", "-2147483647", "-2147483648", "2147483648"};
        for (String s : ss) {
            try {
                System.out.println(str2Int(s));
            } catch (Exception e) {
                System.out.println("invalid in str2Int: " + s);
            }
            try {
                System.out.println(Integer.parseInt(s));
            } catch (Exception e) {
                System.out.println("invalid in parseInt: " + s);
            }
        }
    }

    public static int str2Int(String s) {
        if (s == null || s.length() == 0) {
            throw new NumberFormatException();
        }
        // 判断正负
        boolean isNegative = false;
        int index = 0;
        char ch = s.charAt(index);
        if (ch == '-') {
            index++;
            isNegative = true;
        } else if (ch == '+') {
            index++;
        }
        if (index >= s.length()) {
            throw new NumberFormatException();
        }
        return str2IntCore(s, isNegative, index);
    }

    private static int str2IntCore(String s, boolean isNegative, int index) {
        // 统一用负值的情况来处理
        int limit = isNegative ? Integer.MIN_VALUE : -Integer.MAX_VALUE;
        int multmin = limit / 10; // 判断int位数
        int res = 0;
        for (int i = index; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (ch < '0' || ch > '9') throw new NumberFormatException();
            // 因为是负值,res只有比multmin大才能继续 * 10
            if (res < multmin) throw new NumberFormatException();
            res *= 10;
            int digit = ch - '0';
            if (res < limit + digit) throw new NumberFormatException();
            res -= digit;
        }
        return isNegative ? res : -res;
    }
}

翻转二叉树

包括构建树、翻转树、验证翻转结果(验证逻辑:对翻转前跟翻转后按层次遍历,每层节点list刚好翻转说明翻转正确)

public class Tree {

    static class TreeNode {
        public int value;

        public TreeNode left;

        public TreeNode right;

        public TreeNode(int value) {
            this.value = value;
        }
    }

    public TreeNode buildTree(int[] nodeValues) {
        TreeNode[] treeNodes = new TreeNode[nodeValues.length];
        // 根据value构造treeNodes
        TreeNode root = null;
        for (int i = 0; i < nodeValues.length; i++) {
            TreeNode node = null;
            if (nodeValues[i] != -1) {
                node = new TreeNode(nodeValues[i]);
            }
            treeNodes[i] = node;
            if (i == 0) {
                root = node;
            }
        }
        // 把treeNode挂到root下面
        for (int i = 0; i * 2 + 1 < treeNodes.length; i++) { // 这里注意是只到i * 2 + 1;为什么不是i * 2 + 2;因为它可能有左节点但没有右节点
            if (treeNodes[i] != null) {
                treeNodes[i].left = treeNodes[i * 2 + 1];
                if (i * 2 + 2 < treeNodes.length) {
                    treeNodes[i].right = treeNodes[i * 2 + 2];
                }
            }
        }
        return root;
    }

    public ArrayList<ArrayList<Integer>> hierarchicalTraverse(TreeNode root) {
        ArrayList<ArrayList<Integer>> list = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        if (root != null) {
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            ArrayList<Integer> subList = new ArrayList<>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                if (node != null) {
                    subList.add(node.value);
                    queue.offer(node.left);
                    queue.offer(node.right);
                } else {
                    subList.add(-1);
                }
            }
            list.add(subList);
        }
        return list;
    }

    // 递归写多了,这里使用迭代翻转二叉树
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                // 交换左右节点
                TreeNode tmp = node.left;
                node.left = node.right;
                node.right = tmp;
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
        }
        return root;
    }

    public boolean verify(List<ArrayList<Integer>> list1, List<ArrayList<Integer>> list2) {
        if (list1 == null) {
            return list2 == null;
        }
        if (list2 == null) {
            return false;
        }
        if (list1.size() != list2.size()) {
            return false;
        }
        // 遍历每层
        for (int i = 0; i < list1.size(); i++) {
            List<Integer> subList1 = list1.get(i);
            List<Integer> subList2 = list2.get(i);
            if (subList2.size() != subList1.size()) {
                return false;
            }
            for (int j = 0; j < subList1.size(); j++) {
                if (subList2.get(subList2.size() - j - 1) != subList1.get(j)) {
                    return false;
                }
            }
        }
        return true;
    }

    public static void main(String[] args) {
        Tree tree = new Tree();
        int[] nodeValues = new int[] {4, 1, 6, 0, 2, 5, 7, -1, -1, -1, 3, -1, -1, -1, 8};
        TreeNode root = tree.buildTree(nodeValues);
        // 注意这里的结果会多一层空节点,还没有想好在遍历的时候怎么去掉它们;可以在处理完成之后删除list的最后一项
        List<ArrayList<Integer>> list1 = tree.hierarchicalTraverse(root);
        System.out.println(list1);
        root = tree.invertTree(root);
        List<ArrayList<Integer>> list2 = tree.hierarchicalTraverse(root);
        System.out.println(list2);
        System.out.println(tree.verify(list1, list2));
    }
}
如果你是一名专业的java高级架构师,现在你在面试知识,如下是 数据结构与算法的面试知识体系结构图 请按照这个体系给出每个知识点的学习理解方便记忆和消化,同时给出每个知识点的高频面试的标准面试答案,结合项目经验给出解释和实战 数据结构与算法面试核心知识体系 ├── 一、复杂度分析基础 │ ├── 时间复杂度 │ │ ├── 大O表示法:O(1), O(logn), O(n), O(nlogn), O(n²)等含义? │ │ ├── 最好、最坏、平均时间复杂度分析? │ │ └── 递归算法的时间复杂度分析:主定理? │ ├── 空间复杂度 │ │ ├── 算法运行所需的额外空间?原地操作含义? │ │ ├── 递归调用的空间复杂度(调用栈深度)? │ │ └── 如何权衡时间与空间复杂度? │ └── 实际应用 │ ├── 如何根据数据规模选择合适的算法?(10⁵数据不能用O(n²)) │ ├── 常数项优化在实际工程中的意义? │ └── 摊还分析:某些操作的平均代价? ├── 二、数组、链表与字符串 │ ├── 数组(Array) │ │ ├── 特点:随机访问O(1),插入删除O(n)? │ │ ├── 双指针技巧:快慢指针、左右指针、滑动窗口? │ │ ├── 前缀和数组:快速计算区间和? │ │ └── 差分数组:快速进行区间增减操作? │ ├── 链表(Linked List) │ │ ├── 单链表、双链表、循环链表区别? │ │ ├── 虚拟头节点(dummy node)的作用? │ │ ├── 常见问题:反链表、检测环、相交链表、合并有序链表? │ │ └── 链表排序:归并排序实现? │ └── 字符串(String) │ ├── 字符串匹配算法:KMP、Rabin-Karp? │ ├── 回文串问题:中心扩展法、动态规划? │ ├── 字符串操作:翻转、替换、分割? │ └── 不可变字符串的优势?(线程安全、缓存哈希值) ├── 三、栈、队列与哈希表 │ ├── 栈(Stack) │ │ ├── LIFO特性,应用场景:函数调用栈、括号匹配? │ │ ├── 单调栈:解决"下一个更大元素"问题? │ │ └── 最小栈:如何O(1)获取栈中最小值? │ ├── 队列(Queue) │ │ ├── FIFO特性,BFS算法基础? │ │ ├── 优先队列(堆):获取最值,Dijkstra算法? │ │ ├── 单调队列:解决滑动窗口最值问题? │ │ └── 双端队列(Deque):实现滑动窗口? │ └── 哈希表(Hash Table) │ ├── 原理:哈希函数、冲突解决(链地址法、开放寻址法)? │ ├── 设计哈希合、哈希映射? │ ├── 实际应用:缓存(LRU)、快速查找、去重? │ └── 哈希碰撞攻击原理?如何设计好的哈希函数? ├── 四、树形数据结构 │ ├── 二叉树基础 │ │ ├── 遍历方式:前序、中序、后序(递归/迭代)? │ │ ├── 二叉搜索树(BST):性质、查找、插入、删除? │ │ ├── 平衡二叉树:AVL树、红黑树基本概念? │ │ └── 完全二叉树、满二叉树定义? │ ├── 树的变种 │ │ ├── 堆(Heap):大顶堆、小顶堆,优先队列实现? │ │ ├── Trie树(前缀树):字符串前缀匹配? │ │ ├── 线段树:区间查询、区间更新? │ │ └── 树状数组(BIT):单点更新、前缀查询? │ └── 树的应用 │ ├── 最近公共祖先(LCA)问题? │ ├── 二叉树的序列化与反序列化? │ ├── 树的直径、高度、路径和问题? │ └── 哈夫曼编码:数据压缩? ├── 五、图论算法 │ ├── 图的表示 │ │ ├── 邻接矩阵 vs 邻接表?空间时间复杂度? │ │ ├── 有向图、无向图、加权图? │ │ └── 图的遍历:BFS、DFS实现? │ ├── 最短路径算法 │ │ ├── Dijkstra算法:非负权图,贪心策略? │ │ ├── Bellman-Ford算法:处理负权边? │ │ ├── Floyd-Warshall算法:多源最短路径? │ │ └── A算法:启发式搜索? │ ├── 最小生成树 │ │ ├── Prim算法:从点开始扩展? │ │ ├── Kruskal算法:按边排序+并查? │ │ └:适用场景:网络布线、电路设计? │ └── 其他图算法 │ ├── 拓扑排序:有向无环图(DAG),课程安排? │ ├── 并查(Union-Find):连通分量,路径压缩优化? │ ├── 欧拉路径/回路:一笔画问题? │ └── 强连通分量:Kosaraju或Tarjan算法? ├── 六、排序与搜索算法 │ ├── 排序算法 │ │ ├── 比较排序:冒泡、选择、插入、归并、快速、堆排序? │ │ ├── 非比较排序:计数排序、桶排序、基数排序? │ │ ├── 稳定性分析:哪些是稳定排序? │ │ └── 各排序算法的时间/空间复杂度总结? │ ├── 搜索算法 │ │ ├── 二分查找:模板、边界处理、旋数组搜索? │ │ ├── DFS深度优先:回溯法,排列组合问题? │ │ ├── BFS广度优先:最短路径,层级遍历? │ │ └── 启发式搜索:A算法,估价函数设计? │ └── 实际应用 │ ├── 海量数据排序:外部排序,归并思想? │ ├── 第K大/小元素:快速选择算法O(n)? │ ├── 在排序数组中查找目标值的起始和结束位置? │ └── 寻找旋排序数组中的最小值? ├── 七、动态规划(DP) │ ├── 基本思想 │ │ ├── 重叠子问题、最优子结构? │ │ ├── 自顶向下(记忆化递归) vs 自底向上(迭代)? │ │ └── 状态定义、状态移方程、边界条件? │ ├── 经典问题 │ │ ├── 背包问题:0-1背包、完全背包、状态压缩? │ │ ├── 最长公共子序列(LCS)、最长递增子序列(LIS)? │ │ ├── 编辑距离:字符串换的最小操作数? │ │ ├── 股票买卖问题:多种限制条件? │ │ └── 打家劫舍、零钱兑换、路径问题? │ └── 解题技巧 │ ├── 如何识别DP问题?状态设计技巧? │ ├── 空间优化:滚动数组? │ ├── 输出具体方案而不仅仅是数值? │ └── 数位DP、状压DP、区间DP简介? ├── 八、贪心算法 │ ├── 基本概念 │ │ ├── 贪心选择性质?局部最优导致全局最优? │ │ ├── 与动态规划的区别?贪心无法回溯? │ │ └── 如何证明贪心策略的正确性? │ ├── 典型问题 │ │ ├── 区间调度:最多不重叠区间? │ │ ├── 哈夫曼编码:最优前缀码? │ │ ├── 分糖果、跳跃游戏、加油站问题? │ │ ├:贪心+排序:根据某个指标排序后贪心? │ │ └:贪心+优先队列:实时获取最优解? │ └── 应用场景 │ ├── 最小生成树:Prim和Kruskal算法中的贪心思想? │ ├── 最短路径:Dijkstra算法的贪心选择? │ ├── 数据压缩:贪心构造最优编码? │ └── 任务调度:合理安排任务顺序? ├── 九、高级数据结构与算法 │ ├── 高级数据结构 │ │ ├── 跳表(Skip List):Redis有序合实现? │ │ ├── 布隆过滤器(Bloom Filter):判断存在性,可能误判? │ │ ├── LRU缓存:哈希表+双向链表实现? │ │ ├── 一致性哈希:分布式系统数据分片? │ │ └── 倒排索引:搜索引擎核心? │ ├── 数学与位运算 │ │ ├── 位操作技巧:判断奇偶、交换数值、找出单独数字? │ │ ├── 素数判断、最大公约数(GCD)、最小公倍数(LCM)? │ │ ├── 快速幂算法:计算a^b % mod? │ │ └── 随机数生成:拒绝采样,水塘抽样? │ └── 高级算法思想 │ ├── 分治算法:归并排序、快速排序、最近点对? │ ├── 回溯算法:N皇后、数独、全排列? │ ├── 位运算优化状态表示(状态压缩)? │ ├:扫描线算法:矩形面积并、天际线问题? │ └:摩尔投票法:寻找多数元素? └── 十、实战技巧与系统设计 ├── 解题方法论 │ ├── 解题步骤:理解题意 -> 分析 -> 选择数据结构 -> 编码 -> 测试? │ ├── 边界条件考虑:空输入、极端值、溢出? │ ├── 代码规范:变量命名、注释、模块化? │ └── 测试用例设计:正常情况、边界情况、错误情况? ├── 系统设计中的应用 │ ├── LRU缓存设计:哈希表+双向链表? │ ├── 排行榜设计:跳表、有序合? │ ├── 短网址系统:发号器、哈希算法? │ ├:朋友圈设计:并查管理社交关系? │ └:搜索提示:Trie树实现自动补全? └── 海量数据处理 ├── 分治思想:大数据分解为小数据? ├── 哈希分片:数据均匀分布到多台机器? ├── 位图法:判断存在性,节省空间? ├:堆的应用:Top K问题? └:外部排序:内存不足时如何排序?
09-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值