00:其他类题目

算法面试其他篇

目录:

1.1 python模拟LRU(Least recently used,最近最少使用)

    定义:算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

    核心:

      1. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;

      2. 当链表满的时候,将链表尾部的数据丢弃。

  1、实现原理

    1)使用三个数据标识这个缓存系统      

        self.cache = {}                   # cache模拟所有缓存系统中数据 key: val

        self.keys = []                     # keys只存放cache字典中的 key 

        self.size = size                  # size 来定义这个缓存系统最大容量

    2)模拟数据加入缓存(set)

        1. 如果缓存为达到最大长度,新数据key插入到 keys列表头部,将key:val存入字典

        2. 如果达到最大长度,先删除最后列表keys最后一个元素,将这个key:val 从cache字典中删除

    3)模拟获取缓存数据(get)

        1. 判断获取的key是否在 cache字典中,如果在就从列表keys中先删除原有key,然后将key插入到列表最前面,并返回val

        2. 如果key不在cache字典中,直接返回None即可

#!/usr/bin/env python
# -*- coding:utf-8 -*-
class LRUcache:
    def __init__(self, size=3):
        self.cache = {}
        self.keys = []
        self.size = size

    def get(self, key):
        if key in self.cache:
            self.keys.remove(key)
            self.keys.insert(0, key)
            return self.cache[key]
        else:
            return None

    def set(self, key, value):
        if key in self.cache:
            self.keys.remove(key)
            self.keys.insert(0, key)
            self.cache[key] = value
        elif len(self.keys) == self.size:
            old = self.keys.pop()
            self.cache.pop(old)
            self.keys.insert(0, key)
            self.cache[key] = value
        else:
            self.keys.insert(0, key)
            self.cache[key] = value


if __name__ == '__main__':
    test = LRUcache()
    test.set('a', 1)
    test.set('b', 2)
    test.set('c', 3)
    test.set('d', 4)
    print test.keys  # ['d', 'c', 'b']
    print test.cache  # {'c': 3, 'b': 2, 'd': 4}

    print(test.get('a'))  # None   当d=4加入缓存是,缓存到达上线3,所以把a从缓存中移除了
    print(test.get('b'))  # 2
    print(test.get('c'))  # 3
    print(test.get('d'))  # 4
python模拟LRU

1.2 遍历文件夹

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
def gci(filepath):
    files = os.listdir(filepath)    # 遍历filepath下所有文件,包括子目录
    for fi in files:
        fi_d = os.path.join(filepath, fi)
        if os.path.isdir(fi_d):
            gci(fi_d)
        else:
            print(os.path.join(filepath, fi_d))

# 递归遍历/root目录下所有文件
gci('C:\Go')
遍历文件夹

1.3 is和==

  1.== 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。

  2.is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同。

1.4 python读取超大文件

  1)普通读文件方法弊端分析

      1.with 上下文管理器会自动关闭打开的文件描述符,在迭代文件对象时,内容是一行一行返回的,不会占用太多内存

      2. 如果python读取文件如果被读取的文件里,根本就没有任何换行符,将会变成一个非常巨大的字符串对象,占用大量内存。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def read_file(fname):
    with open(fname) as file:
        for line in file:
            print(line.strip('\n'),)
path = r'C:\aaa\luting\edc-backend\aaa.py'
read_file(path)
python普通方法读文件

  2)读取大文件正确方式

      1.  我们使用了一个 while 循环来读取文件内容,每次最多读取 8kb 大小

      2. 这样可以避免之前需要拼接一个巨大字符串的过程,把内存占用降低非常多。 

#!/usr/bin/python
# -*- coding: utf-8 -*-
def read_big_file_v(fname):
    block_size = 1024 * 8
    with open(fname,encoding="utf8") as fp:
        while True:
            chunk = fp.read(block_size)
            # 当文件没有更多内容时,read 调用将会返回空字符串 ''
            if not chunk:
                break
            print(chunk)
path = r'C:\aaa\luting\edc-backend\tttt.py'
read_big_file_v(path)
python读取大文件

 1.5 手写三级装饰器

  1、手写普通装饰器

#!/usr/bin/python
# -*- coding: utf-8 -*-
def outer_wrapper(func):
    def wrapper(*args, **kwargs):
        print('---->')
        res = func(*args, **kwargs)
    return wrapper

@outer_wrapper
def home():
    print("welcome to home  page")
    return "from home"

home()
普通装饰器

  2、三级装饰器

#!/usr/bin/python
# -*- coding: utf-8 -*-
def auth(auth_type):
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("---->", auth_type)
            res = func(*args, **kwargs)
        return wrapper
    return outer_wrapper

@auth(auth_type="local") # home = wrapper()
def home():
    print("welcome to home  page")
    return "from home"

home()
三级装饰器

  3、计算函数执行时间装饰器

#! /usr/bin/env pythonf
# -*- coding: utf-8 -*-
import time
def timer(func):   #timer(test1)  func=test1
    def deco(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)      #run test1
        stop_time = time.time()
        print("running time is %s"%(stop_time-start_time))
    return deco
@timer     # test1=timer(test1)
def test1():
    time.sleep(3)
    print("in the test1")
test1()
计算函数执行时间装饰器

 1.6 使用yield生成器生成斐波拉契函数

#!/usr/bin/python
# -*- coding: utf-8 -*-
def fib(max_num):
    a,b = 1,1
    while a < max_num:
        yield b
        a,b=b,a+b

g = fib(10)               #生成一个生成器:[1,2, 3, 5, 8, 13]
print(g.__next__())       #第一次调用返回:1
print(list(g))            #把剩下元素变成列表:[2, 3, 5, 8, 13]
生成斐波拉契函数

 1.7 单例模式

  1、单例模式原理及作用

    1、单例模式:永远用一个对象得实例,避免新建太多实例浪费资源
    2、实质:使用__new__方法新建类对象时先判断是否已经建立过,如果建过就使用已有的对象
    3、使用场景:如果每个对象内部封装的值都相同就可以用单例模式

  2、单例模式两种写法

class Foo(object):
   instance = None
   def __init__(self):
      self.name = 'alex'

   def __new__(cls, *args, **kwargs):
      if Foo.instance:
         return Foo.instance
      else:
         Foo.instance = object.__new__(cls,*args,**kwargs)
         return Foo.instance

obj1 = Foo()       # obj1和obj2获取的就是__new__方法返回的内容
obj2 = Foo()
print(obj1,obj2)   # 运行结果: <__main__.Foo object at 0x00D3B450>    <__main__.Foo object at 0x00D3B450>

# 运行结果说明:
# 这可以看到我们新建的两个Foo()对象内存地址相同,说明使用的•同一个类,没有重复建立类
单例模式01
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Singleton(object):
    def __new__(cls, *args, **kwargs):           #new方法最后返回的是一个实例
        if not hasattr(cls, "_instance"):       #如果没有这个字段就调用父类创建
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance                     #永远返回的就是第一次创建的对象

obj1 = Singleton()
obj2= Singleton()
print(obj1,obj2)
单例模式02

 1.8 python高阶函数

   更多详见:https://www.cnblogs.com/xiaonq/p/7898817.html#i2

  1、三元运算 & lambda

name = 'Tom' if 1 == 1 else 'fly'
print(name)   # 运行结果: Tom

  2、lambda使用

f = lambda x:x if x % 2 != 0 else x + 100
print(f(10))                    # 110
f = lambda x,y,z:x+y+z
print(f(1,2,3))                    # 6

  3、 利用 filter、lambda表达式 获取l1中元素小于33的所有元素 l1 = [11, 22, 33, 44, 55]

l1= [11,22,33,44,55]
a = filter(lambda x: x<33, l1)
print(list(a))

  4、利用map,lambda表达式将所有偶数元素加100

l1= [11,22,33,44,55]
ret = map(lambda x:x if x % 2 != 0 else x + 100,l1)
print(list(ret))  # 运行结果: [11, 122, 33, 144, 55]

  5、总结:filter()和map()函数区别

    1. Filter函数用于对序列的过滤操作,过滤出需要的结果,一次性返回他的过滤设置于的是条件

    2. Map函数是对序列根据设定条件进行操作后返回他设置的是操作方法,无论怎样都会返回结果

  7、使用reduce求和

from functools import reduce  # py3
print(reduce(lambda x, y: x + y, [ 1, 2, 3, 4]))  # 10
'''使用reduce将字符串反转'''
s = 'Hello World'
from functools import reduce

result = reduce(lambda x,y:y+x,s)
# 1、第一次:x=H,y=e  => y+x = eH
# 2、第二次:x=l,y=eH  => y+x = leH
# 3、第三次:x=l,y=leH  => y+x = lleH
print( result )      # dlroW olleH
reduce字符串反转

  8、sorted排序

students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
print( sorted(students, key=lambda s: s[2], reverse=False) )    # 按年龄排序
# 结果:[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
sorted对列表排序
d = {'k1':1, 'k3': 3, 'k2':2}
# d.items() = [('k1', 1), ('k3', 3), ('k2', 2)]
a = sorted(d.items(), key=lambda x: x[1])
print(a)            # [('k1', 1), ('k2', 2), ('k3', 3)]
sorted对字典排序

 1.9 python求阶乘 & 青蛙跳问题

  1、求4的阶乘

def test(n):
    if n == 1:
        return 1
    else:
        res = n*test(n-1)
    return res

print(test(4))  # 24
求4的阶乘代码

  2、青蛙跳问题

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
sys.setrecursionlimit(1000000000)             #设置系统最大递归深度

def fib(n):
    if n <= 2:
        return n
    else:
        return fib(n-1) + fib(n-2)
print(fib(4))         # 5
二级台阶
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
sys.setrecursionlimit(1000000000)             #设置系统最大递归深度

def fib(n):
    if n <= 2:
        return n
    elif n == 3:
        return 4
    else:
        return fib(n-1) + fib(n-2) + fib(n-3)
print(fib(4))         # 7
三级台阶
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
sys.setrecursionlimit(1000000000)             #设置系统最大递归深度

def fib(n):
    if n <= 2:
        return n
    else:
        return 2 * fib(n - 1)
print(fib(4))         # 8
n级台阶

1.10 瓶盖换酒问题 

  问题:2元一瓶酒,2个空瓶换一瓶,4个瓶盖换一瓶 问10块钱买几瓶酒??

class Change:
    def __init__(self,money):
        self.money=money      # 总共的钱
        self.beer=money//2    # 买到酒的数量(第一次用钱买酒)
        self.cap=0            # 瓶盖数量
        self.bottle=0         # 空瓶数量
        self.count=0          # 总共和了多少瓶酒

    def ping(self):
        new_beer=self.bottle//2   # 当前空瓶可换酒的数量
        self.beer+=new_beer       # 更新酒的数量
        self.bottle-=(new_beer*2) # 将已使用的空瓶去除

    def gai(self):
        new_beer=self.cap//4     # 当前瓶盖换酒的数量
        self.beer+=new_beer      # 更新酒的数量
        self.cap-=(new_beer*4)   # 将已使用的瓶盖去除

    def drink(self):
        self.bottle+=self.beer   # 最终空瓶 = 酒的数量+当前空瓶数量
        self.cap+=self.beer      # 瓶盖数量 = 酒的数量+当前瓶数量
        self.count+=self.beer    # 喝酒数量 = 酒的数量+已经喝的数量
        self.beer=0              # 喝完后把酒的数量重置为0

    def run(self):
        while self.beer>0 or self.bottle>=2 or self.cap>=4:
            self.drink()   # 喝酒
            self.ping()    # 空瓶换酒
            self.gai()     # 瓶盖换酒
        return '喝了%d瓶酒,剩余%d个瓶子,剩余%d个盖子'%(self.count,self.bottle,self.cap)

person=Change(10)
print(person.run())  # 喝了15瓶酒,剩余1个瓶子,剩余3个盖子
最终喝到酒的数量

 1.11  [lambda x: x*i for i in range(4)] 本质解析

  1、[lambda x: x*i for i in range(4)] 运行结果

# -*- coding: utf-8 -*-
fun = [lambda x: x*i for i in range(4)]
for item in fun:
    print(item(1), end='\t')
# 预计结果为:0, 1, 2, 3
# 实际输出为:3, 3, 3, 3
意外结果
#! /usr/bin/env python
# -*- coding: utf-8 -*-
fun = [lambda x, i=i: x*i for i in range(4)]
for item in fun:
    print(item(1), end='\t')
# 运行结果:0    1    2    3
正确结果

  2、原因分析

     注:python解释器查找变量时,会按照顺序依次查找:局部作用域--->嵌套作用域--->全局作用域--->内建作用域

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def func():
    tmp = []
    for i in range(4):
        print('外层:',i)  # 0 1 2 3
        def lambda_(x):
            print('内存i:',i) # 3
            return x * i
        tmp.append(lambda_)
    return tmp

fl = func()
fl[0](1)
'''1. 运行结果如下:'''
# 外层: 0
# 外层: 1
# 外层: 2
# 外层: 3
# 内存i: 3

'''2. 原因分析'''
# 1. 四次循环中外层函数命名空间中的 i 从 0-->1-->2-->3 最后固定为3
# 2. 内嵌函数lambda_中因为没有定义 i ,从外层函数获取的值都为3
# 3. 导致得不到预计输出结果:0,1,2,3 只能得到 3, 3, 3, 3
自定义函数模拟意外结果

 1.12 ip地址转换

  1、求当前ip地址的下一个地址

      1) socket.inet_aton:  将IPv4点分地址转换成32位进制数     

      2) struct.unpack('!I':  将32位数转换成,十位无符号整形  

      3) struct.pack('!I':将十位数字转换成 32位进制数

      4) socket.inet_ntoa: 把32位进制数转换成ip 127.0.1.0

import socket
import struct
if __name__ == '__main__':
    ip = '127.0.0.255'
    int_ip = struct.unpack('!I', socket.inet_aton(ip))[0]     # 2130706687 (int)
    str_ip = socket.inet_ntoa(struct.pack('!I', int_ip+1 ))   # 127.0.1.0
求当前ip地址下一个地址
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import struct
if __name__ == '__main__':
    ip = '127.0.0.255'

    '''1. 将ip转成数字'''
    # 1.1 socket.inet_aton: 将IPv4点分地址转换成32位进制数
    ip_to_32dec = socket.inet_aton(ip)    # b'\x7f\x00\x00\xff'
    # 1.2 struct.unpack('!I': 将32位数转换成,十位无符号整形
    dec32_to_10 = struct.unpack('!I', ip_to_32dec)[0]  # 2130706687

    '''2. 将数字转换成ip'''
    # 2.1 struct.pack('!I':将十位数字转换成 32位进制数
    dec10_to_32 = struct.pack('!I', 2130706688)  # b'\x7f\x00\x01\x00'
    # 2.2 socket.inet_ntoa: 把32位进制数转换成ip 127.0.1.0
    f_ip = socket.inet_ntoa(dec10_to_32)
    print(f_ip)  # 127.0.1.0
struct库常用方法

1.13  统计文章中单词出现次数并进行排序

  1、统计指定文件中单词出现数量,并找到出现次数最多的前无个

import io
import re
class Counter:
    def __init__(self, path):
        self.mapping = dict()
        with io.open(path, encoding="utf-8") as f:
            data = f.read()
            words = [s.lower() for s in re.findall("\w+", data)]
            print(words)  # ['version', '3', 'services', .... ]
            for word in words:
                self.mapping[word] = self.mapping.get(word, 0) + 1

    def most_common(self, n):
        return sorted(self.mapping.items(), key=lambda item: item[1], reverse=True)[:n]

if __name__ == '__main__':
    pt = r'C:\aaa\luting\edc-backend\docker-compose.yml'
    most_common_5 = Counter(pt).most_common(5)
    print(most_common_5)  # [('mysql', 5), ('root', 3), ('db', 3), ('environment', 2), ('3306', 2)]
统计单词出现次数

 

 

 

 

 

1111111111111111

posted @ 2019-03-07 13:35  不做大哥好多年  阅读(344)  评论(0编辑  收藏  举报