大数据相加_2020 BAT大厂数据挖掘面试经验:“高频面经”之数据结构与算法篇...

​ 注:数据结构与算法为面试基础,基本上所有岗位都有涉及,面试中侧重核心思路阐述和手撕代码。以下试题为作者日常整理的通用高频面经,包含题目,答案与参考文章,欢迎纠正与补充。

目录

1.什么是链表、队列、堆栈、树图?

2.删除链表中重复的节点(剑指offer 83)

3.两数相加(Leetcode 2)

4.反转链表、环形链表、合并链表

5.创建包含min函数的栈

6.二叉树的最大(最小)树深

7.二叉树的遍历

8.通过前序和中序推后序(重建二叉树)

9.二叉树的最近公共祖先(leetcode 236)

10.电话号码的字母组合(leetcode 17)

11.求1+2+...+n(剑指offer 47)

12.有效括号(leetcode 20)

13.最长公共前缀(leetcode 14)

14.排序算法有哪些?

15.快速排序实现

16.求TopK(堆排序)

17.01背包(动态规划)

18.数据流中的中位数(剑指offer 63)

19.买卖股票的最佳时机(leetcode 121)

20.矩阵中的最短路径(剑指offer 65)


1.什么是链表、队列、堆栈、树图?

链表:创建链表的过程和创建数组的过程不同,不会先划出一块连续的内存。因为链表中的数据是不连续的,链表在存储数据的内存中有两块区域,一块区域用来存储数据,一块区域用来记录下一个数据保存在哪里(指向下一个数据的指针)。当有数据进入链表时候,会根据指针找到下一个存储数据的位置,然后把数据保存起来,然后再指向下一个存储数据的位置。这样链表就把一些碎片空间利用起来了,虽然链表是线性表,但是并不会按线性的顺序存储数据。

5f211244fba61d3b973fdf058bf53de6.png

队列:队列是一种先进先出的数据结构,数组和链表也都可以生成队列。当数据进入到队列中时也是先进入的在下面后进入的再上面,但是出队列的时候是先从下面出,然后才是上面的数据出,最晚进入的队列的,最后出。

e1bb8ae675946cba6e69798f28eeee2d.png

堆:堆是一颗完全二叉树。在这棵树中,所有父节点都满足大于等于其子节点的堆叫大根堆。所有父节点都满足小于等于其子节点的堆叫小根堆。堆虽然是一颗树,但是通常存放在一个数组中,父节点和孩子节点的父子关系通过数组下标来确定。

bf0a24f25014c94ba98e9a35a5bd9f48.png

栈:栈是一种先进后出的数据结构,数组和链表都可以生成栈。当数据进入到栈时会按照规则压入到栈的底部,再次进入的数据会压在第一次的数据上面,以此类推,在取出栈中的数据的时候会先取出最上面的数据,所以是先进后出。

由于数组和链表都可以组成栈,所以操作特点就需要看栈是由数组还是链表生成的了,然后就会继承相应的操作特点。

f6dc4943b391c7102eef020148b00043.png

树: 此处树特指二叉树(BinaryTree)。二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树右子树的二叉树组成。二叉树的特点:

  • 每个结点最多有两棵子树。(注意:不是都需要两棵子树,而是最多可以是两棵,没有子树或者有一棵子树也都是可以的。)
  • 左子树和右子树是有顺序的,次序不能颠倒。
  • 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树,下面是完全不同的二叉树:
fc7555f3d0bb52c0ad57f15e09f853c7.png

图:图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。图有各种形状和大小。边可以有权重(weight),即每一条边会被分配一个正数或者负数值。

ecca4d6da501466e09a6b98631f0cccd.png

2.删除链表中重复的节点(剑指offer 83)

递归解法:

# -*- coding:utf-8 -*-# class ListNode:#     def __init__(self, x):#         self.val = x#         self.next = Noneclass Solution:  # 递归    def deleteDuplication(self, pHead):        # write code here        if pHead == None:            return None        if pHead.next == None:            return pHead        if pHead.val != pHead.next.val:            pHead.next = self.deleteDuplication(pHead.next)            return pHead   # 后面的节点递归结束后,返回pHead即可        else:            tempNode = pHead            while tempNode and tempNode.val == pHead.val:                tempNode = tempNode.next            return self.deleteDuplication(tempNode)              # 重复节点都不留,不保留pHead,直接返回下一个不同节点的递归结点。

循环解法:

# -*- coding:utf-8 -*-# class ListNode:#     def __init__(self, x):#         self.val = x#         self.next = Noneclass Solution:    def deleteDuplication(self, pHead):        # write code here        first = ListNode(-1)     # 为了避免重复,在链表开始之前新建一个头结点。        first.next = pHead        curr = pHead   # 作为遍历链表的指针        pre = first    # 记录不重复节点之前的最后信息        while curr and curr.next:            if curr.val != curr.next.val:  # 当前节点不重复,继续往下走                curr = curr.next                pre = pre.next            else:      # 如果重复,找到不重复节点为止。                val = curr.val                while curr and curr.val == val:                    curr = curr.next                pre.next = curr                   # 这里直接令pre.next等于第一个与当前元素不重复的节点即可,                # 不用管这个节点也是重复节点,因为pre一定不重复,且被固定了下来,                # 是不变的,如果这个节点也是重复节点,pre.next会再次更新。        return first.next

3.两数相加(Leetcode 2)

给出两个非空的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储一位数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)

输出:7 -> 0 -> 8

原因:342 + 465 = 807

# Definition for singly-linked list.# class ListNode:#     def __init__(self, x):#         self.val = x#         self.next = None​class Solution:    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:        result = ListNode(0)        result_tail = result        carry = 0        while l1 or l2 or carry:            v1 = l1.val if l1 else 0            v2 = l2.val if l2 else 0            num = v1 + v2 + carry            carry = num // 10            result_tail.next = ListNode(num % 10)            result_tail = result_tail.next            l1 = l1.next if l1 else None            l2 = l2.next if l2 else None        return result.next

4.反转链表、环形链表、合并链表

反转链表(Leetcode 206):

# Definition for singly-linked list.# class ListNode:#     def __init__(self, x):#         self.val = x#         self.next = None​class Solution:    def reverseList(self, head):        if not head:            return None        last = None        while head:            tmp = head.next            head.next = last            last = head            head = tmp        return last

环形链表(leetcode 141):

# Definition for singly-linked list.# class ListNode(object):#     def __init__(self, x):#         self.val = x#         self.next = None​class Solution(object):    def hasCycle(self, head):        """        :type head: ListNode        :rtype: bool        """        if head is None or head.next is None or head.next.next is None:            return False        slow = head.next        fast = head.next.next        while slow != fast and fast is not None and fast.next is not None:            slow = slow.next            fast = fast.next.next        if fast == slow:            return True        else:            return False

合并链表(leetcode 21):

class Solution(object):    def mergeTwoLists(self, l1, l2):        """        :type l1: ListNode        :type l2: ListNode        :rtype: ListNode        """        if l1 == None:            return l2        if l2 == None:            return l1        dummy = ListNode(-1)        cur = dummy        while l1 and l2:            if l1.val < l2.val:                cur.next = l1                l1 = l1.next            else:                cur.next = l2                l2 = l2.next            cur = cur.next        if l1:            cur.next = l1        else:            cur.next = l2        return dummy.next

5.创建包含min函数的栈

# -*- coding:utf-8 -*-​class Solution:    def __init__(self):        self.stack = []        self.minstack = []        self.minm = float('inf')    def push(self, node):        # write code here        if node < self.minm:            self.minm = node            self.minstack.append(self.minm)        self.stack.append(node)    def pop(self):        # write code here        if self.stack != []:            if self.stack[-1] == self.minm:                self.minstack.pop()            self.stack.pop(-1)    def top(self):        # write code here        if self.stack != []:            return self.stack[-1]        else:            return None    def min(self):        # write code here        return self.minstack[-1]

6.二叉树的最大(最小)树深

二叉树的最大树深:

class Solution:    def maxDepth(self, root):        """        :type root: TreeNode        :rtype: int        """         if root is None:             return 0         else:             left_height = self.maxDepth(root.left)             right_height = self.maxDepth(root.right)             return max(left_height, right_height) + 1 

二叉树的最小树深:

# Definition for singly-linked list.# class ListNode:#     def __init__(self, x):#         self.val = x#         self.next = Noneclass Solution:  def minDepth(self, root):      """      :type root: TreeNode      :rtype: int      """      # 边界条件      if not root:        return 0      if not root.left and root.right is not None:        return self.minDepth(root.right)+1      if root.left is not None and not root.right:        return self.minDepth(root.left)+1      # 递归求解左子树和右子树最小深度      left = self.minDepth(root.left)+1      right = self.minDepth(root.right)+1      return min(left,right)

7.二叉树的遍历

二叉树的层次遍历:

# Definition for a binary tree node.# class TreeNode(object):#     def __init__(self, x):#         self.val = x#         self.left = None#         self.right = None class Solution(object):    def levelOrder(self, root):        """        :type root: TreeNode        :rtype: List[List[int]]        """        queue = [root]        res = []        if not root:            return []        while queue:            templst = []            templen = len(queue)            for i in range(templen):                temp = queue.pop(0)                templst.append(temp.val)                if temp.left:                    queue.append(temp.left)                if temp.right:                    queue.append(temp.right)            res.append(templst)        return res

二叉树的前(中、后)序遍历:

# Definition for a binary tree node.# class TreeNode(object):#     def __init__(self, x):#         self.val = x#         self.left = None#         self.right = None​class Solution(object):    def preorderTraversal(self, root):        """        :type root: TreeNode        :rtype: List[int]        """        if not root:            return []        res = []        # 前序遍历# -----------------------------------------------        res.append(root.val)        res.extend(self.preorderTraversal(root.left))        res.extend(self.preorderTraversal(root.right))# -----------------------------------------------        # 中序遍历# -----------------------------------------------        # res.extend(self.preorderTraversal(root.left))        # res.append(root.val)        # res.extend(self.preorderTraversal(root.right))# -----------------------------------------------        # 后序遍历# -----------------------------------------------        # res.extend(self.preorderTraversal(root.left))        # res.extend(self.preorderTraversal(root.right))        # res.append(root.val)# -----------------------------------------------        return res      

8.通过前序和中序推后序(重建二叉树)

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

class TreeNode:    def __init__(self, x):        self.val = x        self.left = None        self.right = Noneclass Solution:    # 返回构造的TreeNode根节点    def reConstructBinaryTree(self, pre, tin):        # write code here        if len(pre)==0:            return None        root=TreeNode(pre[0])        TinIndex=tin.index(pre[0])        root.left=self.reConstructBinaryTree(pre[1:TinIndex+1], tin[0:TinIndex])        root.right=self.reConstructBinaryTree(pre[TinIndex+1:], tin[TinIndex+1:])        return root    def PostTraversal(self,root):  #后序遍历        if root != None:            self.PostTraversal(root.left)            self.PostTraversal(root.right)            print(root.val)

9.二叉树的最近公共祖先(leetcode 236)

class Solution:    def letterCombinations(self, digits: str) -> List[str]:        if not digits:            return []        L1 = [""]        L2 = []        h = {"2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno",            "7": "pqrs", "8": "tuv", "9": "wxyz"}        for char in digits:            L1 = [each + i for each in L1 for i in h[char]]        return L1

10.电话号码的字母组合(leetcode 17)

class Solution:    def letterCombinations(self, digits: str) -> List[str]:        if not digits:            return []        L1 = [""]        L2 = []        h = {"2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno",            "7": "pqrs", "8": "tuv", "9": "wxyz"}        for char in digits:            L1 = [each + i for each in L1 for i in h[char]]        return L1

11.求1+2+...+n(剑指offer 47)

要求:不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)

思路:使用递归f(n) = f(n-1) + n, 但是不能使用if进行递归出口的控制,因此利用python中and的属性,即and判断都为真的话输出and后面的那个数字

class Solution(object):    def longestCommonPrefix(self, strs):        """        :type strs: List[str]        :rtype: str        """        if not strs:            return ""        for i in range(len(strs[0])):            for str in strs:                if len(str) <= i or strs[0][i] != str[i]:                    return strs[0][:i]        return strs[0]

12.有效括号(leetcode 20)

class Solution(object):    def longestCommonPrefix(self, strs):        """        :type strs: List[str]        :rtype: str        """        if not strs:            return ""        for i in range(len(strs[0])):            for str in strs:                if len(str) <= i or strs[0][i] != str[i]:                    return strs[0][:i]        return strs[0]

13.最长公共前缀(leetcode 14)

class Solution(object):    def longestCommonPrefix(self, strs):        """        :type strs: List[str]        :rtype: str        """        if not strs:            return ""        for i in range(len(strs[0])):            for str in strs:                if len(str) <= i or strs[0][i] != str[i]:                    return strs[0][:i]        return strs[0]

14.排序算法有哪些?

73875aa545d12915867558907d9bc3ea.png

15.快速排序实现

class Solution:    def GetLeastNumbers_Solution(self, tinput, k):        n = len(tinput)        if k <= 0 or k > n:            return list()        # 建立大顶堆        for i in range(int(k / 2) - 1, -1, -1):            self.heapAjust(tinput, i, k - 1)        for i in range(k, n):            if tinput[i] < tinput[0]:                tinput[0], tinput[i] = tinput[i], tinput[0]                # 调整前k个数                self.heapAjust(tinput, 0, k - 1)        print(tinput[:k])     def heapAjust(self, nums, start, end):        temp = nums[start]        # 记录较大的那个孩子下标        child = 2 * start + 1        while child <= end:            # 比较左右孩子,记录较大的那个            if child + 1 <= end and nums[child] < nums[child + 1]:                # 如果右孩子比较大,下标往右移                child += 1            # 如果根已经比左右孩子都大了,直接退出            if temp >= nums[child]:                break            # 如果根小于某个孩子,将较大值提到根位置            nums[start] = nums[child]            # nums[start], nums[child] = nums[child], nums[start]            # 接着比较被降下去是否符合要求,此时的根下标为原来被换上去的那个孩子下标            start = child            # 孩子下标也要下降一层            child = child * 2 + 1        # 最后将一开始的根值放入合适的位置(如果前面是交换,这句就不要)        nums[start] = temp

16.求TopK(堆排序)

# DC分治算法def max_profit_dc(prices):  len_prices = len(prices)  if len_prices <= 1:       # 边界条件    return 0  mid = len_prices//2  prices_left = prices[:mid]  prices_right = prices[mid:]​  maxProfit_left = max_profit_dc(prices_left)    # 递归求解左边序列  maxProfit_right = max_profit_dc(prices_right)  # 递归求解右边序列  maxProfit_left_right = max(prices_right) - min(prices_left)    #跨界情况    return max(maxProfit_left,maxProfit_right,maxProfit_left_right)

17.01背包(动态规划)

import numpy as np# 0-1 背包def oneZeroPack(w,v,c):    dp = np.zeros((len(w),(c+1)),dtype=np.int32)    for i in range(len(w)):        for j in range(c+1):            if i == 0:                dp[i][j] = v[i] if j >= w[i] else 0            else:                if j >= w[i]:                    dp[i][j] = max(dp[i-1][j-w[i]]+v[i], dp[i-1][j])                else:                    dp[i][j] = dp[i-1][j]    return dp[-1][-1]# 0-1 背包省空间的办法def oneZeroPack2(w,v,c):    dp = np.array([0]*(c+1),dtype=np.int32)    for i in range(len(w)):        for j in range(c,-1,-1):            if j >= w[i]:                dp[j] = max(dp[j-w[i]]+v[i], dp[j])    return dp[-1]

18.数据流中的中位数(剑指offer 63)

限制:n个数组无法完全放在内存中

# DC分治算法def max_profit_dc(prices):  len_prices = len(prices)  if len_prices <= 1:       # 边界条件    return 0  mid = len_prices//2  prices_left = prices[:mid]  prices_right = prices[mid:]​  maxProfit_left = max_profit_dc(prices_left)    # 递归求解左边序列  maxProfit_right = max_profit_dc(prices_right)  # 递归求解右边序列  maxProfit_left_right = max(prices_right) - min(prices_left)    #跨界情况    return max(maxProfit_left,maxProfit_right,maxProfit_left_right)

19.买卖股票的最佳时机(leetcode 121)

class Solution:    def hasPath(self, matrix, rows, cols, path):        assistMatrix = [True]*rows*cols        for i in range(rows):            for j in range(cols):                if(self.hasPathAtAStartPoint(matrix,rows,cols, i, j, path, assistMatrix)):                    return True        return False​    def hasPathAtAStartPoint(self, matrix, rows, cols, i, j, path, assistMatrix):        if not path:            return True        index = i*cols+j        if i<0 or i>=rows or j<0 or j>=cols or matrix[index]!=path[0] or assistMatrix[index]==False:            return False        assistMatrix[index] = False        if(self.hasPathAtAStartPoint(matrix,rows,cols,i+1,j,path[1:],assistMatrix) or               self.hasPathAtAStartPoint(matrix,rows,cols,i-1,j,path[1:],assistMatrix) or               self.hasPathAtAStartPoint(matrix,rows,cols,i,j-1,path[1:],assistMatrix) or               self.hasPathAtAStartPoint(matrix,rows,cols,i,j+1,path[1:],assistMatrix)):            return True        assistMatrix[index] = True        return False

20.矩阵中的最短路径(剑指offer 65)

class Solution:    def hasPath(self, matrix, rows, cols, path):        assistMatrix = [True]*rows*cols        for i in range(rows):            for j in range(cols):                if(self.hasPathAtAStartPoint(matrix,rows,cols, i, j, path, assistMatrix)):                    return True        return False​    def hasPathAtAStartPoint(self, matrix, rows, cols, i, j, path, assistMatrix):        if not path:            return True        index = i*cols+j        if i<0 or i>=rows or j<0 or j>=cols or matrix[index]!=path[0] or assistMatrix[index]==False:            return False        assistMatrix[index] = False        if(self.hasPathAtAStartPoint(matrix,rows,cols,i+1,j,path[1:],assistMatrix) or               self.hasPathAtAStartPoint(matrix,rows,cols,i-1,j,path[1:],assistMatrix) or               self.hasPathAtAStartPoint(matrix,rows,cols,i,j-1,path[1:],assistMatrix) or               self.hasPathAtAStartPoint(matrix,rows,cols,i,j+1,path[1:],assistMatrix)):            return True        assistMatrix[index] = True        return False

参考链接:

https://www.cnblogs.com/jimoer/p/8783604.html

https://blog.csdn.net/ggdhs/article/details/90447401

https://blog.csdn.net/songyunli1111/article/details/80138757

https://blog.csdn.net/wfq784967698/article/details/79551476

https://blog.csdn.net/weixin_43250623/article/details/88931925

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值