常见程序员面试题

前几天去了趟北京找工作,自信满满,却碰了一鼻子灰回来。甚是不爽啊。很多面试题当时没做出来,但是回来查一下谷歌,或者自己思考一下,基本也就解决了。

1. 给定一个班里的成绩,给你一个分数,输出这个分数的排名。

继续追问:不需要是谁,重复分数算在内。比如4个100分,99分就是第5名。

我的答案:拿着这个分数去遍历数组,大于它的就拿出来,计算一下length即可。算法复杂度O(n)。

2. 给定一个班里M个人的成绩,输出每个分数的名次。

继续追问:分数都是整数。

我的答案:以空间换时间,按照顺序建立101个数组,遍历数组,把相应的人加入相应的数组,然后再输出,旁边附上一个自加值即可。时间O(n),空间2n 。

增加要求:有没有可能空间复杂度为n?

我没有想到...

面试官答案:为什么要记人呢,为什么要记分数呢?只需要记住几个就可以了。建立一个字典,甚至只是一个数组,遍历数组,相应的位置加1即可。

————————————

虽然没答出来,但是这是我最成功的一次面试了,我反应奇快。

————————————

1. 快速的取出中位数。

我:额。。排序?我毫无头绪。(唉,编程之美一定要看啊。)

面试官:快速排序啊,这个问题可以转化为找出第k大的数,平均复杂度基本就是n。

————————————

痛定思痛,回来好好研究了一下快速排序。根据@研究者July的博客,也算是研究了几种快速找出第k大的数。

先来快速排序:

def quicksort(q):
    
    if len(q) <= 1:
        return q
    else:
        pivot = q[0]
        return quicksort([x for x in q[1:] if x < pivot]) + [pivot] +\
             quicksort([x for x in q[1:] if x >= pivot])

快速排序的算法很简单,Python写迭代甚至比伪代码都好理解。找出数组中的第一个数,然后比这个数小的放到前面,比这个数大的放到后面。然后对前后两个数组再分别快速排序。算法平均复杂度公认为O(n)

借着快速排序,我可以找到第k小的数。因为以后这个模块还可以用来做别的,因此先写一个模块,输出前k小的数。

def min_k_elements_helper(q, k):
    '''
    find the minimum k elements in q
    not sorted
    '''
    if len(q) == k:
        return q
    elif len(q) > k:
        pivot = q[0]
        left_half = [x for x in q[1:] if x < pivot] + [pivot]
        right_half = [x for x in q[1:] if x >= pivot]
        if len(left_half) == k:
            return left_half
        elif len(left_half) > k:
            return min_k_elements_helper(left_half, k)
        else:
            return left_half + min_k_elements_helper(right_half, k - len(left_half))
    else:
        raise
算法思维也很简单,我每次拿出一个数,比它小的排前面,比它大的排后面,然后继续分,不排序,前面那个数组的大小正好为k的时候(多分少补),返回前面那个数组即可。

然后再取出其中的最大值返回即可。

中位数的话无非加个判断,分两种情况去计算。

根据@研究者July的分析,其实维护一个k大的数组,然后每次跟其中最大的相比,小的则取代最大值存入数组。缺点是当数组很大时,计算出其中的最大值需要点时间。

然后就是最节省时间的最小堆了。维护一个k大的最小堆,顶部是最大值,一一比较。最小二叉树的机制保证了当存入一个新数值时的效率,具体不说明了。但是想要做一个k大的最小堆并不容易。使用heapq可以比较容易的实现最大堆:

class MaxkHeap(object):

    def __init__(self, k):
        self.k = k
        self.data = []
        
    def push(self, item):
        if len(self.data) < self.k:
            heapq.heappush(self.data, item)
        else:
            topk_small = self.data[0]
            if item > self.data[0]:
                heapq.heapreplace(self.data, item)
                
    def topk(self):
        '''
        return the maxmum sorted elements 
        '''
        return [item for item in reversed([heapq.heappop(self.data) for _ in xrange(len(self.data))])]

heapq可以保证顶点始终是最小的数,如果过来的数比它大,那么替换顶点。Topk是用来输出最大n个值,顺序需要转换。

去网上查了一下,有人给出了一个非常巧妙的最小堆的实现方法,那就是存入负值,然后取出的时候取反即可。

class MinkHeap(object):
     def __init__(self, k):
        self.k = k
        self.data = []
        
    def push(self, item):
        item = -item
        if len(self.data) < self.k:
            heapq.heappush(self.data, item)
        else:
            if item > self.data[0]:
                heapq.heapreplace(self.data, item)
                
    def topk(self):
        '''
        return minimum k sorted elements 
        '''
        return [-heapq.heappop(self.data) for _ in range(len(self.data))]

仔细对比一下上下两个类,就是输入和输出有一点不一样,实在是太巧妙了。

有了最小堆之后,我们又可以继续优化效率了。

def min_k_elements_with_heapq(q, k):
    from top_k_heapq import MinkHeap
    heap_min = MinkHeap(k)
    for item in q:
        heap_min.push(item)
    return heap_min.topk()

数据结构,算法都是我很欠缺的东西,从今天开始要加油了。

源代码:Github

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值