数据开发工程师-面试题

1、数据仓库的结构?

数据仓库是一个用于存储、管理和分析大规模数据的集中式数据存储系统。它的结构通常包括以下主要组件和层次。

  • 数据源:数据仓库的数据源包括各种不同的数据系统,如关系数据库、日志文件、外部API、云存储等。数据从这些源头采集、抽取、转换和加载到数据仓库种。
  • 数据抽取:在这个阶段,数据从各个数据源中提取出来,以备后续处理。抽取的数据可能包括原始数据、历史数据、事务数据等。
  • 数据转换:在数据仓库中,数据通常需要经过清洗、转换和整合,以满足分析和报告需求。数据转换的任务包括数据清洗、字段映射、数据合并、计算指标等。
  • 数据加载:转换后的数据被加载到数据仓库中的不同层次,通常包括原始数据存储、中间数据存储和数据仓库数据存储。数据加载过程可以是批处理或实时流处理。
  • 数据存储层次:数据仓库包括多个存储层次,其中包括①原始数据存储,保留未经处理的原始数据,以备不同的分析需求;②数据仓库存储,包含已经经过清洗、转换和整合的数据,用于执行高级分析和报告。③数据汇总存储:包含已经汇总和预计算的数据,用于提高查询性能和执行复杂的分析任务。
  • 元数据管理:数据仓库需要维护元数据,以描述存储在其中的数据的结构、来源、定义、变换规则等信息。元数据对于数据仓库的管理和查询优化非常重要。
  • 数据访问层:数据仓库提供不同的方式供用户和应用程序访问数据,包括SQL查询、OLAP(在线分析处理)工具、报告工具和API等。
  • 查询和分析工具:数据仓库通常于各种查询和分析工具集成,以支持用户进行数据探索、查询和报告。
  • 安全性和权限控制:数据仓库需要实施严格的安全行和权限控制,以确保只有经过授权的用户可以访问和操作数据。
  • 监控和性能优化:数据仓库需要实时监控,以确保性能和可用性。性能优化是一个持续的过程,包括索引、分区、缓存等技术。

2、python-逆序对取模

逆序对取模通常需要使用归并排序算法。逆序对在一个数组中,如果一个数比其后面的数大,则称这两个数构成一个逆序对。

def merge_sort(arr, mod):
    if len(arr) > 1:
        mid = len(arr) // 2
        left_half = arr[:mid]
        right_half = arr[mid:]

        left_half, left_inv_count = merge_sort(left_half, mod)
        right_half, right_inv_count = merge_sort(right_half, mod)

        merged_arr, split_inv_count = merge(left_half, right_half, mod)

        inv_count = left_inv_count + right_inv_count + split_inv_count
        return merged_arr, inv_count
    else:
        return arr, 0

def merge(left, right, mod):
    merged_arr = []
    inv_count = 0
    i = j = 0

    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            merged_arr.append(left[i])
            i += 1
        else:
            merged_arr.append(right[j])
            j += 1
            inv_count += len(left) - i

    merged_arr.extend(left[i:])
    merged_arr.extend(right[j:])

    return merged_arr, inv_count

def count_inverse_pairs(arr, mod):
    _, inv_count = merge_sort(arr, mod)
    return inv_count % mod

# 示例用法
arr = [2, 4, 1, 3, 5]
mod = 1000000007
result = count_inverse_pairs(arr, mod)
print("逆序对数取模结果:", result)

3、Python-合并两个已排序的数组

方法:使用双指针的方法,遍历两个输入数组,逐个比较元素,将较小的元素添加到新数组中。

def merge_sorted_arrays(arr1, arr2):
    merged_arr = []
    i = j = 0

    while i < len(arr1) and j < len(arr2):
        if arr1[i] < arr2[j]:
            merged_arr.append(arr1[i])
            i += 1
        else:
            merged_arr.append(arr2[j])
            j += 1

    # 将剩余的元素添加到新数组中
    while i < len(arr1):
        merged_arr.append(arr1[i])
        i += 1

    while j < len(arr2):
        merged_arr.append(arr2[j])
        j += 1

    return merged_arr

# 示例用法
arr1 = [1, 3, 5, 7]
arr2 = [2, 4, 6, 8]
result = merge_sorted_arrays(arr1, arr2)
print("合并后的已排序数组:", result)

4、Spark和Hadoop是大数据处理领域的两个重要框架,它们有一些异同点。

相同点:

  • 都为开源框架,可以免费使用和修改。
  • 两者都旨在处理大规模数据,采用分布式计算模型,能够在多台计算节点上并行执行任务。
  • 容错性:两者都具备容错性,能够处理节点故障,并保证任务的完成。
  • 支持多种数据源:两者都可以处理不同的数据源,包括结构化数据(如关系型数据)、半结构化数据(XML,JSON)和非结构化数据(文本、日志等)
  • 支持多种编程语言:包括Java、Python、Scala等,使开发者可以使用他们熟悉的语言进行大数据处理。

不同点:

  • 数据处理模型:①Hadoop使用MapReduce模型,这是一种批处理模型,适用于离线的数据处理。MapReduce任务通常需要将数据写入磁盘中间存储,因此对于迭代算法或实时处理来说效率较低。②spark使用基于内存的计算模型,可以更高效地进行迭代处理和实时处理,因为它可以将中间数据存储在内存中。
  • 性能:①Spark通常比Hadoop的Mapreduce更快,尤其在迭代算法(如机器学习)和实时数据处理方面。这是因为spark充分利用了内存计算,减少了磁盘IO。②Hadoop的MapReduce更适合一次性、批处理式的任务。
  • API和生态系统:①Spark提供了更多的高级API,包括支持SQL查询的Spark SQL、机器学习库MLib、图处理库GraphX等,使得开发更加方便。②Hadoop生态系统包括Hive(支持SQL查询、pig(数据流脚本语言)、HBase(NoSQL数据库)等,但需要更多的工具和库来实现类似的功能。
  • 复杂度:①Spark通常更容易使用,因为它提供了高级API和更友好的编程模型。②Hadoop的MapReduce需要更多的手工编码和配置。
  • 资源管理器:①Hadoop使用YARN(Yet Another Resource Negotiator)作为资源管理器,负责分配集群资源给不同的任务。②Spark自带了一个资源管理器,称为Cluster Manager,也可以与YARN和Mesos等外部资源管理器集成。

5、Java-二分查找

public int search(int[] nums, int left, int right, int target) {
        while (left <= right){
            int mid = left + (right - left) / 2;
            if (nums[mid] == target){
                return mid;
            }else if (nums[mid]< target){
                return search(num, mid + 1, right, target);
            }else{
                return search(num, left, mid - 1, target);
            }
        }
        return -1;
 }

6、Java-双指针

递增数组,判断数组中是否存在两个数之和为target,思路为双指针,一个begin,一个end,每次移动一个指针

public int[] twoSum(int[] numbers, int target) {
        int p1 = 0;
        int p2 = numbers.length - 1;
        while (p1 < p2){
            int sum = numbers[p1] + numbers[p2];
            if (sum == target){
                return new int[]{p1 + 1, p2 + 1};
            } else if (sum < target){
                p1++;
            } else {
                p2--;
            }
        }
        //无解
        return new int[]{-1, -1};
}

7、Java-最长递增子序列LIS(动态规划)

 给一个整数数组nums,找到其中最长严格递增子序列的长度。最优时间复杂度为O(nlogn)

public int lengthOfLIS(int[] nums) {
        int dp[] = new int[nums.length];
        dp[0] = 1;
        int maxSeqLen = 1;
        for (int i=1; i<nums.length; i++) {
            //初始化为1
            dp[i] = 1;
            for (int j = 0; j <= i; j++) {
                //对dp[i]进行更新,严格递增
                if (nums[i] > nums[j]){
                    dp[i] = Math.max(dp[i], dp[j]+1);
                }
            }
            maxSeqLen = Math.max(maxSeqLen, dp[i]);
        }
        return maxSeqLen;
}

8、逆序链表两数求和

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode pre = new ListNode(0);
        ListNode cur = pre;
        int carry = 0;
        while(l1 != null || l2 != null) {
            int x = l1 == null ? 0 : l1.val;
            int y = l2 == null ? 0 : l2.val;
            int sum = x + y + carry;
            
            carry = sum / 10;
            sum = sum % 10;
            cur.next = new ListNode(sum);

            cur = cur.next;
            if(l1 != null)
                l1 = l1.next;
            if(l2 != null)
                l2 = l2.next;
        }
        if(carry == 1) {
            cur.next = new ListNode(carry);
        }
        return pre.next;
    }
}

9、Mysql的三范式

  • 第一范式(1NF):数据表中的每一列都包含不可再分的原子数据,也就是每个单元格中只能存储一个值。所有的数据必须是原子的,不能包含集合、数组、嵌套表格等非原子数据。

  • 第二范式(2NF):数据表必须符合第一范式(1NF)。所有非主键列(非关键字列)都必须完全依赖于候选键(主键)。 这意味着没有部分依赖,即表中的每一列都应该与主键有关系,而不是只与主键的一部分有关。

  • 第三范式(3NF):数据表必须符合第二范式(2NF)。所有非主键列都不应该传递依赖于主键。换句话说,非主键列之间不应该存在传递依赖关系。如果一个非主键列依赖于另一个非主键列,而后者又依赖于主键,那么就存在传递依赖,这需要通过分解表来消除。非主键都和主键直接相关,不存在间接相关。

10、Mysql的InnoDB和MyIsam的区别

  • InnoDB支持事务,MyISAM不支持事务。
  • InnoDB支持外键
  • InnoDB是聚集索引,MyISAM是非聚集索引
  • InnoDB不支持具体的行数;InnoDB的最小锁的粒度是行锁,MyISAM的最小粒度是表锁。

11、MySQL的隔离级别

  • 未提交读:即未提交也读,事务中间也可以读,容易产生脏读、幻读、不可重复读。
  • 已提交读:只有在事务提交后才可以读,避免了脏读,但是无法避免不可重复读、幻读。
  • 可重复读:在事务提交的时候,不可以读和修改数据,避免了脏读和不可重复读。
  • 串行读:隔离级别最高,所有的事务都是串行化执行,避免了脏读、幻读和不可重复读。

猜你喜欢

转载自blog.csdn.net/qq_43687860/article/details/133217780