1 贪心算法
贪心算法:常用堆和排序
1.1 题目一:会议问题
class Program: # 每个会议有开始时间,结束时间
def __init__(self,start,end):
self.start = start
self.end = end
# 方法一。暴力,穷举
def bestArrange1(self,programs):
if programs is None or len(programs) == 0:
return 0
return self.process(programs,0,0)
# 还剩什么会议都放在programs里
# done 已经安排会议的数量
# timeLine 目前来到的时间点是什么
# 目前来到timeLine的时间点,已经安排了done多的会议,剩下的会议programs可以自由安排
# 返回能安排的最多会议数量
def process(self,programs,done,timeLine):
if len(programs) == 0:
return done # 会议全安排完了,返回已经安排的会议
# 如果有会议可以选,进行下面操作
max = done
Programs = [] # 新会议数组
# 当前安排的会议是什么会议,每一个都枚举
for i in range(len(programs)):
if programs[i].start >= timeLine:
Programs.append(programs.pop(i)) # 将第i号会议删除,并生成一个新的programs数组
if max < self.process(Programs,done+1,programs[i].end):
max = self.process(Programs,done+1,programs[i].end) # Programs为新安排会议,开会后时间变为programs[i].end
return max
# 方法二:贪心算法
def bestArrange2(self,programs):
# 什么会议结束时间早,什么会议排在前面
self.paixu(programs)
timeLine = 0
result = 0
for i in range(len(programs)):
if timeLine <= programs[i].start:
result = result + 1
timeLine = programs[i].end
return result
def paixu(self,programs):
programss = []
while len(programs) != 0:
min = programs[0].end
index = 0
for i in range(1,len(programs)):
if min > programs[i].end:
min = programs[i].end
index = i
programss.append(programs.pop(index))
return programss
1.2 字符串x和.,灯照亮问题
这个暴力太难了
# 灯问题
# 方法一:纯暴力
def minLight1(road):
if road is None or len(road) == 0: # 路没了
return 0
return process(list(road),0,dict()) # 0位置开始做决定,还没放灯
# 10难,存在缺陷,难
# str[index....]位置,自由选择放灯还是不放灯
# str[0...index-1]位置呢?已经做完决定了,那些放了灯的位置,存在lights里
# 要求选出能照亮所有.的方案,并且在这些有效的方案中,返回最少需要几个灯
def process(str,index,lights):
if index == len(str): # 当index来到终止位置时,没有位置放灯了
# 这种方案能不能把所有位置都照亮
for i in range(len(str)):
if str[i] != 'X': # 如果当前位置为点,查询是否被照亮
if not lights.get(i-1) and not lights.get(i) and not lights.get(i+1): # 如果没有被照亮
return 11111 # 返回整数最大值作为灯的数量
return len(lights) # 有多长就放了几盏灯
else: # str没有结束
# i 位置是x或点,都可以选择不放灯
yes = 11111
no = process(str,index+1,lights) # 没有把i位置扔到lights里去,i位置加一,i位置不放灯
if str[index] == '.':
lights.update({index:1})
yes = process(str,index+1,lights)
lights.pop(index) # 恢复现场
min = 0
if no > yes:
min = yes
else:
min = no
return min
方法二:贪心
# 方法二:贪心
def minLight2(road):
str = list(road)
index = 0
light = 0 # 当前灯数量
while index < len(str): # 路没有全部遍历时
if str[index] == 'X': # 当走到x,不放灯
index += 1 # 路往后走
else:
light += 1 # 当走到点时,一定放灯,灯的数量加,但是后面决定灯放在哪一
if index + 1 == len(str): # 如果放灯的数量等于路的长度,跳出循环
break
else:
if str[index + 1] == 'X': # 如果下一个是X
index = index + 2 # 往后跳两个再决定
else:
index = index + 3
return light # 返回灯数量
road = "xxx.x.x.x."
print(minLight2(road))
1.3
ks + te ->ks*26的2次方+te = ks00+te = kste
def lowestString(strs):
if strs == None or len(strs) == 0:
return ""
a = sorted(strs)
t = "".join(a) # 将排序后的列表转为字符串
res = ""
for i in range(len(t)):
res = res + strs[i]
return res
1.4 返回分割最小代价
创建小根堆,弹两个数据结合后扔回去,堆里只剩一个数字停,所有叶子节点加起来就是代价最小(和哈夫曼树差不多)
# 小根堆
class Heap:
def __init__(self):
self.heap = []
self.heapSize = 0 # 堆当前大小
def push(self,val):
self.heap.append(val) # 新值在heapSize上
self.heapInsert(self.heap,self.heapSize)
self.heapSize = self.heapSize + 1 # heapSize有值的后一位
def heapInsert(self,heap,heapSize): # 小根堆规则,(heapSize - 1) / 2,可以找到父节点位置
while heap[int(heapSize)] < heap[int((heapSize - 1) / 2)]: # 如果小于父节点,那么就交换
self.swap(heap,heapSize,int((heapSize - 1) / 2))
heapSize = int((heapSize - 1) / 2)
def swap(self,arr, i, j):
tmp = arr[i]
arr[i] = arr[j]
arr[j] = tmp
# 用户此时,返回最大值,并且在大根堆中,把最小值删除
# 剩下的数,依然保持小根堆组织
def pop(self):
self.heapSize = self.heapSize - 1 # 最后一个值的位置
self.swap(self.heap,0,self.heapSize) # 第0位和最后一位调换位置,最后一位为最小值
ans = self.heap.pop()
self.heapify(self.heap,0,self.heapSize) # 重新修复为小根堆
return ans
# 从index位置往下,不断下沉
# 停:孩子都不再比我大 或 已经没有孩子了
def heapify(self,heap,index,heapSize):
left = index * 2 + 1 # 左孩子下标
while left < heapSize: # 防止越界,说明我是有左孩子的,没有越界往下执行,没有左孩子就没孩子了
# 左右两个孩子中,谁小,谁把自己的下标给largest
# 条件:1.有右孩子 && 2. 右孩子的值比左孩子的值大
# 否则为左孩子
if left + 1 < heapSize and heap[left+1] < heap[left]:
largest = left + 1 # 那么右孩子位置给largest
else:
largest = left
# largest是最大孩子的位置
if heap[largest] >= heap[index]: # 如果父节点值小于两孩子,lar就指向父节点
largest = index
if largest == index: # 如果largest是父节点位置,不用交换,退出循环
break
self.swap(heap,largest,index)
index = largest
left = index * 2 + 1 # 继续找下一个节点的左孩子
def lessMoney(arr):
pQ = Heap() # 小根堆
for i in range(len(arr)):
pQ.push(arr[i])
sum = 0
while pQ.heapSize > 1: # 只剩下一个元素,停止
cur = pQ.pop() + pQ.pop() # 弹出两个最小的
sum = sum + cur
pQ.push(cur) # 结合后放回去
return sum
arr = [10,20,30]
print(lessMoney(arr))
1.5 项目花费最少
class Program:
def __init__(self,p,c):
self.p = p # 利润
self.c = c # 花费
# 最多k个项目
# w是初始资金
# profits[] 和 Capital[] 一定等长
# 返回最终最大资金
def findMaximizedCapital(k,w,Profits,Capital):
minCostQ = []
maxProfitQ = []
for i in range(len(Profits)): # 以项目类型加入到minCostQ列表中
minCostQ.append(Program(Profits[i],Capital[i]))
minCostQ = sorted(minCostQ, key=lambda x: x.c) # minCostQ,按照花费值排序,从小到大
for i in range(k): # 最多完成k个项目
while minCostQ and minCostQ[0].c <= w: # 花费组不为空,且花费组最少的花费小于w的
maxProfitQ.append(minCostQ.pop(0)) # 将花费组第一个元素弹出,也就是取出最小花费项目放到maxProfitQ数组
maxProfitQ = sorted(maxProfitQ, key=lambda x: x.p,reverse=True) # 加入的新元素后,对整体以利润的方式排序,最前面的利润最大
if not maxProfitQ: # 如果利润列表为空,结束
return w
w = w + maxProfitQ.pop(0).p # 资金叠加,弹出最大利润
return w
Profits = [2,4,3,5,8]
Capital = [1,3,1,3,4]
w = findMaximizedCapital(2,1,Profits,Capital)
print(w)