二分查找[ 非递归 ]
/**
* 二分查找非递归实现
*
* @author qb
* @version 1.0
* @since 2022/3/7 11:30
*/
public class BinarySearchNoRecur {
public static void main(String[] args) {
int[] arr = {1,3,8,10,11,67,100};
int i = binarySearch(arr, 100);
System.out.println(i);
}
/**
*
* @param arr 带查找的数组,是升序的
* @param target 需要查找的数据
* @return 对应下标 -1表示没有找到
*/
public static int binarySearch(int[] arr, int target){
int left = 0;
int right = arr.length -1;
while (left <= right){
//说明继续查找
int mid = (left + right) / 2;
if(arr[mid] == target){
return mid;
}
else if(arr[mid] > target){
//需要向左边查找
right = mid -1;
}
else{
left = mid + 1;
}
}
return -1;
}
}
分治算法
/**
* 分治算法
*
* @author qb
* @version 1.0
* @since 2022/3/7 13:34
*/
public class DAC {
public static void main(String[] args) {
hanoiTower(5,'A','B','C');
}
//汉诺塔的移动方法
//使用分治算法
public static void hanoiTower(int num,char a,char b,char c){
if(num == 1){
System.out.println("第一个盘从 "+ a + "->" +c);
}else{
hanoiTower(num -1,a,c,b);
System.out.println("第" +num +"个盘从 "+a +"->"+c);
hanoiTower(num-1,b,a,c);
}
}
}
KMP算法
/**
* KMP算法
*
* @author qb
* @version 1.0
* @since 2022/3/8 10:51
*/
public class KMPAlgorithm {
public static void main(String[] args) {
String str1 = "BBC ABCDAB ABCDABCDABDE";
String str2 = "ABCDABD";
System.out.println();
int[] nest = kmpNext("ABCDABD");
System.out.println(Arrays.toString(nest));
// int bBc = kmpSearch(str1, str2, nest);
// System.out.println(bBc);
}
/**
* kmp的搜索算法
* @param str1 源字符串
* @param str2 子串
* @param next 部分匹配表,子串对应的部分匹配表
* @return int -1没有匹配到 ,反之返回第一个匹配的位置
*/
public static int kmpSearch(String str1,String str2,int[] next){
//遍历
for (int i = 0,j=0; i < str1.length(); i++) {
//需要处理 str1.charAt(i) != str2.charAt(j)
//KMP 的核心,去调整j的大小
while ( j > 0 && str1.charAt(i) != str2.charAt(j)){
j = next[j-1];
}
if(str1.charAt(i) == str2.charAt(j)){
j++;
}
if(j == str2.length()){
return i - j +1;
}
}
return -1;
}
/**
* 获取一个字符串的部分匹配值
*/
public static int[] kmpNext(String dest){
//创建一个数组,保存部分匹配值
int[] next = new int[dest.length()];
//如果字符串长度为1,部分匹配值就是0
next[0] = 0;
for (int i = 1,j=0; i < dest.length(); i++) {
System.out.println("for: "+ dest.charAt(i)+","+dest.charAt(j));
//当dest.charAt(i) != dest.charAt(j)时,我们需要从next[j-1],获取新的节点,
// 直到我们发现有dest.charAt(i) == dest.charAt(j)条件满足时退出
while (j > 0 && dest.charAt(i) != dest.charAt(j)){
System.out.println("while: "+ dest.charAt(i)+","+dest.charAt(j));
//kmp算法的核心 TODO: 此处的意思还不是很懂
j = next[j-1];
}
//当条件满足时,部分匹配值就是要+1
if(dest.charAt(i) == dest.charAt(j)){
j++;
}
next[i] = j;
}
return next;
}
}
贪心算法
/**
* 贪心算法
*
* @author qb
* @version 1.0
* @since 2022/3/8 13:59
*/
public class Greedy {
public static void main(String[] args) {
//创建广播电台 ,放入到map中
HashMap<String, HashSet<String>> broadcasts = new HashMap<>();
//将各个电台放入到broadcasts
HashSet<String> hashSet1 = new HashSet<>();
hashSet1.add("北京");
hashSet1.add("上海");
hashSet1.add("天津");
HashSet<String> hashSet2 = new HashSet<>();
hashSet2.add("广州");
hashSet2.add("上海");
hashSet2.add("深圳");
HashSet<String> hashSet3 = new HashSet<>();
hashSet3.add("成都");
hashSet3.add("上海");
hashSet3.add("杭州");
HashSet<String> hashSet4= new HashSet<>();
hashSet4.add("上海");
hashSet4.add("天津");
HashSet<String> hashSet5= new HashSet<>();
hashSet5.add("杭州");
hashSet5.add("大连");
//加入到map
broadcasts.put("k1",hashSet1);
broadcasts.put("k2",hashSet2);
broadcasts.put("k3",hashSet3);
broadcasts.put("k4",hashSet4);
broadcasts.put("k5",hashSet5);
HashSet<String> allAreas = new HashSet<>();
allAreas.add("北京");
allAreas.add("上海");
allAreas.add("天津");
allAreas.add("广州");
allAreas.add("深圳");
allAreas.add("成都");
allAreas.add("杭州");
allAreas.add("大连");
//创建arrayList存放选择的电台集合
List<String> selects = new ArrayList<>();
//定义一个;临时的集合,在遍历的过程中,存放遍历过程中的电台覆盖的地区和当前还没有覆盖的地区的交集
HashSet<String> tempSet = new HashSet<>();
//定义maxKey,保存在一次遍历过程中,能够覆盖最大未覆盖的地区对应的电台的key
String maxKey ;
//如果maxKey不为null,则会放入到selects
while (allAreas.size() != 0){
//如果allAreas 不为0,则表示还没有覆盖到所有的地区
maxKey = null;
//遍历
for (String key : broadcasts.keySet()){
HashSet<String> areas = broadcasts.get(key);
tempSet.clear();
tempSet.addAll(areas);
//求出两个tempSet和 allAreas的交集,交集会赋给tempSet
tempSet.retainAll(allAreas);
//如果当前的集合包含的未覆盖地区的数量,比maxKey的指向的集合的地区还多
//就需要重置maxKey
// tempSet.size() > broadcasts.get(maxKey).size() 体现贪心算法
if(tempSet.size() > 0 &&
(maxKey == null || tempSet.size() > broadcasts.get(maxKey).size())){
maxKey = key;
}
}
//maxKey != null ,就应该将maxKey 加入selects
if(maxKey != null){
selects.add(maxKey);
//将maxKey指向的集合,从allAreas中去掉
allAreas.removeAll(broadcasts.get(maxKey));
}
}
//[k1,k2,k3,k5]
System.out.println("得到的结果:"+selects);
}
}
普里姆算法 [ MST ]
/**
* 普里姆算法
*
* @author qb
* @version 1.0
* @since 2022/3/8 15:17
*/
public class PrimAlgorithm {
public static void main(String[] args) {
//创建图
char[] data = new char[]{'A','B','C','D','E','F','G'};
int verxs = data.length;
//邻接矩阵的关系
int[][] weight = new int[][]{
/*A*/ /*B*/ /*C*/ /*D*/ /*E*/ /*F*/ /*G*/
/*A*/{10000, 5, 7, 10000, 10000, 10000, 2},
/*B*/{5, 10000, 10000, 9, 10000, 10000, 3},
/*C*/{7, 10000, 10000, 10000, 8, 10000, 10000},
/*D*/{10000, 9, 10000, 10000, 10000, 4, 10000},
/*E*/{10000, 10000, 8, 10000, 10000, 5, 4},
/*F*/{10000, 10000, 10000, 4, 5, 10000, 6},
/*G*/{2, 3, 10000, 10000, 4, 6, 10000}
};
MGraph mGraph = new MGraph(verxs);
MinTree minTree = new MinTree();
minTree.createGraph(mGraph,verxs,data,weight);
minTree.showGraph(mGraph);
//
minTree.prim(mGraph,1);
}
}
/**
* 创建最小生成树
*/
class MinTree{
/**
*
* @param graph 图对象
* @param verxs 图对应的顶点个数
* @param data 图的各个定点的值
* @param weight 图的邻接矩阵
*/
public void createGraph(MGraph graph,int verxs,char[] data,int[][] weight){
int i,j;
for (i=0;i<verxs;i++){
graph.data[i] = data[i];
for (j = 0; j < verxs ; j++) {
graph.weight[i][j] = weight[i][j];
}
}
}
/**
* 显示图
*/
public void showGraph(MGraph graph){
for (int[] link : graph.weight)
{
System.out.println(Arrays.toString(link));
}
}
/**
* 编写pirm,得到最小生成树
* @param graph 图
* @param v 表示从图的第几个定点开始生成
*/
public void prim(MGraph graph,int v){
//标记定点是否被访问过
int[] visited = new int[graph.verxs];
//把当前节点标记为已访问
visited[v] = 1;
//用h1和h2记录两个顶点的下标
int h1 = -1;
int h2 = -1;
int minWeight = 10000;
for (int k = 1; k < graph.verxs; k++) {
//因为有 graph.verxs 个顶点,普里姆算法结束后,有 graph.verxs - 1 条边
//这个是确定每一次生成的子图,和哪儿个节点的距离最近
for (int i = 0; i < graph.verxs; i++) {
// i 节点标识被访问过的顶点
for (int j = 0; j < graph.verxs; j++) {
// j 还没有访问过的节点
if(visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight){
//寻找 i 与未访问过的j的最小的值
minWeight = graph.weight[i][j];
h1 = i;
h2 = j;
}
}
}
//找打最小的边
System.out.println("边 <"+graph.data[h1]+","+graph.data[h2] +"> 权值" +minWeight);
visited[h2] = 1;
// minWeight 重新设置
minWeight = 10000;
}
}
}
class MGraph{
/**
* 表示图的节点个数
*/
int verxs;
/**
* 存放节点数据
*/
char[] data;
/**
* 存放边,就是我们的邻接节点
*/
int[][] weight;
public MGraph(int verxs){
this.verxs = verxs;
data = new char[verxs];
weight = new int[verxs][verxs];
}
}
克鲁斯卡尔算法
/**
* 克鲁斯特尔
*
* @author qb
* @version 1.0
* @since 2022/3/8 16:33
*/
public class KruskalCase {
/**
* 边的个数
*/
private int edgeNum;
/**
* 顶点数组
*/
private char[] vertexs;
/**
* 邻接矩阵
*/
private int[][] matrix;
/**
* 表示两个顶点不能连通
*/
private static final int INF = Integer.MAX_VALUE;
public static void main(String[] args) {
char[] vertexs = {'A','B','C','D','E','F','G'};
int[][] matrix = {
/*A*/ /*B*/ /*C*/ /*D*/ /*E*/ /*F*/ /*G*/
/*A*/{ 0, 12, INF, INF, INF, 16, 14},
/*B*/{ 12, 0, 10, INF, INF, 7, INF},
/*C*/{ INF, 10, 0, 3, 5, 6, INF},
/*D*/{ INF, INF, 3, 0, 4, INF, INF},
/*E*/{ INF, INF, 5, 4, 0, 2, 8},
/*F*/{ 16, 7, 6, INF, 2, 0, 9},
/*G*/{ 14, INF, INF, INF, 8, 9, 0}
};
//创建实例
KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);
kruskalCase.print();
EData[] edges = kruskalCase.getEdges();
kruskalCase.kruskal();
}
public KruskalCase(char[] vertexs,int[][] matrix){
//初始化定点数和边的个数
int vLen = vertexs.length;
//初始化顶点
this.vertexs = new char[vLen];
for (int i = 0; i < vertexs.length; i++) {
this.vertexs[i] = vertexs[i];
}
//初始化边
this.matrix = new int[vLen][vLen];
for (int i = 0; i < vLen; i++) {
for (int j = 0; j < vLen; j++) {
this.matrix[i][j] = matrix[i][j];
}
}
//统计边
for (int i = 0; i < vLen; i++) {
for (int j = i+1; j < vLen; j++) {
if(this.matrix[i][j] != INF){
edgeNum ++ ;
}
}
}
}
public void kruskal(){
//标识最后结果数组的索引
int index = 0;
//用于保存已有最小生成树中的每个顶点在最小生成树中的终点
int[] ends = new int[edgeNum];
//创建结果数组,保存最后的最小生成树
EData[] rets = new EData[edgeNum];
//获取图中所有的边的集合,一共有12条边
EData[] edges = getEdges();
System.out.println(Arrays.toString(edges));
//按照边的权值大小进行排序
sortEdge(edges);
//遍历edges 数组,将边添加到最小生成树中,判断是否形成了回路,没有就加入rets
for (int i = 0; i < edgeNum; i++) {
//获取到第i条边的第一个顶点(起点)
int p1 = getPosition(edges[i].start);
//获取到第i条边的第二个顶点(起点)
int p2 = getPosition(edges[i].end);
//获取p1这个顶点在已生成的最小生成树中的终点
int m = getEnd(ends,p1);
//获取p2在已有最小生成树中的终点
int n = getEnd(ends,p2);
//判断是否构成回路
if(m != n){
//说明不够成回路
//设置m 在“已有最小生成树”中的终点
ends[m] = n;
//有一条边加入到rets中
rets[index++] = edges[i];
}
}
System.out.println("最小生成树为:");
for (int i = 0; i < index; i++) {
System.out.println(rets[i]);
}
}
public void print(){
System.out.println("邻接矩阵为:");
for (int i = 0; i < vertexs.length; i++) {
for (int j = 0; j < vertexs.length; j++) {
System.out.printf("%12d\t",matrix[i][j]);
}
System.out.println();
}
}
/**
* 对边进行排序处理,冒泡
*/
private void sortEdge(EData[] edges){
for (int i = 0; i < edges.length - 1; i++) {
for (int j = 0; j < edges.length - 1 - i; j++) {
if(edges[j].weight > edges[j+1].weight){
EData tmp = edges[j];
edges[j] = edges[j + 1];
edges[j+1] = tmp;
}
}
}
}
/**
*
* @param ch 传入的顶点的值
* @return 返回的ch对应的下标, -1为未找到
*/
public int getPosition(char ch){
for (int i = 0; i < vertexs.length; i++) {
if(vertexs[i] == ch){
return i;
}
}
return -1;
}
/**
* 获取图中的边,放到EData数组中,后面我们需要遍历该数组
* 是通过matrix邻接矩阵来获取
* EData[] -> [['A','B',12],['B','F',7]]
* @return
*/
private EData[] getEdges(){
int index =0;
EData[] edges = new EData[edgeNum];
for (int i = 0; i < vertexs.length; i++) {
for (int j = i+1; j < vertexs.length ; j++) {
if(matrix[i][j] != INF){
edges[index++] = new EData(vertexs[i],vertexs[j],matrix[i][j]);
}
}
}
return edges;
}
/**
* 获取下标为i的顶点的终点,用于判断后面两个顶点的中间是否相同
* @param ends 记录了各个顶点对应的终点是哪儿个,ends 是在遍历过程中逐步形成的
* @param i 传入的顶点对应的下标
* @return 下标为i的,这个顶点对应的终点的下标
*/
private int getEnd(int[] ends,int i){
while (ends[i] != 0){
i = ends[i];
}
return i;
}
}
/**
* 对象实例表示 一条边
*/
class EData{
/**
* 边的起点
*/
char start;
/**
* 边的终点
*/
char end;
/**
* 边的权值
*/
int weight;
public EData(char start,char end,int weight){
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public String toString() {
return "EData{" +
"<" + start +
", " + end +
"> =weight=" + weight +
'}'+"\t";
}
}
骑士周游问题
/**
* 马踏棋盘
* @author qb
* @version 1.0
* @since 2022/3/9 9:45
*/
public class HorseChessBoard {
/**
* 棋盘的列
*/
private static int X;
/**
* 棋盘的行
*/
private static int Y;
/**
* 标记棋盘的各个位置是否被访问过
*/
private static boolean visited[];
/**
* 是否棋盘的所有位置都被访问了
*/
private static boolean finished = false;
public static void main(String[] args) {
System.out.println("start");
X = 6;
Y = 6;
//马儿走的初始位置的行
int row = 2;
//马儿走的初始位置的列
int column = 4;
//创建棋盘
int[][] chessBoard = new int[X][Y];
visited = new boolean[X*Y];
//
long start = System.currentTimeMillis();
traversalChessBoard(chessBoard,row-1,column-1,1);
long end = System.currentTimeMillis();
System.out.println("共耗时: "+ (end - start)/1000 +" s");
//输出棋盘的最后情况
for (int[] rows: chessBoard) {
for (int step :rows){
System.out.print(step +"\t");
}
System.out.println();
}
}
/**
* 骑士周游问题的算法
* @param chessBoard 棋盘
* @param row 马儿当前位置的行 从0开始
* @param column 马儿当前位置的列 从 0
* @param step 马儿走到了第几步,初始位置就是第一步
*/
public static void traversalChessBoard(int[][] chessBoard,int row,int column,int step){
chessBoard[row][column] = step;
//马儿当前的位置
visited[row * X + column] = true;
//获取当前可以走的下一个位置的集合,此处column就是x 列, row就是行y
List<Point> ps = next(new Point(column, row));
//遍历ps
while (!ps.isEmpty()){
//取出下一个可以走的位置
Point p = ps.remove(0);
//判断是否已经访问过
if(!visited[p.y * X +p.x]){
traversalChessBoard(chessBoard,p.y,p.x,step+1);
}
}
//判断马儿是否完成了任务,使用step 和应该走的步数比较
//如果没有打到数量,则表示没有完成任务,将整个棋盘置0
if(step < X*Y && !finished){
chessBoard[row][column] = 0;
visited[row*X+column] = false;
}else{
finished = true;
}
}
/**
* 根据当前的位置,计算马儿还能走哪儿些位置,并放在list中,最多有8个位置
* @param curPoint 当前点
* @return List<Point> 可走的点集合
*/
public static List<Point> next(Point curPoint){
List<Point> ps = new ArrayList<>();
//创建一个point
Point p1 = new Point();
// curPoint.x - 2 向左移动两列,curPoint.y-1 向上移动一行
// 表示马儿可以走 5
if((p1.x = curPoint.x - 2) >=0 && (p1.y = curPoint.y-1) >=0){
ps.add(new Point(p1));
}
//判断马儿能不能走6
if((p1.x = curPoint.x - 1) >=0 && (p1.y = curPoint.y-2) >=0){
ps.add(new Point(p1));
}
//判断马儿能不能走7
if((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y-2) >=0){
ps.add(new Point(p1));
}
//判断马儿能不能走0
if((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y-1) >=0){
ps.add(new Point(p1));
}
//判断马儿能不能走1
if((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y){
ps.add(new Point(p1));
}
//判断马儿能不能走2
if((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y){
ps.add(new Point(p1));
}
//判断马儿能不能走3
if((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y){
ps.add(new Point(p1));
}
//判断马儿能不能走4
if((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y){
ps.add(new Point(p1));
}
return ps;
}
}
贪心算法优化
/**
* 马踏棋盘
* @author qb
* @version 1.0
* @since 2022/3/9 9:45
*/
public class HorseChessBoard {
/**
* 棋盘的列
*/
private static int X;
/**
* 棋盘的行
*/
private static int Y;
/**
* 标记棋盘的各个位置是否被访问过
*/
private static boolean visited[];
/**
* 是否棋盘的所有位置都被访问了
*/
private static boolean finished = false;
public static void main(String[] args) {
System.out.println("start");
X = 8;
Y = 8;
//马儿走的初始位置的行
int row = 1;
//马儿走的初始位置的列
int column = 1;
//创建棋盘
int[][] chessBoard = new int[X][Y];
visited = new boolean[X*Y];
//
long start = System.currentTimeMillis();
traversalChessBoard(chessBoard,row-1,column-1,1);
long end = System.currentTimeMillis();
System.out.println("共耗时: "+ (end - start)/1000 +" s");
//输出棋盘的最后情况
for (int[] rows: chessBoard) {
for (int step :rows){
System.out.print(step +"\t");
}
System.out.println();
}
}
/**
* 骑士周游问题的算法
* @param chessBoard 棋盘
* @param row 马儿当前位置的行 从0开始
* @param column 马儿当前位置的列 从 0
* @param step 马儿走到了第几步,初始位置就是第一步
*/
public static void traversalChessBoard(int[][] chessBoard,int row,int column,int step){
chessBoard[row][column] = step;
//马儿当前的位置
visited[row * X + column] = true;
//获取当前可以走的下一个位置的集合,此处column就是x 列, row就是行y
List<Point> ps = next(new Point(column, row));
//对ps排序,排序的规则就是堆ps的所有的point对象的下一步位置的数目,进行非递归排序
sort(ps);
System.out.println(ps);
//遍历ps
while (!ps.isEmpty()){
//取出下一个可以走的位置
Point p = ps.remove(0);
//判断是否已经访问过
if(!visited[p.y * X +p.x]){
traversalChessBoard(chessBoard,p.y,p.x,step+1);
}
}
//判断马儿是否完成了任务,使用step 和应该走的步数比较
//如果没有打到数量,则表示没有完成任务,将整个棋盘置0
if(step < X*Y && !finished){
chessBoard[row][column] = 0;
visited[row*X+column] = false;
}else{
finished = true;
}
}
/**
* 根据当前的位置,计算马儿还能走哪儿些位置,并放在list中,最多有8个位置
* @param curPoint 当前点
* @return List<Point> 可走的点集合
*/
public static List<Point> next(Point curPoint){
List<Point> ps = new ArrayList<>();
//创建一个point
Point p1 = new Point();
// curPoint.x - 2 向左移动两列,curPoint.y-1 向上移动一行
// 表示马儿可以走 5
if((p1.x = curPoint.x - 2) >=0 && (p1.y = curPoint.y-1) >=0){
ps.add(new Point(p1));
}
//判断马儿能不能走6
if((p1.x = curPoint.x - 1) >=0 && (p1.y = curPoint.y-2) >=0){
ps.add(new Point(p1));
}
//判断马儿能不能走7
if((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y-2) >=0){
ps.add(new Point(p1));
}
//判断马儿能不能走0
if((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y-1) >=0){
ps.add(new Point(p1));
}
//判断马儿能不能走1
if((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y){
ps.add(new Point(p1));
}
//判断马儿能不能走2
if((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y){
ps.add(new Point(p1));
}
//判断马儿能不能走3
if((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y){
ps.add(new Point(p1));
}
//判断马儿能不能走4
if((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y){
ps.add(new Point(p1));
}
return ps;
}
/**
* 根据当前这一步的所有的下一步的选择的位置,进行非递减排序
* 减少回溯的次数, 贪心算法想用最少的回溯
*/
public static void sort(List<Point> ps){
ps.sort((o1, o2) -> {
//获取到o1的下一步的所有位置的个数
int count1 = next(o1).size();
int count2 = next(o2).size();
if(count1 < count2){
return -1;
}
else if(count1 == count2){
return 0;
}
return 1;
});
}
}