腾讯算法岗面试算法题——计数排序
给定两个整数数组,对第一个数组进行排序,整数顺序由其在第二个数组中的位置决定。对于没有出现在
第二个整数数组中的整数,应排在末尾,其之间的顺序无限制。这里整数的取值范围是[0, 2 ^ 32 - 1]
例: 第一个整数数组为 5 1 6 2 1 2 3, 第二个整数数组为2 1 3, 则排序结果为2 2 1 1 3 6 5或2 2 1 1 3 5 6
这道题很明显的第一个思路就是,先生成一个数到index下标的映射,然后建立一个compare函数,根据这个compare函数写一个快排。
相关代码如下:
# -*- coding:utf-8 -*- num2index = dict() def cmp(a, b): if a not in num2index and b not in num2index: return 0 elif a not in num2index: return 1 elif b not in num2index: return -1 else: if num2index[a] < num2index[b]: return -1 elif num2index[a] == num2index[b]: return 0 else: return 1 def quicksort(array, begin, end): # print array if end - begin <= 1: return elif end - begin == 2: if cmp(array[begin], array[begin + 1]) > 0: array[begin], array[begin + 1] = array[begin + 1], array[begin] return flag = array[begin] low = begin + 1 high = end - 1 while (low < high): while low < high and cmp(array[high], flag) > 0: high -= 1 while low < high and cmp(array[low], flag) <= 0: low += 1 if low != high: array[low], array[high] = array[high], array[low] if high == low == begin + 1: quicksort(array, begin + 1, end) else: array[begin], array[high] = array[high], array[begin] quicksort(array, begin, high) quicksort(array, high + 1, end) def func(list1, list2): global num2index for index, num in enumerate(list2): num2index[num] = index # sorted(list1, cmp=cmp) quicksort(list1, 0, len(list1)) print list1 if __name__ == "__main__": list1 = [5, 1, 6, 2, 1, 2, 3] list2 = [2, 1, 3] func(list1, list2)
这个代码提交给面试官,面试官问了一下时间复杂度,显然快排的时间复杂度是nlog(n),面试官让我改成线性时间,o(╯□╰)o,抱歉当时那几个线性排序的算法都不记得了,面试官提醒了一下用count sort ,当时想不起来了。
回去的时候才想了一下。纯整数的排序,确实能够利用空间换时间,得到线性的时间复杂度。先遍历list1,建立一个num到count的映射,然后遍历list2,直接每个数字出现了多少次就加几个到结果中。同时把对应的count置为0,然后在遍历一遍num2count,对应value不是0的,加入value个键值。最后返回result
# -*- coding:utf-8 -*- def func(list1, list2): num2count = dict() for num in list1: if num2count.has_key(num): num2count[num] += 1 else: num2count[num] = 1 result = [] for num in list2: for _ in range(num2count[num]): result.append(num) num2count[num] = 0 for num in num2count: if num2count[num] != 0: for _ in range(num2count[num]): result.append(num) print result if __name__ == "__main__": list1 = [5, 1, 6, 2, 1, 2, 3] list2 = [2, 1, 3] func(list1, list2)
后面又回去想了想,好像还可以继续优化,第一遍利用快排的思想,把list1变成左边是在list2中出现过的数,右边是没有出现过的数。于是右边本来就是无序的,就可以直接对左边部分进行count sort,这样的代价才应该是最低的,无论从空间复杂性还是时间复杂性来说。
# -*- coding:utf-8 -*- num2index = dict() def cmp(a, b): if a not in num2index and b not in num2index: return 0 elif a not in num2index: return 1 elif b not in num2index: return -1 else: if num2index[a] < num2index[b]: return -1 elif num2index[a] == num2index[b]: return 0 else: return 1 def quicksort_one_time(array, flag): # print array if len(array) <= 1: return low = 0 high = len(array) - 1 while (low < high): while low < high and cmp(array[high], flag) > 0: high -= 1 while low < high and cmp(array[low], flag) <= 0: low += 1 if low != high: array[low], array[high] = array[high], array[low] return low def func(list1, list2): global num2index for index, num in enumerate(list2): num2index[num] = index index = quicksort_one_time(list1, list2[-1]) left, right = list1[:index + 1], list1[index + 1:] num2count = dict() for i in left: if num2count.has_key(i): num2count[i] += 1 else: num2count[i] = 1 result = [] for num in list2: if num2count[num] != 0: for _ in range(num2count[num]): result.append(num) return result + right if __name__ == "__main__": list1 = [5, 1, 6, 2, 1, 2, 3] list2 = [2, 1, 3] func(list1, list2) if __name__ == "__main__": list1 = [5, 1, 6, 2, 1, 2, 3] print func(list1, list2)