算法面试其他篇
目录:
1.1 简单数组题
1、去除列表中相加等于指定数后的列表(x+y=4)
[1,3,5,7,1,2] ==> [5,7,1,2] [1,3,3,5,7,1,2] ==> [3,5,7,1,2]

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def func(l, tag=4):
for i in range(len(l)):
a = l[i]
if a != None:
b = tag - a
try:
index = l.index(b)
if index != i: # 避免 2 + 2 = 4只有一个2也没重置为None
l[i] = None
l[index] = None
else:
if l.count(b) > 2: # 如果有两个就都重置为 None
l[i] = None
l[l.index(b)] = None
except Exception as e:
pass
print l # [None, None, 5, 7, 1, 2]
while None in l:
l.remove(None)
return l # [5, 7, 1, 2]
l = [1,3,5,7,1,2]
print func(l, 4)
'''
[1,3,5,7,1,2]
[None, None, 5, 7, 1, 2]
[5, 7, 1, 2]
'''
2、合并两个有序列表

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def loop_merge_sort(l1, l2):
tmp = []
while len(l1) > 0 and len(l2) > 0:
if l1[0] < l2[0]:
tmp.append(l1[0])
del l1[0]
else:
tmp.append(l2[0])
del l2[0]
tmp.extend(l1)
tmp.extend(l2)
return tmp
l1 = [1,4,5]
l2 = [2,3,6]
print loop_merge_sort(l1, l2) # [1, 2, 3, 4, 5, 6]

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def loop_merge_sort(l1, l2):
n1, n2 = 0, 0
len1, len2 = len(l1), len(l2)
tmp = []
while n1 < len1 and n2 < len2:
if l1[n1] < l2[n2]:
tmp.append(l1[n1])
n1 = n1 + 1
else:
tmp.append(l2[n2])
n2 = n2 + 1
else:
while n1 < len1:
tmp.append(l1[n1])
n1 = n1 + 1
while n2 < len2:
tmp.append(l2[n2])
n2 = n2 + 1
return tmp
l1 = [1,4,5]
l2 = [2,3,6]
print loop_merge_sort(l1, l2) # [1, 2, 3, 4, 5, 6]

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def find_kth(l1, l2, k):
val = None
n = 1
while len(l1) > 0 and len(l2) > 0: # l1和l2都未取到最后一个数
if l1[0] < l2[0]:
val = l1[0]
del l1[0]
else:
val = l2[0]
del l2[0]
if n == k:
return val
n = n + 1
while len(l1) > 0: # l2为空,l1不为空
val = l1[0]
del l1[0]
if n == k:
return val
n = n + 1
while len(l2) > 0: # l1为空l2不为空
val = l2[0]
del l2[0]
if n == k:
return val
n = n + 1
l1 = [1,4,5,11,12,13,18]
l2 = [2,3,6,7,8,9,10]
print find_kth(l1, l2, 14) # 18
3、一个有序列表找中位数

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def get_median(data):
data.sort()
half = len(data) // 2
return (data[half] + data[-1-half]) / float(2)
data = [1,2,3,4,5,6,7,8,9,10,11,12]
print get_median(data)
4、找素数(除了1和他本身没有其他的约数)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import math
def func(n):
l = []
for i in range(2, n+1):
for j in range(2, int(math.sqrt(i))+1):
if i%j == 0: #如果出现整除说明有因子
break #跳出最外层for循环判断下一个
else: #如果第二层循环结束还没有跳出的话
l.append(i) #说明是素数,加到列表里
return l
print func(20) # [2, 3, 5, 7, 11, 13, 17, 19]

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import math
def func(n):
l = [2,3,5]
for i in range(6, n+1):
for j in l:
if j <= ( math.sqrt(i) + 1 ) and i%j == 0:
break #跳出最外层for循环判断下一个
else: #如果第二层循环结束还没有跳出的话
l.append(i) #说明是素数,加到列表里
return l
print func(10) # [2, 3, 5, 7]
'''
说明:判断100是否是质素只需要对比4次
1、不需要循环[1,2,3,4,5,6,7,8,9,10]依次与100取余
2、只需要循环[2, 3, 5, 7] 依次与100取余即可
'''
5、列表去重

#1、set去重
a=[1,2,3,4,1,2,3,4]
print( list(set(a)) ) # [1, 2, 3, 4]
#2、使用字典去重
b = {}
b=b.fromkeys(a).keys()
print(list(b)) # [1, 2, 3, 4]

a=[1,2,3,4,1,2,3,4]
#1、去重不改变顺序
d = {}
tmp = []
for i in a:
if not d.get(i):
tmp.append(i)
d[i] = True
print(tmp) # [1, 2, 3, 4]
6、实现enumerate函数

l = [1,2,3,4]
def my_enumerate(l):
n = 0
for val in l:
print(n,val)
n += 1
my_enumerate(l)
7、求两个列表 交集、差集、并集

list_1 = set([1,2,3,4,5])
list_2 = set([4,5,6,7,8])
#1、交集(在list_1和list_2中都有的元素4,5)
print(list_1.intersection(list_2)) #交集: {4, 5}
#2、差集
print(list_1.difference(list_2)) #差集:在list_1中有在list_2中没有: {1, 2, 3}
print(list_2.difference(list_1)) #差集:在list_1中有在list_2中没有: {8, 6, 7}
#3、并集(在list_1和list_2中的元素全部打印出来,重复元素仅打印一次)
print(list_1.union(list_2)) #并集: {1, 2, 3, 4, 5, 6, 7, 8}
8、求股票最大利润

def maxProfit(prices):
min_p, max_p = max(prices), 0
for i in range(len(prices)):
min_p = min(min_p, prices[i]) # 到目前为止最小买入价格
max_p = max(max_p, prices[i] - min_p) # 最大利润 = 当天价格 - 历史最小价格
return max_p
l = [7,1,5,3,6,4]
print(maxProfit(l)) # 5
1.2 找两个有序列表中位数(难)
1、找中位数(两个列表)
参考博客:https://www.jb51.net/article/152061.htm
参考官网:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/
1)给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
2)请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def median(A, B):
m, n = len(A), len(B)
if m > n:
A, B, m, n = B, A, n, m
if n == 0:
raise ValueError
imin, imax, half_len = 0, m, (m + n + 1) / 2
while imin <= imax:
i = (imin + imax) / 2
j = half_len - i
if i < m and B[j-1] > A[i]:
# i is too small, must increase it
imin = i + 1
elif i > 0 and A[i-1] > B[j]:
# i is too big, must decrease it
imax = i - 1
else:
# i is perfect
if i == 0: max_of_left = B[j-1]
elif j == 0: max_of_left = A[i-1]
else: max_of_left = max(A[i-1], B[j-1])
if (m + n) % 2 == 1:
return max_of_left
if i == m: min_of_right = B[j]
elif j == n: min_of_right = A[i]
else: min_of_right = min(A[i], B[j])
return (max_of_left + min_of_right) / 2.0
nums1 = [1,2,4]
nums2 = [3,5,6]
print median(nums1, nums2) # 3.5
1.3 从无序列表查找数组第K大的数(O(n))
参考博客:https://blog.csdn.net/wenqiwenqi123/article/details/81669899
1、原理分析
1. 利用快排每次循环可以将元素分为左侧都比自己小,右侧都比自己大的思想
2. 找到k大概的位置,每次去一半即可
3. 为什么时间复杂度是O(n)而不是nlog(n)
1) 第一次排序需要遍历所有元素,时间复杂度为:n
2)第二次排序数量只有上次的一半:n/2
3) 所以总时间复杂度: n + n/2 + n/4 + n/8 + ...... + < 2n
注:这种方法如果又重复元素很容易导致死循环

#!/usr/bin/env python
# -*- coding:utf-8 -*-
def partition(num, low, high):
cur = num[low]
while low < high:
while low < high and num[high] > cur: # 从右向左比,直到遇到右边一个元素小于cur为止
high -= 1
while low < high and num[low] < cur: # 从左向右比,直到遇到左边一个元素大于cur为止
low += 1
num[low],num[high] = num[high],num[low] # 交互目前最大和最小位置
num[low] = cur
return low # 找到当前二分查找中间的位置下标
def findkth(num, low, high, k): # 找到数组里第k个数
index = partition(num, low, high)
if index == k: # 如果位置下标正好为k就已经找到
return num[index]
if index < k: # 如果k大于index说明要找的值在右面
return findkth(num, index + 1, high, k)
else: # 否则要找的k在左面
return findkth(num, low, index - 1, k)
l = [2, 3, 1, 5, 4, 6] # [1,2,3,4,5,6]
# pai = [2,2,2,2,2] # [1,2,3,4,5,6]
print findkth(l, 0, len(l) - 1, 3) # 第3号位置应该是:4

#!/usr/bin/env python
# -*- coding:utf-8 -*-
def heap_build(parent, heap):
child = 2 * parent + 1
while child < len(heap):
if child + 1 < len(heap) and heap[child + 1] < heap[child]:
child = child + 1
if heap[parent] <= heap[child]:
break
heap[parent], heap[child] = heap[child], heap[parent]
parent, child = child, 2 * child + 1
return heap
def Find_heap_kth(array, k):
if k > len(array):
return None
heap = array[:k]
for i in range(k, -1, -1):
heap_build(i, heap)
for j in range(k, len(array)):
if array[j] > heap[0]:
heap[0] = array[j]
heap_build(0, heap)
return heap[0]
l = [2, 1, 4, 3, 5, 9, 8, 0, 1, 3, 2, 5]
print(Find_heap_kth(l, 6))
1.4 求丑数
1、丑数定义
1. 根据丑数的定义,丑数应该是另一个丑数乘以2、3或者5的结果(1除外)。
2. 因此我们可以创建一个数组,里面的数字是排好序的丑数。
3. 里面的每一个丑数是前面的丑数乘以2、3或者5得到的。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def finduglynum(n):
uglynum = []
i = 1
count = 0
while True:
temp = i
while temp % 2 == 0:
temp = temp // 2
while temp % 3 == 0:
temp = temp // 3
while temp % 5 == 0:
temp = temp // 5
if temp == 1:
uglynum.append(i)
count += 1
if count >= n:
break
i += 1
return uglynum
# 测试
print finduglynum(8) # [1, 2, 3, 4, 5, 6, 8, 9]

#!usr/bin/env python
#encoding:utf-8
def finduglynum2(n):
uglynum = [1]
i = 1
t2 = m2 = 0
t3 = m3 = 0
t5 = m5 = 0
while i < n:
for x in range(t2, len(uglynum)):
m2 = uglynum[x] * 2
if m2 > uglynum[-1]:
t2 = x
# print("t2:",t2)
break
for x in range(t3, len(uglynum)):
m3 = uglynum[x] * 3
if m3 > uglynum[-1]:
t3 = x
break
for x in range(t5, len(uglynum)):
m5 = uglynum[x] * 5
if m5 > uglynum[-1]:
t5 = x
break
uglynum.append(min(m2, m3, m5))
i += 1
return uglynum
# 测试
print finduglynum2(10) # [1, 2, 3, 4, 5, 6, 8, 9, 10, 12]

这种思路的关键在于怎样确保数组里面的丑数是排好序的。 我们假设数组中已经有若干个丑数,排好序后存在数组中,我们把现有的最大丑数记做M。 现在我们来生成下一个丑数,该丑数肯定是前面某一个丑数乘以2、3或者5的结果。 我们首先考虑把已有的每个丑数乘以2。 在乘以2的时候,能得到若干个结果小于或等于M的。 由于我们是按照顺序生成的,小于或者等于M肯定已经在数组中了,我们不需再次考虑; 我们还会得到若干个大于M的结果,但我们只需要第一个大于M的结果,因为我们希望丑数是按从小到大顺序生成的,其他更大的结果我们以后再说。 我们把得到的第一个乘以2后大于M的结果,记为M2。 同样我们把已有的每一个丑数乘以3和5,能得到第一个大于M的结果M3和M5。 那么下一个丑数应该是M2、M3和M5三个数的最小者。 前面我们分析的时候,提到把已有的每个丑数分别都乘以2、3和5,事实上是不需要的,因为已有的丑数是按顺序存在数组中的。 对乘以2而言,肯定存在某一个丑数T2,排在它之前的每一个丑数乘以2得到的结果都会小于已有最大的丑数 在它之后的每一个丑数乘以2得到的结果都会太大。 我们只需要记下这个丑数的位置,同时每次生成新的丑数的时候,去更新这个T2。对乘以3和5而言,存在着同样的T3和T5。
1.5 最长递增子序列LIS的O(nlogn)的求法
1、说明
1. 最长递增子序列是指n个数的序列的最长单调递增子序列。
2. 比如,A = [1,3,6,7,9,4,10,5,6]的LIS是1 3 6 7 9 10。
2、实现一个复杂度O(nlogn)的算法
1. 如果 x 比所有的tails都大,说明x可以放在最长子序列的末尾形成一个新的自许下,那么就把他append一下,并且最长子序列长度增加1
2. 如果tails[i-1] < x <= tails[i],说明x需要替换一下前面那个大于x的数字,以便保证tails是一个递增的序列,那么就更新tails[i]
3. 这样维护一个tails变量,最后的答案就是这个长度。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def lengthOfLIS(nums):
tails = [0] * len(nums)
size = 0
for x in nums:
low = 0
high = size # size 0 1 2 3 3 统计当前列表最大单调长度
while low != high:
mid = (low + high) // 2 # 使用二分法找到x应该的位置
if tails[mid] < x: # x第一次为tials中间位置,如果x比较大,去tails后面的一半列表比较
# 直到找到tails中大于x元素的下标位置
low = mid + 1 # 使tails下标m加一(i加一后m就会加一)
else:
high = mid
tails[low] = x # 如果x比tails最后一个元素还大,就追加到tails末尾,否则替换嗲tials中第一个大于x的元素
size = max(low + 1, size)
return size
print lengthOfLIS([3, 4, 7, 2, 5]) # 3

比如我们的目标数组是[3, 4, 7, 2, 5]。 ####1. 第一步:x = 3 ''' 1)此时i = 0,直接令tails[0] = 3,tails = [3, 0, 0, 0, 0]。 2)说明到目前为止长度为1的递增子序列末尾最小为3。 ''' ####2. 第二步:x = 4 ''' 1)此时i != j,但是x大于tails的末尾,直接另tail[1] = 4, tails = [3, 4, 0, 0, 0]。 2)说明到目前为止长度为1的递增子序列末尾最小为3,长度为2的递增子序列末尾最小为4。 ''' ####3. 第三步:x = 7 ''' 1)大于tails的末尾,直接令tails[2] = 7,tails = [3, 4, 7, 0, 0]。 2)说明到目前为止长度为1的递增子序列末尾最小为3,长度为2的递增子序列末尾最小为4,长度为3的递增子序列末尾最小为7. ''' ####4. x = 2 ''' 1)此时x小于tails的末尾,需要用二分查找到比x大的最小的那个数更新之, 2)查找到tails中比2大的最小数是3,更新tail[0] = 2,此时tails = [2, 4, 7, 0, 0]。 3)说明到目前为止长度为1的递增子序列末尾最小为2,长度为2的递增子序列末尾最小为4,长度为3的递增子序列末尾最小为7。 ''' ####对第四步重点说明: ''' 1)这一步理解很关键,[2, 4, 7, 0, 0]的存在并不是说目前为止的递增子序列是2 4 7, 2)而是长度分别为1,2, 3的递增子序列目前所能得到的最小结尾元素是2,4,7。 3)我们这样做的目的就是,通过维护tails中的元素,保证每次对于长度为i+1的一个子序列对应的tails[i]元素最小, 4)“虽然在我之前,你们形成了一个长度为m的递增序列,但是呢,你们长度为m这个序列的末尾最大的一个数比我还大, 5)不如把我和末尾最大的那个元素换一下,这样你看咱们还是一个递增序列,长度也不变,但是我和你们更亲近”, 6)别的元素一听是这么个道理啊,于是就踢出最后一个元素,换上了这个新的更小的元素。 '''

tails的第i个位置记录nums中长度为i+1的所有递增子序列中,结尾最小的数字。 我们很容易证明,tails是一个递增的数组。 首先,tails[0]一定是所有元素中最小的那个数字min1,因为长度为1的子序列中,结尾最小的数字就是序列中最小的那个。 同样,长度为2的子序列中,结尾最小的的那个子序列的结尾元素一定大于min1, 因为首先所有长度为2的递增子序列,第二个元素一定比第一个元素大,如果长度为2的子序列中某个子序列的结尾元素小于min1, 那么在第一次操作中,这个元素就会更新为min1。 对于长度为3的子序列,假设之前tails已经存储了前两个结尾最小数[a, b], 若长度为三的子序列结尾数字c3小于b,即[c1, c2, c3]是一个递增子序列,且c3 < b,则必然有c2 < b, 这样和之前的结论b是长度为2的递增子序列结尾最小元素矛盾。 所以,通过这样的一步步的反证法,很容易证明tails一定是一个递增的数组。 那么很容易通过二分查找, 找到在tails数组中需要被更新的那个数。
11111111
来源:https://www.cnblogs.com/xiaonq/p/10488342.html
