贪心算法
局部最优->全局最优
暴力解+对数器验证贪心策略
Q1:使若干字符串组成的字符串字典序最小
根据字符串字典序排序 再组合 ba,b显然错误
根据AB<BA的贪心策略排序,满足题意。(AB<BA)(BC<CB) ->(AC<CA)
Q2:切金条问题,长度为n的金条需要花费n金币,需要将长度为n的金条分成所需要的长度(a,b,c...)
哈夫曼树
Q3:会议问题,知道会议开始和结束的时间,如何使一天安排最多的会议。
根据结束时间最早排序
Q4:有M本金,可以完成k个项目(不可并行),知道项目的启动资金和收益,求最大收益。
两个堆,一个小顶堆(启动资金排序)用于存放启动不了的项目,一个大顶堆(收益排序)存放启动资金足够的项目,但大顶堆中无项目或者已完成K个项目停止。
堆的应用
Q1:随时取得数据流的中位数。
大致思路:用两个堆分别存放N/2的数,堆顶即为中位数
小顶堆存放大于中位数的数,大顶堆存放小于中位数的数。数据小于等于大顶堆时,属于中位数前,放大顶堆,大于时放小顶堆。当两堆的数据个数差大于2时,个数多的堆顶进个数小的堆。
前缀树
结点含pass,end值,结点数组分别表示多少以这个为前缀,多少个这个字符串,多少条往下的道路
创造和访问都直接按照字符串访问前缀树即可。
N皇后问题
朴素解法:
public static int num1(int n){
if(n<1) return 0;
//存放皇后位置 record[i]=j表示第i行的皇后放在j列
int record[]=new int[n];
return process1(0,record,n);
}
public static int process1(int i,int[] record,int n){
//前0到 i-1行已经摆好,找i行应该放哪
if(i==n) return 1; //0到i-1摆好则找到一种正确摆法
int res=0;
for(int j=0;j<n;j++){
if(isValid(record,i,j)){
record[i]=j;
//此处是递归回溯的关键,过程类似树的遍历
res+=process1(i+1,record,n);
}
}
return res;
}
public static boolean isValid(int[] record,int i,int j){
for(int k=0;k<i;k++){
//同列或在斜线上
if(record[k]==j||(Math.abs(i-j)==Math.abs(record[i]-j))){
return false;
}
}
return true;
}
位运算优化:通过int类型的位数32位表示皇后所在的位置,只能实现32皇后之内,受限于数据位数。
public static int num1(int n){
if(n<1 || n>32) return 0;
//limit的后n位为1,表示可以摆放的位置
int limit=n==32? -1:(1<<n)-1;
return process1(limit,0,0,0);
}
public static int process1(int limit,int colLim,int leftLim,int rightLim){
//limit表示全摆放完的状态,colLim、leftLim、rightLim标记不可摆放的位置。
if(colLim==limit) return 1;
int pos=limit&(~(colLim|leftLim|rightLim));
//找到不受限的位置
//limit 000 1 1 1 1 1 1 1 1
//col 000 0 0 0 1 0 0 0 0
//left 000 0 0 1 0 0 0 0 0
//right 000 0 0 0 0 1 0 0 0
// 000 0 0 1 1 1 0 0 0
// 111 1 1 0 0 0 1 1 1
//pos 000 1 1 0 0 0 1 1 1 此处1表示剩下5行可以摆放的位置
int mostRightOne=0;
int res=0;
while(pos!=0){
//mostRightOne表示选择的列
mostRightOne=pos&(~pos+1);
pos=pos-mostRightOne;
res+=process1(limit,colLim|mostRightOne,
leftLim|mostRightOne<<1,
rightLim|mostRightOne>>>1);
//>>>逻辑右移,高位补0
}
return res;
}