articleList

第26章 大厂业务高频的数据结构和算法介绍

2025/04/11 posted in  数据结构和算法
Tags: 

第 1 集 架构大课核心业务数据结构内容安排

简介: 架构大课核心业务数据结构内容安排

  • 目标

    • 掌握多个互联网中间件和存储用的核心数据结构,非基础刷题算法
    • 主要内容包括不限于:
      • 海量数据处理算法、排序算法、流控算法、缓存算法、负载均衡算法
      • BitMap、BloomFilter、B-Tree、 B+Tree 等
    • 架构大课 不单纯讲数据结构,而是结合当下中间件底层进行
      • 常规数组、链表、队列、栈 就不细讲了,过于基础
      • 大学刚学数据结构的时候,只有理论更多,却不知道如何应用
      • 现在再学数据结构,大家已经掌握了很多中间件,但入手也是难题
      • 这个时候就是 进阶数据结构和算法 最终实践应用,也就是底层实现原理
      • 比如 B-Tree 和 B+Tree 的区别,在 mysql 里面为啥选择 B+Tree 等
      • 一步步进阶【对比和思考】不一样的角度
        • 例如
          • 二叉树->二叉查找树->平衡二叉树 AVL->红黑树
          • B 树(B-Tree)mongodb 底层 ->B+树(Mysql 索引底层)
        • 不会这些【树数据结构】,这边你也能学会
  • 课程安排

    • 回顾基础:时间、空间复杂度、常见基础数据结构(有专题课程,小滴课堂官网上)
    • 正式内容:进阶数据结构和算法
  • 掌握程度

    • 部分数据结构算法:要求掌握编码实现(面试上机实现和开发项目使用)
    • 部分数据结构算法:要求掌握大体原理和优缺点(面试问答)
  • 如何看待刷题算法这个是否重要和怎么提升

    • 会算法的同学忽略这个哈

    • 主要是不会算法的同学怎么快速提升和时间分配

      • 算法题核心面试的公司:字节、微信、华为等

        • 这个情况,只能少投递,或者刷题目了
        • 但常规有些一线大厂会卡这个算法题,但是多数都不会卡
        • 更何况还有二三线互联网大厂也基本不卡,要求没那么高
      • 算法题非核心面试的公司:如阿里、美团、京东、腾讯、平安、58 等等

        • 都是技术基础(面试八股文)+项目为主
        • 算法题会有少数,但没做出或者思路有误,多数不卡
        • 因为是综合评估多个【技术点】: 语言基础、框架、网络、数据库、项目、沟通能力等掌握情况
      • 时间分配

        • 如果除了【算法】外,【技术点】都掌握差不多
          • 则可以去补下算法题,刷下 LeetCode
          • 面试前 1~2 个月每天 3~5 题,但期间也要定时复习【技术点】
        • 如果【算法】和 【技术点】都不牢固,优先把【技术点】补充好,因为选择更多
      • 23 年下半年我们小滴课堂会有专门针对《LeetCode 刷题算法大课》

        • 好比高中数学题,做多刷多灵感自然有了,题型也就那些
  • 说个趣事

    • 很多大厂内部每年都会举办些算法比赛
    • 以我这边多年经历告诉大家
      • 算法题这个一般都是临时刷题找灵感才行
      • 【多数同学】都是专注业务开发和中间件研发等,日常工作用少就生疏了
      • 举办过多次比赛,3 人一组,我也经常参加,队友是 P7 算法专家,还有另外一个业务开发同事
      • 5 道题目 2 小时,我们经常一道题都没做出来,另外不止我们组是这样,多数组都是题没做出来!!!
      • 所以不用怀疑自己的能力和智商

第 2 集 常见基础数据结构案例串讲回顾

简介: 常见基础数据结构案例串讲回顾

  • 程序 = 数据结构 + 算法

  • 数据结构(data structure)

    • 数据结构指的是相互之间有一种或者是多种特定的关系数据元素集合。
    • 大白话:计算机在对数据进行存储时候并不是杂乱没有顺序的,而是具有一定的规则
    image-20230205170616097
  • 算法(Algorithm)

    • 算法是被计算机使用来解决问题的方法,就对于程序而言,算法就是程序的灵魂
    • 优秀的程序可以在面对大量数据计算时,依旧能够保持高速的计算
    • 大白话: 算法就是一种解决特定问题的思路,决定程序的功能和效率
      • 冒泡、选择、排序、快速、归并、堆排序、LRU、LFU、轮训、随机、哈希等
  • 算法复杂度

    • 对于一个问题的处理一般有多个算法,不同算法消耗的时间一般是不同的;运行过程占用的空间资源也是不同的

    • 根据业务需求情况,会从时间和空间两两方面进行取舍,目标是 “快” 和 “省”

    • 时间复杂度空间复杂度 这两方面衡量算法的性能

      • 时间复杂度

        • 是指算法执行所需要的时间复杂度,也就是算法执行所消耗的时间,它反映的是算法的执行效率

        • 它衡量的是算法的运行时间随数据规模的增长而增长的程度

        • 它用大 O 表示法来表示,其中 O(n)表示算法的时间复杂度为线性时间

        • 下表格是 从上至下依次的时间复杂度越来越大,执行的效率越来越低

          • 技巧:计算循环执行次数最多的代码
      复杂度 概述
      常数阶 O(1) 没有循环等复杂结构,不因某个变量的增长而增长
      对数阶段 O(logN) 消耗的时间是 logN 增长,性能影响不大
      线性阶 O(n) 消耗的时间是随着 n 的变化而变化的,常见的是单层循环
      平方阶 O(n²) 把 O(n) 的代码再嵌套循环一遍,常见的是双层循环
      立方阶 O(n³) 和平方阶 O(n²)类似,O(n³)相当于三层 n 循环
      指数阶(2^n) 随着 n 增长,运算时间是指数式增长
      • 空间复杂度
        • 空间复杂度是指算法在计算机内所耗费的存储空间大小
        • 它衡量的是算法在计算机内存储占用的空间随数据规模增长而增长的程度
        • 它用大 O 表示法来表示,其中 O(1)表示算法的空间复杂度为常数空间
        • 常见的空间复杂度有 O(1),O(logn),O(n),O(nlogn),O(n2),O(n2logn),O(n3)等等
      复杂度 概述
      常数阶 O(1) 算法执行所需要的临时空间不随着某个变量 n 的大小而变化
      对数阶段 O(logN) 消耗的空间是 logN 增长,性能影响不大
      线性阶 O(n) 消耗的空间是随着 n 的变化而变化的,常见的是单层循环
      平方阶 O(n²) 把 O(n) 的代码再嵌套循环一遍,常见的是双层循环
      立方阶 O(n³) 和平方阶 O(n²)类似,O(n³)相当于三层 n 循环
      指数阶(2^n) 随着 n 增长,运算所需空间是指数式增长
    • 例子(学完本大板块中你自然会解决)

      • 一百亿个 id 数据里面,给一个 id 如何判断是否存在里面
      • 100 亿个浮点数据,从中找出最大的 1000 个数字
      • ...
    • 建议

      • 空间复杂度相对简单,一般对程序复杂度的分析,重点都会放在时间复杂度

      • 现在存储资源便宜,日常开发中会利用空间来换时间,比如缓存技术,数据库冗余设计

      • 不同的数据结构没有说一定哪个好,哪个不好,各有所长

      • 不同的编程语言实现数据结构和算法都可以,选择自己熟悉的即可,我们这边就选择 java 语言

第 3 集 基础数据结构编码实现-栈

简介: 基础数据结构编码实现-栈

  • 背景

    • 本章快速复习编码实现常见的几个基本数据结构,方便后续进阶使用
    • 也是招聘面试里面,基础的上机编写题目
  • 需求

    • 栈本身有多种,实现方式也有多种
    • java 语言编码实现,基于数组实现栈
  • 编码

    public class ArrayStack {
        private int[] arr;
        private int top;
    
        public ArrayStack(int capacity) {
            arr = new int[capacity];
            top = -1;
        }
    
        // 入栈
        public void push(int value) {
            if (top == arr.length - 1) {
                throw new RuntimeException("栈已满,无法入栈");
            }
            // ++top 会先执行
            arr[++top] = value;
        }
    
        // 出栈
        public int pop() {
            if (top == -1) {
                throw new RuntimeException("栈为空,无法出栈");
            }
            // top-- 会后执行
            return arr[top--];
        }
    
        // 查看栈顶元素
        public int peek() {
            if (top == -1) {
                throw new RuntimeException("栈为空,无法查看栈顶元素");
            }
            return arr[top];
        }
    }
    
  • 应用

    • 函数调用栈,每进入一个函数,会有一个栈帧进入压栈;函数执行完后,则对应的栈帧出栈

第 4 集 基础数据结构编码实现-队列

简介: 基础数据结构编码实现-队列

  • 需求

    • 队列实现方式也有多种
    • java 语言编码实现,基于数组实现队列
  • 编码

    public class MyQueue {
    
        /**
         * 数组存放数据
         */
        public int [] array;
        /**
         * 数组长度
         */
        private int length;
        private int head;
        private int tail;
        /**
         * 队列当前元素个数
         */
        private int size;
    
        public MyQueue(int length){
            array = new int[length];
            this.length = length;
            head = 0;
            tail = -1;
            size = 0;
        }
    
    
        /**
         * 入队
         * @param value
         * @return
         */
        public boolean enQueue(int value){
            //如果队列满,则返回false
            if(size == length){
                System.err.println("队列满了,入队失败");
                return false;
            }
            //将元素放到队尾
            array[++tail] = value;
            //元素个数增加1
            size++;
            return true;
        }
    
        /**
         * 出队,将队头的元素取出
         * @return
         */
        public int deQueue(){
            //如果队列为空,则返回-1
            if(size == 0){
                return -1;
            }
            //取出队头的元素
            int value = array[head++];
            //元素个数 减1
            size--;
            return value;
        }
    
        /**
         * 获取队列长度里面的元素数量
         * @return
         */
        public int getQueueSize(){
            return this.size;
        }
    
    }
    

第 5 集 基础数据结构编码实现-链表

简介: 基础数据结构编码实现-链表

  • 需求

    • 链表本身有多种,实现方式也有多种
    • java 语言编码实现,单向链表

    image-20230206145530079

  • 编码

    public class LinkedList {
    
        Node head;
        public LinkedList() {
            this.head = null;
        }
        // 向链表中添加元素,尾部添加
        public void add(int element) {
            Node newNode = new Node(element);
            if (head == null) {
                head = newNode;
                return;
            }
            //临时节点记录头部节点,找到最后一个节点,尾插法
            Node temp = head;
            while (temp.next != null) {
                temp = temp.next;
            }
            temp.next = newNode;
        }
        // 从链表中删除元素
        public void delete(int element) {
            if (head == null) {
                return;
            }
            //第一个节点刚好是查找的值,则第二个节点成为头部节点
            if (head.val == element) {
                head = head.next;
                return;
            }
            //遍历链表查找元素
            Node temp = head;
            while (temp.next != null && temp.next.val != element) {
                temp = temp.next;
            }
            if (temp.next != null && temp.next.val == element) {
                temp.next = temp.next.next;
            }
        }
    
        // 打印链表中的元素
        public void printList() {
            Node temp = head;
            while (temp != null) {
                System.out.println(temp.val);
                temp = temp.next;
            }
        }
    }
    
    class Node {
    
        int val;
        Node next;
        public Node(int val) {
            this.val = val;
        }
    }