第4章 数据结构算法

py内置数据结构算法常考

常用内置的算法和数据结构

sorted
list/set/dict/tuple

常用内置数据结构和算法

数据结构/算法 语言内置 内置库
线性结构 list(列表)/tuple(元组) array(数组, 不常用)/collections.namedtuple
链式结构 collections.deque(双端队列)
字典结构 dict(集合) collections.Counte(计数器)/OrderedDict(有序字典)
集合结构 set(集合)/frozenset(不可变集合)
排序算法 sorted
二分算法 bisect模块
堆算法 heapq模块
缓存算法 functools.lru_cached(Least Recent Used, py3)

有用过collections模块吗?
collections模块提供了一些内置数据结构的扩展
namedtuple
deque
Counter
OrderedDict
defaultdict

import collections

Point = collections.namedtuple('Point', 'x, y')

p = Point(1, 2)
print(p.x)
print(p.y)
print(p[0])
print(p[1])
#namedtuple让tuple属性可读

#deque可以方便地实现queue/stack
de = collections.deque()
de.append(1)
de.appendleft(0)
deque([0, 1])
de.pop()
de.popleft()
import collections

c = collections.Counter()
c = collections.Counter('abcab')

print(c)
print(c['a'])
print(c.most_common())
#需要计数器的地方使用Counter  

# !< out:
    Counter({'a': 2, 'b': 2, 'c': 1})
    2
    [('a', 2), ('b', 2), ('c', 1)]
import collections

od = collections.OrderedDict()

od['c'] = 'c'
od['a'] = 'a'
od['b'] = 'b'

print(list(od.keys()))
# !< out:
    ['c', 'a', 'b']
#LRU cache
#带有默认值的字典
import collections
dd = collections.defaultdict(int)

print(dd['a'])
dd['b'] += 1

print(dd)
# !< out:
    0
    defaultdict(<class 'int'>, {'a': 0, 'b': 1})

底层原理实现
py dict 底层结构

  • dict底层使用的哈希表
    为了支持快速查找使用了哈希表作为底层结构
    哈希表平均查找时间复杂度O(1)
    CPython解释器使用二次探查解决哈希冲突问题

哈希冲突和扩容是常考题

py list/tuple 区别

list vs tuple
都是线性结构 支持下标访问
list是可变对象, tuple保存的引用不可变
list没法作为字典的key, tuple可以(可变对象不可hash)

t = ([1], 2, 3)
t[0].append(1)

#保存的引用不可变指的是你没法替换掉这个对象
#但是如果对本身是一个可变对象,是可以修改
#这个引用指向的可变对象的

什么是LRUCache?
Least-Recently-Used替换掉最近最少使用的对象
缓存剔除策略,当缓存空间不够用的时候需要一种方式剔除key
常见的有 LRU,LFU等
LRU通过使用一个循环双端队列不断把最新访问的key放到表头实现

如何实现LRUCache?
字典用来缓存,循环双端链表用来记录访问顺序
利用py内置的dict+collections.OrderedDict实现

? dict用来当做k/v键值对的缓存

? OrderedDict用来实现更新最近访问的key

from collections import OrderedDict

class LRUCache:

    def __init__(self, capacity=128):
        self.od = OrderedDict()
        self.capacity = capacity

    def get(self, key): #每次访问更新最新使用的key
        if key in self.od:
            val = self.od[key]
            self.od.move_to_end(key)
            return val
        else:
            return -1

    def put(self, key, value): #更新k/v
        if key in self.od:
            del self.od[key]
            self.od[key] = value #更新key到表头
        else: #insert
            self.od[key] = value
            #判断当前容量是否已经满了
            if len(self.od) > self.capacity:
                self.od.popitem(last=False)

    #请实现LRUCache并编写单元测试

算法常考点

排序+查找 重中之重
常考排序算法:冒泡排序, 快速排序, 归并排序, 堆排序
线性查找, 二分查找等
能独立实现代码(手写) 能够分析时间空间复杂度
常用排序算法的时空复杂度

排序算法 最差时间分析 平均时间复杂度 稳定度 空间复杂度
冒泡排序 O(n^2) O(n^2) 稳定 O(1)
选择排序 O(n^2) O(n^2) 不稳定 O(1)
插入排序 O(n^2) O(n^2) 稳定 O(1)
快速排序 O(n^2) O(n*log2n) 不稳定 O(log2n) - O(n)
堆排序 O(n*log2n) O(n*log2n) 不稳定 O(1)

py数据结构常考题

常考题型
py web后端常考数据结构
常见的数据结构链表, 队列, 栈, 二叉树, 堆
使用内置结构实现高级数据结构, 比如内置的list/deque实现栈

? Leetcode或者上的常见题

常考数据结构之链表
链表有单链表, 双链表, 循环双端链表
如何使用Py来表示链表结构
实现链表常见操作, 比如插入节点, 反转链表, 合并多个链表等
Leetcode练习常见链表题目

数据结构之链表

#leetcode
# Definition for singly-linked list.
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre = None
        cur = head
        while cur:
            nextnode = cur.next
            cur.next = pre
            pre = cur
            cur = nextnode
        return pre

常考数据结构之队列
队列(queue)是先进先出结构
如何使用py实现队列?

? 实现队列的apend和pop操作,如何做到先进先出

? 使用py的list或者collections.deque实现队列

Collections.deque 双端队列

#实现队列, 使用deque
from collections import deque

class Queue:
    def __init__(self):
        self.items = deque()

    def append(self, val):
        return self.items.append(val)

    def pop(self):
        return self.items.popleft()

    def empty(self):
        return len(self.items) == 0

def test_queue():
    q = Queue()
    q.append(0)
    q.append(1)
    q.append(2)
    print(q.pop())
    print(q.pop())
    print(q.pop())

test_queue()

常考数据结构之栈
栈是后进先出结构
如何使用py实现栈?
实现栈的push和pop操作,如何做到后进先出
同样可以用py list或者collections.deque实现栈

#请实现一个Stack
#借助内置的数据结构非常容易实现一个栈Stack, 后进先出
from collections import deque
class Stack(object):
    def __init__(self):
        self.deque = deque()    #或者用list

    def push(self, value):
        self.deque.append(value)

    def pop(self):
        return self.deque.pop()

#如何使用两个stack 实现队列 先进先出
#实现获取最小值栈minstack

常考数据结构之字典与集合
py dict/set底层都是哈希表
哈希表的实现原理, 底层其实就是一个数组
根据哈希函数快速定位一个元素, 平均查找O(1), 非常快
不断加入元素会引起哈希表重新开辟空间, 拷贝之前元素到新数组

哈希表如何解决冲突
链接法和开放寻址法
元素key冲突之后使用一个链表填充相同key的元素
开放寻址法是冲突之后根据一种方式(二次探查)寻找下一个可用的槽
cpython使用的是二次探查

常考数据结构之二叉树
先序, 中序, 后序遍历
先(根)序:先处理根, 之后是左子树, 然后是右子树
中(根)序:先处理左子树, 然后是根, 然后是右子树
后(根)序:先处理左子树, 然后是右子树, 最后是根

#数据结构之二叉树:
#树的遍历方式
#先序遍历 递归代码里先处理根
class BinTreeNode(object):
    def __init__(self, data, left=None, right=None):
        self.data, self.left, self.right = data, left, right

class BinTree(object):
    def __init__(self, root=None):
        self.root = root

    def preorder_trav(self, subtree):
        """先(根)序遍历"""
        if subtree is not None:
            print(subtree.data) #递归函数里先处理根
            self.preorder_trav(subtree.left)    #递归处理左子树
            self.preorder_trav(subtree.right)   #递归处理右子树
     #中序遍历 调整下把print(subtree.data)放中间就好
    def inorder_trav(self, subtree):
        if subtree is not None:
            self.preorder_trav(subtree.left)
            print(subtree.data) #中序遍历放到中间就好
            self.preorder_trav(subtree.right)

常考数据结构之堆
堆其实是完全二叉树, 有最大堆和最小堆
最大堆:对于每个非叶子节点V, V的值都比它的孩子大
最大堆支持每次pop操作获取最大的元素, 最小堆获取最小元素

最大堆支持每次pop操作获取最大的元素, 最小堆获取最小元素

常见问题:用堆来完成topk问题, 从海量数字找寻找最大的k个
"""
class Topk:

    def __init__(self, iterable, k):
        self.minheap = []
        self.capacity = k
        self.iterable = iterable

    def push(self, val):
        if len(self.minheap) >= self.capacity:
            min_val = self.minheap[0]
            if val < min_val:   #可以直接>操作, 这里只显示跳过这个元素
                pass
            else:
                heapq.heapreplace(self.minheap, val) #返回并且pop堆顶最小值,
                                                     #推入新的val值并调整堆
        else:
            heapq.heappush(self.minheap, val)   #前面k个元素直接放入minheap

    def get_topk(self):
        for val in self.iterable:
            self.push(val)
        return self.minheap

def test():
    import random
    i = list(range(1000))   #这里可以是一个可迭代的元素, 节省内存
    random.shuffle(i)
    _ = Topk(i, 10)
    print(_.get_topk())

test()
# !< out:
[990, 991, 992, 995, 993, 998, 994, 996, 997, 999]

使用堆解决TOPK问题

py白板编程(手写代码)
手写算法题
刷题, Leetcode github题解
如何准备
刷题
面试之前系统整理之前做过的题目,不要靠记忆而是真正理解掌握
打好基础是重点,面试可以刷常见题突击, 保存手感

面试前练习
刷题(leetcode + 剑指offer + 面经)
上常见题目用py是实现
把leetcode上常见分类题目刷一遍(github搜索leetcode分类)
常见排序算法和数据结构能手写

常考数据结构题--链表
链表涉及到指针操作较为复杂,容易出错, 经常用作考题
熟悉链表的定义和常见操作
常考题:删除一个链表节点 leetcode 237
合并两个有序链表 leetcode 21

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        root = ListNode(None)
        cur = root
        while l1 and l2:
            if l1.val < l2.val:
                node = ListNode(l1.val)
                l1 = l1.next
            else:
                node = ListNode(l2.val)
                l2 = l2.next
            cur.next = node
            cur = node
        if l1 is None:
            cur.next = l2
        else:
            cur.next = l1
        return root.next

二叉树
二叉树涉及到递归和指针操作, 常结合递归考察
二叉树的操作涉及到递归和指针操作
二叉树的镜像(反转二叉树)
如何层序遍历二叉树(广度优先) 102题
输出左右视角的二叉树结果

class TreeNode:
    def __init__(self, val, left, right):
        self.val, self.left, self.right = val, left, right

#leetcode 237

栈与队列

后进先出 vs 先进先出

? 熟练掌握用py的list 或者 collections.deque()实现栈和队列
? 常考题:用栈实现队列(使用两个栈实现)
? leetcode:implement - queue - using - stacks 232题

     |__|   |__|
     |__|   |__|
     |__|   |__|
     |__|   |__|
      S1     S2
栈2不为空直接pop 否则把栈1的所有元素放到栈2
然后执行栈2 pop操作

deque() 双端队列

from collections import deque

class Stack:
    def __init__(self):
        self.items = deque()

    def push(self, val):
        return self.items.append(val)

    def pop(self):
        return self.items.pop()

    def top(self): #return stack top value
        return self.items[-1]

    def empty(self):
        return len(self.items) == 0

class MyQueue:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.s1 = Stack()
        self.s2 = Stack()

    def push(self, x: int) -> None:
        """
        Push element x to the back of queue.
        """
        self.s1.push(x)

    def pop(self) -> int:
        """
        Removes the element from in front of queue and returns that element.
        """
        if not self.s2.empty():
            return self.s2.pop()
        while not self.s1.empty():
            val = self.s1.pop()
            self.s2.push(val)
        return self.s2.pop()

    def peek(self) -> int:
        """
        Get the front element.
        """
        if not self.s2.empty():
            return self.s2.top()
        while not self.s1.empty():
            val = self.s1.pop()
            self.s2.push(val)
        return self.s2.top()

    def empty(self) -> bool:
        """
        Returns whether the queue is empty.
        """
        return self.s1.empty() and self.s2.empty()

def test():
    q = MyQueue()
    q.push(1)
    q.push(2)
    q.push(3)
    print(q.pop())
    print(q.pop())
    print(q.pop())

test()

数据结构之堆

堆的常考题基本围绕在合并多个有序(数组/链表) Topk问题
|-理解堆的概念 堆是完全二叉树 有最大堆和最小堆
|-会使用py内置的heapq模块实现堆的操作
|-常考题:合并k个有序链表leetcode merge-k-sorted-list 23 title
合并两个有序链表..

? |--1.读取所有链表值

? |--2.构造一个最小堆heapq实现

? |--3.根据最小堆构造链表

# Definition for singly-linked list.
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

from heapq import heapify,heappop
class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        h = []
        for node in lists:
            while node:
                h.append(node.val)
                node = node.next
        if not h:
            return None
        heapify(h)

        root = ListNode(heappop(h))
        curnode = root
        while h:
            nextnode = ListNode(heappop(h))
            curnode.next = nextnode
            curnode = nextnode
        return root

字符串常考题

字符串题
了解常用的字符串操作
翻转一个字符串 334
判断是否是回文串 9

? py内置字符串操作split, upper, replace etc

s.reverse()

def reverseString(self, s):
beg = 0
end = len(s) - 1
while beg < end:
    s[beg], s[end] = s[end], s[beg]
    beg += 1
    end -= 1
def isPalindrome(x):
    if x < 0:
        return False
    s = str(x)
    beg, end = 0, len(s) - 1
    while beg < end:
        if s[beg] == s[end]:
            beg += 1
            end -= 1
        else:
            return False
    return True

字符串的双端遍历方法,可以解决类似问题

练习题
|--链表, 字符串,
如何反转一个单链表
使用循环的方式实现
使用递归地方式实现

原文地址:https://www.cnblogs.com/xuzhaoping/p/11616745.html

时间: 2024-08-06 17:12:49

第4章 数据结构算法的相关文章

算法导论笔记——第十二~十四章 数据结构(二)树

第十二章 二叉搜索树 >=左子树的所有key,<=右子树的所有key 在一棵高度为h的二叉搜索树上,动态集合上的操作SEARCH,MINIMUM,MAXIMUM,SUCCESSOR,PREDECESSOR,INSERT和DELETE可以在O(h)时间内完成. h>=(lgn向下取整) 和快速排序算法一样,其平均性能更接近于最好情形. 随机构建二叉搜索树期望高度为O(lgn). 各种操作请自行查阅. 第十三章 红黑树 是一种(近似)平衡的二叉搜索树.可以保证在最坏情况下基本动态集合操作的时

算法导论读书笔记-第十四章-数据结构的扩张

算法导论第14章 数据结构的扩张 一些工程应用需要的只是标准数据结构, 但也有许多其他的应用需要对现有数据结构进行少许的创新和改造, 但是只在很少情况下需要创造出全新类型的数据结构, 更经常的是通过存储额外信息的方法来扩张一种标准的数据结构, 然后对这种数据结构编写新的操作来支持所需要的应用. 但是对数据结构的扩张并不总是简单直接的, 因为新的信息必须要能被该数据结构上的常规操作更新和维护. 14.1 动态顺序统计 顺序统计树(order-static tree) : 在红黑树的基础上, 在每个

第一章 &nbsp; &nbsp; &nbsp;数据结构和算法简介

数据是对客观事物的符号表示,在计算机科学中是指所有能输入到计算机中并能被计算机程序处理的符号的总称. 数据结构是相互之间存在一种或多种特定关系的数据元素的结合. 数据结构=数据元素+关系(结构): 数据在计算机中的表示称为数据的储结构 任何一个算法的设计取决于选定的逻辑结构,而算法的实现依赖于采用的存储结构 程序=数据结构+算法 算法是对特定问题求解步骤的一种描述 算法的5大特性: 1)  有限性,算法必须在有限的步骤之后结束 2)  确定性,算法的每一步都是确定的定义,无二义性.即在任何条件下

程序员教程-4章-数据结构与算法

目录结构 4.1 线性结构 4.1.1 线性表 1 线性表的定义 2 线性表的存储结构 3 线性表的应用 4.1.2 栈和队列 1 栈 2 队列 4.1.3 串 1 串的定义及基本运算 4.2 数组 1 数组 2 矩阵 4.3 树与二叉树 4.3.1 树的基本概念 4.3.2二叉树 1 二叉树的性质 2 满二叉树和完全二叉树 3 二叉树的存储结构 4 二叉树的遍历 4.3.3 树和森林 1 树和森林与二叉树的相互转换 2 树和森林的遍历 4.3.4 最优二叉树 4.3.5 二叉查找树 4.4图

《大话数据结构》---第一章 数据结构绪论

数据结构是一门研究非数值计算的程序设计问题中的操作对象,以前他们之间的关系和操作等相关问题的学科. 程序设计 = 数据结构 + 算法 数据:描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合.数据元素:组成数据的.有一定意义的基本单位,在计算机中通常作为整体处理.也被称为记录.数据项:一个数据元素可以由若干个数据项组成,数据项是数据不可分割的最小单位.数据对象:是性质相同的数据元素的集合,是数据的子集.(性质相同即具有相同数量和类型的数据项)数据结构:是

C++ Primer 读书笔记:第11章 泛型算法

第11章 泛型算法 1.概述 泛型算法依赖于迭代器,而不是依赖容器,需要指定作用的区间,即[开始,结束),表示的区间,如上所示 此外还需要元素是可比的,如果元素本身是不可比的,那么可以自己定义比较函数. 2.常用的泛型算法函数: fill,fill_n, copy, replace, sort, unique, count_if, stable_sort 此外在有一个谓词函数会结合以上的函数使用,像sort, count_if等 3.再谈迭代器 (1)插入迭代器 back_inserter, f

第十一章 执行查询算法

第十一章      执行查询算法 基本概念: 三类查找方法:线性查找.树形查找.哈希表查找 动态查找表:在查找的同时,对表做修改操作(如插入和删除),则相应的表称为动态查找表 静态查找表:与动态查找表相反 查找过程中对关键字需要执行的平均比较次数(也称平均比较长度作为衡量一个查找算法优劣的标准 平均比较长度: 其中:n是结点的个数:pi是查找第i个结点的概率.若不特别申明,认为每个结点的查找概率相等,都为1/n:ci是找到第i个结点所需进行的比较次数. 线性查找: 基本思想:从表的一端开始,顺序

【数据结构&amp;&amp;算法系列】KMP算法介绍及实现(c++ &amp;&amp; java)

KMP算法如果理解原理的话,其实很简单. KMP算法简介 这里根据自己的理解简单介绍下. KMP算法的名称由三位发明者(Knuth.Morris.Pratt)的首字母组成,又称字符串查找算法. 个人觉得可以理解为最小回溯算法,即匹配失效的时候,尽量少回溯,从而缩短时间复杂度. KMP算法有两个关键的地方,1)求解next数组,2)利用next数组进行最小回溯. 1)求解next数组 next数组的取值只与模式串有关,next数组用于失配时回溯使用. 在简单版本的KMP算法中,每个位置 j 的 n

数据结构算法1------算法和算法分析

最近面试iOS实习生.几次都是败在数据结构算法上面了.决定重新捡起大学的课本,复习一下数据结构算法相关知识. 1.反转一个链表.循环算法.               1     List   reverse(List   l)   {       2     if(!l)   return   l;       3         list   cur   =   l.next;       4     list   pre   =   l;       5     list   tmp;