目录
// 第三种方法, 使用位运算优化常数时间(位运算 运算速度最快)
前缀树:
生成前缀树方法,将字符作为路径的权值,挂上一个不为空的结点。没有此路径就新建,否则就复用。
案例展示:
/**
* @ProjectName: study3
* @FileName: TrieNode
* @author:HWJ
* @Data: 2023/6/10 17:18
*/
public class TrieNode {
public int pass;
public int end;
// HashMap<Char, Node> nexts;
// TreeMap<Char, Node> nexts; 如果字符种类特别多,可以用此方式来添加路
public TrieNode[] nexts;
public TrieNode() {
this.pass = 0;
this.end = 0;
// nexts[0] = null 没有走向‘a’的路
// nexts[0] != null 有走向‘a’的路
// ...
// nexts[25] != null 有走向‘a’的路
this.nexts = new TrieNode[26];
}
}
/**
* @ProjectName: study3
* @FileName: Trie
* @author:HWJ
* @Data: 2023/6/10 17:20
*/
public class Trie {
private TrieNode root;
public Trie() {
this.root = new TrieNode();
}
public void insert(String word) {
if (word == null) {
return;
}
char[] charArray = word.toCharArray();
TrieNode node = root;
node.pass++;
int index = 0;
for (int i = 0; i < charArray.length; i++) {
index = charArray[i] - 'a';
if (node.nexts[index] == null) {
node.nexts[index] = new TrieNode();
}
node = node.nexts[index];
node.pass++;
}
node.end++;
}
// word 这个单词之前加入过几次
public int search(String word) {
if (word == null) {
return 0;
}
char[] charArray = word.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < charArray.length; i++) {
index = charArray[i] - 'a';
if (node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
return node.end;
}
// 所有加入的字符串中,有几个是以pre这个字符串作为前缀的
public int prefixNumber(String pre) {
if (pre == null) {
return 0;
}
char[] charArray = pre.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < charArray.length; i++) {
index = charArray[i] - 'a';
if (node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
return node.pass;
}
public void delete(String word){
if (search(word) != 0) { // 确定树中确实加入过word,才删除
int index = 0;
TrieNode node = root;
node.pass--;
char[] charArray = word.toCharArray();
for (int i = 0; i < charArray.length; i++) {
index = charArray[i] - 'a';
if (--node.nexts[index].pass == 0){
node.nexts[index] = null;
return;
}
node = node.nexts[index];
}
node.end--;
}
}
}
贪心算法:
贪心算法解题思路:
贪心算法是得到的局部最优解 , 如何通过贪心算法得到全部最优解, 需要一定的技巧和练习;
安排会议宣讲的最好方法(使用贪心算法):
import java.util.Arrays;
import java.util.Comparator;
/**
* @ProjectName: study3
* @FileName: BestArrangement
* @author:HWJ
* @Data: 2023/6/10 20:18
*/
public class BestArrangement {
public static class Program{
public int start;
public int end;
public Program(int start, int end) {
this.start = start;
this.end = end;
}
}
public static class ProgramComparator implements Comparator<Program> {
@Override
public int compare(Program o1, Program o2) { // 以结束时间来升序排列, 先结束的项目先做
return o1.end - o2.end;
}
}
public static int bestArrange(Program[] programs, int timeStart){
int result = 0;
Arrays.sort(programs, new ProgramComparator());
for (Program program : programs) {
if (timeStart <= program.start){
timeStart = program.end;
result++;
}
}
return result;
}
}
import java.util.Arrays;
import java.util.Comparator;
/**
* @ProjectName: study3
* @FileName: StringMerge
* @author:HWJ
* @Data: 2023/6/10 20:58
*/
public class StringMerge {
public static void main(String[] args) {
String result = StringMerging(new String[]{"b", "ba"});
System.out.println(result);
}
public static class StringComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return (o1 + o2).compareTo(o2 + o1); // o1 和 o2 拼接 和 o2 和 o1 拼接进行比较,谁的字典序小,那个就排前面
}
}
public static String StringMerging(String[] strings){
Arrays.sort(strings, new StringComparator()); // 排完序后直接相加即可
StringBuilder result = new StringBuilder();
for (String s :strings) {
result.append(s);
}
return result.toString();
}
}
分金条,求最小代价:
public class LessMoney {
public static void main(String[] args) {
int[] arr = {20, 30, 10};
System.out.println(lessMoney(arr));
}
public static int lessMoney(int[] arr) {
PriorityQueue<Integer> pq = new PriorityQueue<>();
for (int i = 0; i < arr.length; i++) {
pq.add(arr[i]);
}
int sum = 0;
int cur = 0;
while (pq.size() > 1) {
cur = pq.poll() + pq.poll();
sum += cur;
pq.add(cur);
}
return sum;
}
}
求做项目的最大收益:
import java.util.Comparator;
import java.util.PriorityQueue;
/**
* @ProjectName: study3
* @FileName: IPO
* @author:HWJ
* @Data: 2023/6/10 21:29
*/
public class IPO {
public static class Node{
public int cost;
public int interest;
public Node(int cost, int interest) {
this.cost = cost;
this.interest = interest;
}
}
public static class minCost implements Comparator<Node>{
@Override
public int compare(Node o1, Node o2) {
return o1.cost - o2.cost;
}
}
public static class maxInterest implements Comparator<Node>{
@Override
public int compare(Node o1, Node o2) {
return o2.interest - o1.interest;
}
}
public static int getMaxMoney(int[] costs, int[] profits, int k, int m){
// k 表示你最多做的项目个数
// m 表示你的现有资金、
// costs 表示所有项目的花费
// profits 表示所有项目的收益
PriorityQueue<Node> minCost = new PriorityQueue<>(new minCost());
for (int i = 0; i < costs.length; i++) {
minCost.add(new Node(costs[i], profits[i])); // 以花销加入来创建小根堆
}
PriorityQueue<Node> maxProfit = new PriorityQueue<>(new maxInterest());
while (k != 0){
while (!minCost.isEmpty()) {
Node node = minCost.poll();
if (node.cost <= m) { // 如果能够完成的项目就加入大根堆(以收益来作为比较)
maxProfit.add(node);
}
}
if (maxProfit.isEmpty()){ // 如果此时还能做项目,但是没有满足条件的项目就返回
break;
}else {
k--;
Node node = maxProfit.poll();
m += node.interest; // 完成收益最高的项目,然后将其移除
}
}
return m;
}
}
利用大根堆和小根堆来得到一个数据流的中位数
// 利用大根堆和小根堆来得到一个数据流的中位数
// 如果新加入的数 <=大根堆,就进入大根堆,否则进入小根堆
//如果大根堆和小根堆数组大小相差达到二,则多的那个堆顶进入另一个堆
import java.util.Comparator;
import java.util.PriorityQueue;
/**
* @ProjectName: study3
* @FileName: searchAverage
* @author:HWJ
* @Data: 2023/6/10 21:54
*/
public class searchAverage {
public static void main(String[] args) {
double[] arr = {2,3,1,4,5,0};
System.out.println(average(arr));
}
public static class minNumber implements Comparator<Double> { // 实现大根堆
@Override
public int compare(Double o1, Double o2) {
return (int) (o2 - o1);
} // 这里返回整数,所以需要转型
}
public static class maxNumber implements Comparator<Double> { // 实现小根堆
@Override
public int compare(Double o1, Double o2) {
return (int) (o1 - o2);
}
}
public static double average(double[] arr) {
if (arr == null) {
return 0;
}
PriorityQueue<Double> min = new PriorityQueue<>(new minNumber()); // 实现大根堆, 放小数
PriorityQueue<Double> max = new PriorityQueue<>(new maxNumber()); // 实现小根堆, 放大数
min.add(arr[0]);
for (int i = 1; i < arr.length; i++) {
// 比较要加入的数,与大根堆的堆顶进行比较,如果小于等于堆顶就加入大根堆,否则加入小根堆
if (arr[i] <= min.peek()) {
min.add(arr[i]);
} else {
max.add(arr[i]);
}
// 如果两个的大小达到了2 就将多的那个的堆顶加入少的
if (max.size() - min.size() == 2) {
min.add(max.poll());
}else if (max.size() - min.size() == -2){
max.add(min.poll());
}
}
// 两个堆大小相等就返回两个堆顶的平均值
// 否则返回多的那个堆顶
if (min.size() == max.size()){
return (min.poll() + max.poll()) / 2;
}else if(min.size() > max.size()){
return min.poll();
}else {
return max.poll();
}
}
}
八皇后问题(N皇后问题): 时间复杂度为O(N^N)
没法对时间复杂度进行优化,但可以进行对常数时间的优化
// 第一种方法,含打印信息
/**
* @ProjectName: study3
* @FileName: Queen
* @author:HWJ
* @Data: 2023/6/11 9:41
*/
class Queen {
int count = 0; // 记录有多少方法
Queen() {
}
public boolean judge(int[] var1, int var2) {
// 判断加入的此行 和以前行是冲突
for(int var3 = 0; var3 < var2; var3++) {
// var1[var2] == var1[var3] 判断是否在同列
// Math.abs(var2 - var3) == Math.abs(var1[var2] - var1[var3] 判断是否在同斜线上
// 因为如果在同行会顶出数据 所以不做判断
if (var1[var2] == var1[var3] || Math.abs(var2 - var3) == Math.abs(var1[var2] - var1[var3])) {
return false;
}
}
return true;
}
public void putQueen(int[] var1, int var2, int n) {
//判断第n-1行和当前行是否冲突
if (this.judge(var1, n-1)) {
++this.count;
System.out.println("\n第" + this.count + "种方法为: ");
this.print(var1);
} else {
for(int var3 = 0; var3 < n; ++var3) {
var1[var2] = var3;
if (this.judge(var1, var2)) {// 如果不冲突就加入,然后继续寻找下一行的有效位置
this.putQueen(var1, var2 + 1, n); // 使用递归和循环的策略找到所有放置方法
}
}
}
}
public void print(int[] var1) {
// 构建一个棋盘列表
char[][] var2 = new char[var1.length][var1.length];
for(int var3 = 0; var3 < var2.length; ++var3) { // 遍历棋盘赋值,并打印
for(int var4 = 0; var4 < var2[var3].length; ++var4) {
var2[var3][var4] = '*';
var2[var3][var1[var3]] = 'Q';
System.out.print(var2[var3][var4] + " ");
}
System.out.println();
}
}
}
import java.util.Scanner;
/**
* @ProjectName: study3
* @FileName: QueenProblem
* @author:HWJ
* @Data: 2023/6/11 9:42
*/
public class QueenProblem {
public QueenProblem() {
}
public static void main(String[] var0) {
Scanner input = new Scanner(System.in);
int N = input.nextInt(); // 输入N 表示N*N的棋盘
int[] var1 = new int[N];
Queen var2 = new Queen();
// putQueen(var1, var2, N) var1表示棋盘 var1[2]=0, 表示当前第三行第一列已经有一个皇后
// var2 表示当前需要在第几行添加数据
// N 表示一共有多少行
var2.putQueen(var1, 0, N);
System.out.println("一共有" + var2.count + "种方法");
}
}
// 第二种方法 不含打印信息
import java.util.Date;
/**
* @ProjectName: study3
* @FileName: Queen2
* @author:HWJ
* @Data: 2023/6/11 10:27
*/
public class Queen2 {
public static void main(String[] args) {
Date date = new Date();
System.out.println(num1(14));
Date date1 = new Date();
System.out.println(date1.getTime() - date.getTime());
}
public static int num1(int n){
if(n < 1){ // 如果传入数据为0或者负数,直接返回0;
return 0;
}
int[] record = new int[n];
// 0 表示当前需要加入数据的行,record表示棋盘 record[2]=0, 表示当前第三行第一列已经有一个皇后
return process1(0, record, n);
}
public static int process1(int i, int[] record, int n){
if (i == n){ // 如果所有行都加入了,则找到了一种方法。
return 1;
}
int res = 0;
for (int j = 0; j < n; j++) {
if (isValid(record, i, j)){ // 先判断j这个数是否能加入到第i行,如果能加入就加入,并寻找下一行
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 - k) == Math.abs(j - record[k])){
return false;
}
}
return true;
}
}
// 第三种方法, 使用位运算优化常数时间(位运算 运算速度最快)
import java.util.Date;
/**
* @ProjectName: study3
* @FileName: Queen3
* @author:HWJ
* @Data: 2023/6/11 11:27
*/
public class Queen3 {
public static void main(String[] args) {
Date date = new Date();
System.out.println(num(14));
Date date1 = new Date();
System.out.println(date1.getTime() - date.getTime());
}
// 这里使用位运算 限制不能超过32皇后问题, 如果想求更大的问题,可以将int换为long即可求不超过64皇后的问题
public static int num(int n){
if (n < 1 || n > 32){
return 0;
}
int limit = n == 32? -1:(1<<n)-1;
return process(limit, 0, 0, 0);
}
public static int process(int limit, //总限制,将放皇后的位置进行限制
int colLim, // 列限制
int leftDiaLim, // 左斜线限制
int rightDiaLim ) { // 右斜线限制,均是1不能放皇后 ,0可以放皇后
if (colLim == limit){
return 1;
}
// 得到所有候选皇后的位置
int pos = limit & (~(colLim | leftDiaLim | rightDiaLim)); // 用于寻找当前行的限制
int mostRightOne = 0; // 找到当前行可以放皇后的最右位置
int res = 0;
while (pos != 0){
mostRightOne = pos & (~pos + 1);
pos -= mostRightOne;
res += process(limit, colLim | mostRightOne, // 得到下一行的列限制, 左斜线限制, 右斜线限制
(leftDiaLim | mostRightOne)<<1,
(rightDiaLim | mostRightOne)>> 1);
}
return res;
}
}