您的位置:首页 > 理论基础 > 数据结构算法

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

2020-03-20 12:07 603 查看

目录

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

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

3.两数相加(Leetcode2)

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

5.创建包含min函数的栈

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

7.二叉树的遍历

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

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

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

11.求1+2+...+n(剑指offer47)

12.有效括号(leetcode 20)

13.最长公共前缀(leetcode14)

14.排序算法有哪些?

15.快速排序实现

16.求TopK(堆排序)

17.01背包(动态规划)

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

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

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

 

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

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

 

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

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

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

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

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

  • 每个结点最多有两棵子树。(注意:不是都需要两棵子树,而是最多可以是两棵,没有子树或者有一棵子树也都是可以的。)

  • 左子树和右子树是有顺序的,次序不能颠倒。

  • 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树,下面是完全不同的二叉树:

                        

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

    

    参考链接:

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

 

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

    递归解法:

#-*-coding:utf-8-*-
#classListNode:
#def__init__(self,x):
#self.val=x
#self.next=None
classSolution:#递归
defdeleteDuplication(self,pHead):
#writecodehere
ifpHead==None:
returnNone
ifpHead.next==None:
returnpHead
ifpHead.val!=pHead.next.val:
pHead.next=self.deleteDuplication(pHead.next)
returnpHead#后面的节点递归结束后,返回pHead即可
else:
tempNode=pHead
whiletempNodeandtempNode.val==pHead.val:
tempNode=tempNode.next
returnself.deleteDuplication(tempNode)
            # 重复节点都不留,不保留pHead,直接返回下一个不同节点的递归结点。
[/code]

    循环解法:

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

    参考链接:

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

 

3.两数相加(Leetcode2)

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

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

    示例:

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

    输出:7->0->8

    原因:342+465=807

#Definitionforsingly-linkedlist.
#classListNode:
#def__init__(self,x):
#self.val=x
#self.next=None
classSolution:
defaddTwoNumbers(self,l1:ListNode,l2:ListNode)->ListNode:
result=ListNode(0)
result_tail=result
carry=0
whilel1orl2orcarry:
v1=l1.valifl1else0
v2=l2.valifl2else0
num=v1+v2+carry
carry=num//10
result_tail.next=ListNode(num%10)
result_tail=result_tail.next
l1=l1.nextifl1elseNone
l2=l2.nextifl2elseNone
returnresult.next
[/code]

 

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

  反转链表(Leetcode206):

#Definitionforsingly-linkedlist.
#classListNode:
#def__init__(self,x):
#self.val=x
#self.next=None
classSolution:
defreverseList(self,head):
ifnothead:
returnNone
last=None
whilehead:
tmp=head.next
head.next=last
last=head
head=tmp
returnlast
[/code]

    环形链表(leetcode141):

#Definitionforsingly-linkedlist.
#classListNode(object):
#def__init__(self,x):
#self.val=x
#self.next=None
classSolution(object):
defhasCycle(self,head):
"""
:typehead:ListNode
:rtype:bool
"""
ifheadisNoneorhead.nextisNoneorhead.next.nextisNone:
returnFalse
slow=head.next
fast=head.next.next
whileslow!=fastandfastisnotNoneandfast.nextisnotNone:
slow=slow.next
fast=fast.next.next
iffast==slow:
returnTrue
else:
returnFalse
[/code]

    合并链表(leetcode21):

class Solution(object):
defmergeTwoLists(self,l1,l2):
"""
:typel1:ListNode
:typel2:ListNode
:rtype:ListNode
"""
ifl1==None:
returnl2
ifl2==None:
            return l1
dummy=ListNode(-1)
        cur = dummy
whilel1andl2:
ifl1.val<l2.val:
cur.next=l1
l1=l1.next
else:
cur.next=l2
l2=l2.next
            cur = cur.next
ifl1:
cur.next=l1
else:
cur.next=l2
        return dummy.next
[/code]

 

5.创建包含min函数的栈

#-*-coding:utf-8-*-
classSolution:
def__init__(self):
self.stack=[]
self.minstack=[]
self.minm=float('inf')
defpush(self,node):
#writecodehere
ifnode<self.minm:
self.minm=node
self.minstack.append(self.minm)
self.stack.append(node)
defpop(self):
#writecodehere
ifself.stack!=[]:
ifself.stack[-1]==self.minm:
self.minstack.pop()
self.stack.pop(-1)
deftop(self):
#writecodehere
ifself.stack!=[]:
returnself.stack[-1]
else:
returnNone
defmin(self):
#writecodehere
returnself.minstack[-1]
[/code]

 

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

    二叉树的最大树深:

classSolution:
defmaxDepth(self,root):
"""
:typeroot:TreeNode
:rtype:int
"""
ifrootisNone:
return0
else:
left_height=self.maxDepth(root.left)
right_height=self.maxDepth(root.right)
returnmax(left_height,right_height)+1
[/code]

    二叉树的最小树深:

#Definitionforsingly-linkedlist.
#classListNode:
#def__init__(self,x):
#self.val=x
#         self.next = None
classSolution:
defminDepth(self,root):
"""
:typeroot:TreeNode
:rtype:int
"""
      # 边界条件
ifnotroot:
return0
ifnotroot.leftandroot.rightisnotNone:
returnself.minDepth(root.right)+1
ifroot.leftisnotNoneandnotroot.right:
returnself.minDepth(root.left)+1
      #递归求解左子树和右子树最小深度
left=self.minDepth(root.left)+1
right=self.minDepth(root.right)+1
returnmin(left,right)
[/code]

 

7.二叉树的遍历

     二叉树的层次遍历:

#Definitionforabinarytreenode.
#classTreeNode(object):
#def__init__(self,x):
#self.val=x
#self.left=None
#self.right=None
classSolution(object):
deflevelOrder(self,root):
"""
:typeroot:TreeNode
:rtype:List[List[int]]
"""
queue=[root]
res=[]
ifnotroot:
return[]
whilequeue:
templst=[]
templen=len(queue)
foriinrange(templen):
temp=queue.pop(0)
templst.append(temp.val)
iftemp.left:
queue.append(temp.left)
iftemp.right:
queue.append(temp.right)
res.append(templst)
returnres
[/code]

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

#Definitionforabinarytreenode.
#classTreeNode(object):
#def__init__(self,x):
#self.val=x
#self.left=None
#self.right=None
classSolution(object):
defpreorderTraversal(self,root):
"""
:typeroot:TreeNode
:rtype:List[int]
"""
ifnotroot:
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      
[/code]

 

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

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

classTreeNode:
def__init__(self,x):
self.val=x
self.left=None
self.right=None
classSolution:
#返回构造的TreeNode根节点
defreConstructBinaryTree(self,pre,tin):
#writecodehere
iflen(pre)==0:
returnNone
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:])
returnroot
defPostTraversal(self,root):#后序遍历
ifroot!=None:
self.PostTraversal(root.left)
self.PostTraversal(root.right)
            print(root.val)
[/code]

    参考链接:

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

 

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

class Solution:
deflowestCommonAncestor(self,root,A,B):
#A&B=>LCA
#!A&!B=>None
#A&!B=>A
#B&!A=>B
#若root为空或者root为A或者root为B,说明找到了A和B其中一个
if(rootisNoneorroot==Aorroot==B):
returnroot
left=self.lowestCommonAncestor(root.left,A,B)
right=self.lowestCommonAncestor(root.right,A,B)
#若左子树找到了A,右子树找到了B,说明此时的root就是公共祖先
ifleftandright:
returnroot
#若左子树是none右子树不是,说明右子树找到了A或B
ifnotleft:
returnright
#若右子树是none左子树不是,说明左子树找到了A或B
ifnotright:
returnleft
returnNone
[/code]

 

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

classSolution:
defletterCombinations(self,digits:str)->List[str]:
ifnotdigits:
return[]
L1=[""]
L2=[]
h={"2":"abc","3":"def","4":"ghi","5":"jkl","6":"mno",
"7":"pqrs","8":"tuv","9":"wxyz"}
forcharindigits:
L1=[each+iforeachinL1foriinh[char]]
        return L1
[/code]

 

11.求1+2+...+n(剑指offer47)

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

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

classSolution:
defSum_Solution(self,n):
#writecodehere
ans=(n>0)andn
returnansandself.Sum_Solution(n-1)+ans
[/code]

 

12.有效括号(leetcode 20)

classSolution:
defisValid(self,s):
"""
:types:str
:rtype:bool
"""
leftP='([{'
rightP=')]}'
stack=[]
forcharins:
ifcharinleftP:
stack.append(char)
ifcharinrightP:
ifnotstack:
returnFalse
tmp=stack.pop()
ifchar==')'andtmp!='(':
returnFalse
ifchar==']'andtmp!='[':
returnFalse
ifchar=='}'andtmp!='{':
returnFalse
returnstack==[]
[/code]

 

13.最长公共前缀(leetcode14)

classSolution(object):
deflongestCommonPrefix(self,strs):
"""
:typestrs:List[str]
:rtype:str
"""
ifnotstrs:
return""
foriinrange(len(strs[0])):
forstrinstrs:
iflen(str)<=iorstrs[0][i]!=str[i]:
returnstrs[0][:i]
returnstrs[0]
[/code]

 

14.排序算法有哪些?

    参考链接:

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

 

15.快速排序实现

defquick_sort(alist,start,end):
"""快速排序"""
ifstart>=end:#递归的退出条件
return
mid=alist[start]#设定起始的基准元素
low=start#low为序列左边在开始位置的由左向右移动的游标
high=end#high为序列右边末尾位置的由右向左移动的游标
whilelow<high:
#如果low与high未重合,high(右边)指向的元素大于等于基准元素,则high向左移动
whilelow<highandalist[high]>=mid:
high-=1
alist[low]=alist[high]#走到此位置时high指向一个比基准元素小的元素,将high指向的元素放到low的位置上,此时high指向的位置空着,接下来移动low找到符合条件的元素放在此处
#如果low与high未重合,low指向的元素比基准元素小,则low向右移动
whilelow<highandalist[low]<mid:
low+=1
alist[high]=alist[low]#此时low指向一个比基准元素大的元素,将low指向的元素放到high空着的位置上,此时low指向的位置空着,之后进行下一次循环,将high找到符合条件的元素填到此处
#退出循环后,low与high重合,此时所指位置为基准元素的正确位置,左边的元素都比基准元素小,右边的元素都比基准元素大
alist[low]=mid#将基准元素放到该位置,
#对基准元素左边的子序列进行快速排序
quick_sort(alist,start,low-1)#start:0low-1原基准元素靠左边一位
#对基准元素右边的子序列进行快速排序
    quick_sort(alist, low + 1, end)  # low+1 : 原基准元素靠右一位  end: 最后
[/code]

    参考链接:

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

 

16.求TopK(堆排序)

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

 

17.01背包(动态规划)

import numpy as np
#0-1背包
defoneZeroPack(w,v,c):
dp=np.zeros((len(w),(c+1)),dtype=np.int32)
foriinrange(len(w)):
forjinrange(c+1):
ifi==0:
dp[i][j]=v[i]ifj>=w[i]else0
else:
ifj>=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]
returndp[-1][-1]
#0-1背包省空间的办法
defoneZeroPack2(w,v,c):
dp=np.array([0]*(c+1),dtype=np.int32)
foriinrange(len(w)):
forjinrange(c,-1,-1):
ifj>=w[i]:
                dp[j] = max(dp[j-w[i]]+v[i], dp[j])
returndp[-1]
[/code]

 

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

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

#-*-coding:utf-8-*-
importheapq
#-*-coding:utf-8-*-
classSolution:
def__init__(self):
self.nums=[]
defInsert(self,num):
#writecodehere
self.nums.append(num)
defGetMedian(self,n=None):
#writecodehere
self.nums.sort()
iflen(self.nums)%2==0:
res=(self.nums[len(self.nums)/2]+self.nums[len(self.nums)/2-1])/2.0
else:
res=self.nums[len(self.nums)//2]
returnres
[/code]

 

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

#DC分治算法
defmax_profit_dc(prices):
len_prices=len(prices)
iflen_prices<=1:#边界条件
return0
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)#跨界情况
returnmax(maxProfit_left,maxProfit_right,maxProfit_left_right)
[/code]

 

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

classSolution:
defhasPath(self,matrix,rows,cols,path):
assistMatrix=[True]*rows*cols
foriinrange(rows):
forjinrange(cols):
if(self.hasPathAtAStartPoint(matrix,rows,cols,i,j,path,assistMatrix)):
returnTrue
returnFalse
​
defhasPathAtAStartPoint(self,matrix,rows,cols,i,j,path,assistMatrix):
ifnotpath:
returnTrue
index=i*cols+j
ifi<0ori>=rowsorj<0orj>=colsormatrix[index]!=path[0]orassistMatrix[index]==False:
returnFalse
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)):
returnTrue
assistMatrix[index]=True
returnFalse
  • 点赞 2
  • 收藏
  • 分享
  • 文章举报
云祁° 发布了341篇原创文章·获赞370·访问量20万+ 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: