数据结构基础 python

以下是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)

不解释了,背过吧。。。真心不知道面试官自己有没有写过几个二叉树的。。。。

当然也有非递归的解法:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值