以下是python语言实现对应的一些基础的数据结构,面试时虽然不会直接考,但是很多数据结构由于平时不用,比如红黑树等,很多人甚至从来也没写过这些基础数据结构在对应语言中怎么写
队列:
队列的特性就是先进先出,在python中可以用数组来作为队列使用。
初始化队列:
queue = list()
进队列:
queue.append(i)
出队列:
queue.pop(0)
进队列时不用判断,出队列时得先判断队列是否不为空,否则会报错。
栈:
栈的特性是先入后出,在python中可以使用数组来作为栈使用
初始化栈:
stack = list()
入栈:
stack.append(i)
出栈:
stack.pop()
python中的list对应的pop方法,默认是pop最后一个值,参数用于指定list中的位置
链表:
python对应的基础数据结构中没有链表的,所以需要面向对象来进行解决
初始化一个单向链表的结点
class ListNode(object):
def __init__(self, x=None, next=None):
self.val = x
self.next = next
双向链表就是再加一个next,比如这样
class ListNode(object):
def __init__(self, x=None, left=None, right=None):
self.left = left
self.val = x
self.right = right
使用方法,举例:将一个list转换成一个单向链表并输出
# -*- coding:utf-8 -*-
class linkNode(object):
def __init__(self, x=None, next=None):
self.val = x
self.next = next
def func(nums):
pHead = linkNode()
pTemp = pHead
for i in nums:
node = linkNode(i)
pTemp.next = node
pTemp = pTemp.next
pTemp = pHead
while(pTemp):
print(pTemp.val)
pTemp = pTemp.next
func([1,2,3,4,5,6])
而为了应付面试,最少要熟练掌握节点查找插入删除;
节点的插入和删除是最基本的操作,当然面试不会直接考察,而是会嵌入到具体的算法体中处理。
二叉树:
二叉树的数据结构长的样子大家都明白,面试的话基础就是先中后序的遍历方法,二叉树为什么大家都喜欢考,原因可能是代码短小适合在面试短时间内考察,而且考这一个就顺手考察了链表,或者为了考而考,算法题中很多,但是实际上项目代码中使用的并不太多(红黑树那个梗)。
首先是二叉树的结点,就是上面双向链表的节点:
class ListNode(object):
def __init__(self, x=None, left=None, right=None):
self.left = left
self.val = x
self.right = right
因为本身二叉树就是一种递归的数据结构,创建和读取都可以用递归的方式完成
二叉树的特点就是
1.除了根节点,所有的节点都有父节点;
2.除了叶子节点,所有的节点都有子节点;
3.二叉树中,第 i 层最多有 2的i次方-1 个结点;
4.如果二叉树的深度为 K,那么此二叉树最多有 2的K次方-1 个结点;
5.
6.
简单了解下就好,下面重点就是这先中后序遍历的递归和非递归写法
先序遍历:
就是先访问节点,再访问左子节点,最后访问右子节点
比如这个
这个二叉树的遍历结果就是:
先序遍历:A-B-D-G-C-E-F
中序遍历:D-G-B-A-E-C-F
后序遍历:G-D-B-E-F-C-A
一个小问题:代码中如何创建一颗二叉树,二叉树的创建方法有很多,二叉树如果比较简单的话,通过手工创建即可,数量比较大或者在编程中遇到,还是需要去创建的,这里先简单提一种创建二叉树的方法
首先的问题是,根据一个先序遍历的结果是无法找到一个准确的二叉树的,所以这里需要把结果补充完整,比如这里的先序遍历结果是A-B-D-G-C-E-F,将叶子节点的左右空节点用#来表示,树中空出的节点也用#表示,就可以将对应的二叉树转换成一种扩展的二叉树,这里的话对应的扩展二叉树的先序遍历结果就是A-B-D-#-G-#-#-#-C-E-#-#-F-#-#
当然这种算法本身也是一种递归算法,所以就要找出它的对应的递归实现条件:
1. 递归的出口:结果中没有值了
2. 递归每次实现的功能是什么:创建一个根节点和两个子节点
3. 递归返回值:返回本层中创建好的root节点
所以二叉树的先序递归创建代码就是这样的:
class ListNode(object):
def __init__(self, x=None, left=None, right=None):
self.left = left
self.val = x
self.right = right
def newBinaryTree(root,nums):
if len(nums) == 0:
return
if nums[0] != "#":
root = ListNode(nums.pop(0))
root.left = newBinaryTree(root.left,nums)
root.right = newBinaryTree(root.right,nums)
return root
else:
root = None
nums.pop(0)
return root
nums = list("ABD#G###CE##F##")
root = newBinaryTree(None,nums)
创建好之后,我们可以用先中后序的方法依次遍历下这个创建好的二叉树,验证下上述代码创建的二叉树正确性
三种递归遍历
def preOrder(root): #先序遍历
if root == None:
return
print (root.val)
preOrder(root.left)
preOrder(root.right)
def inOrder(root): # 中序遍历
if root == None:
return
inOrder(root.left)
print (root.val)
inOrder(root.right)
def postOrder(root): # 后序遍历
if root == None:
return
inOrder(root.left)
inOrder(root.right)
print (root.val)
三种非递归遍历
def preOrderNorecur(root): # 先序遍历非递归
if root == None:
return
stack = [root]
while stack:
s = stack.pop()
if s:
print(s.val)
stack.append(s.right)
stack.append(s.left)
def inOrderNorecur(root): # 中序遍历非递归
if root == None:
return
stack = []
while stack or root:
while root:
stack.append(root)
root = root.left
root = stack.pop()
print(root.val)
root = root.right
def postOrderNorecur(root): # 后序遍历非递归
if root == None:
return
stack = []
while stack or root:
while root: # 下行循环,直到找到第一个叶子节点
stack.append(root)
if root.left: # 能左就左,不能左就右
root = root.left
else:
root = root.right
s = stack.pop()
print(s.val)
#如果当前节点是上一节点的左子节点,则遍历右子节点
if stack and s == stack[-1].left:
root = stack[-1].right
else:
root = None
还有一种层次遍历,一般面试不会问,可以了解一下,就是从上到下从左到右输出当前的二叉树,上图所示的二叉树的层次遍历结果就是:A-B-C-D-E-F-G
代码实现就是:
def BFS(root):
queue = [root]
while queue:
n = len(queue)
for i in range(n):
q = queue.pop(0)
if q:
print(q.val)
queue.append(q.left if q.left else None)
queue.append(q.right if q.right else None)
除了最上面说的通过扩展的方法来重建二叉树,也可以通过先序和中序遍历重建二叉树,比如会由这样的面试题,(对应的说lc的 重建二叉树 )
根据给出的先序和中序遍历结果,重建二叉树(已知没有重复元素):
先序遍历 1,2,4,7,3,5,6,8
中序遍历 4,7,2,1,5,3,8,6
看到这种面试题,首先要知道二叉树的性质,比如二叉树的先序遍历结果中,第一个肯定是root,而中序遍历的结果,根节点在中间,也就是说 4 7 2为左子树,5 3 8 6为右子树
所以程序的第一步就是找到左子树,右子树以及根节点
# -*- coding:utf-8 -*-
class ListNode(object):
def __init__(self, x=None, left=None, right=None):
self.left = left
self.val = x
self.right = right
def rebuildBinaryTree(list_preorder,list_inorder):
root = list_preorder[0]
root_index = list_inorder.index(root)
list_leftbinary = list_inorder[:root_index]
list_rightbinary = list_inorder[root_index:]
list_preorder = [1,2,4,7,3,5,6,8]
list_rightbinary = [4,7,2,1,5,3,8,6]
rebuildBinaryTree(list_preorder,list_inorder)
实际上现在关于左子树和右子树,又可以分别看成两个二叉树,因为本身二叉树就是递归的,所以从这里可以看出来,这道题实际上还是递归的解法比较简单
# -*- coding:utf-8 -*-
class TreeNode(object):
def __init__(self, x=None, left=None, right=None):
self.left = left
self.val = x
self.right = right
def myBuildTree(preorder_left, preorder_right, inorder_left, inorder_right):
if preorder_left > preorder_right:
return None
# 前序遍历中的第一个节点就是根节点
preorder_root = preorder_left
# 在中序遍历中定位根节点
inorder_root = index[preorder[preorder_root]]
# 先把根节点建立出来
root = TreeNode(preorder[preorder_root])
# 得到左子树中的节点数目
size_left_subtree = inorder_root - inorder_left
# 递归地构造左子树,并连接到根节点
# 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1)
# 递归地构造右子树,并连接到根节点
# 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right)
return root
preorder = [1,2,4,7,3,5,6,8]
inorder = [4,7,2,1,5,3,8,6]
n = len(preorder)
# 构造哈希映射,帮助我们快速定位根节点
index = {element: i for i, element in enumerate(inorder)}
myBuildTree(0, n - 1, 0, n - 1)
不解释了,背过吧。。。真心不知道面试官自己有没有写过几个二叉树的。。。。
当然也有非递归的解法: