二分算法

条件:数组或者列表对象事先都要经过排序.

思路:

数组:

1.先对数组进行最大值下标和最小值下标的初始化

2.循环判断最大值是否大于等于最小值

2.1 取最大值和最小值的中间下标.

2.2目标值去对比中间下标的取值

2.3如果相等则返回中间值下标对应的数组值退出

2.4如果下标值比目标值大,则将中间值下标减一赋值给最大值下标,然后跳转到步骤2

2.5如果下标值比目标值小,则将中间值下标加一赋值给最小值下标,然后跳转到步骤2

3.退出循环时返回null.

链表:

1.判断链表节点是否为空

1.1取节点的数据和目标数据相比

1.2如果相等则返回节点对象对应的数据退出

1.3如果取出的节点数据比目标数据大,则将当前节点赋值为当前节点的右节点,然后跳转到步骤1

1.4如果取出的节点数据比目标数据小,则将当前节点赋值为当前节点的左节点,然后跳转到步骤1

2.退出循环时返回null.

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package per.xiaotian.datasort.algorithm;

public class Binary {

/**
* @description: array数组是排序过的升序数组
* @params: [array, target]
* @return: T 返回目标对象
* @date: 2023/2/2
*/
public <T extends Comparable> T algorithm(T[] array, T target){
int min = 0;
int max = array.length-1;
while(max >= min){
int mid = (max+min)/2;
if(target.compareTo(array[mid])==0){
return array[mid];
}else if(target.compareTo(array[mid])<0){//目标对象比中间值小,左移
max = mid-1;
}else{//否则右移
min = mid+1;
}
}
return null;
}

public <T extends Comparable> T algorithmLinked(Node<T> node, T target){
while(node != null) {
if (target.compareTo(node.getData()) == 0) {
return node.getData();
}else if(target.compareTo(node.getData()) == -1){
node = node.getLeft();
}else{
node = node.getRight();
}
}
return null;
}


class Node<T> {
private T data;
private Node left;
private Node right;

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}

public Node getLeft() {
return left;
}

public void setLeft(Node left) {
this.left = left;
}

public Node getRight() {
return right;
}

public void setRight(Node right) {
this.right = right;
}
}


}

分治算法:

场景:汉诺塔游戏用例

思路:

1.如果只有1个圆盘,那就是从a移动到c结束

2.num>=2,分治思想:把圆盘看成两部分,第num个和第num-1组

2.1.先到把num-1组从a移动到b,中间会借助c

2.2然后再把num个组a移动到c

2.3最后再把num-1组从b移动到c,中间会借助a

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package per.xiaotian.datasort.algorithm;

//汉诺塔问题
public class Partition {
public int count = 0;
/**
* @description: 目标数组的圆盘都需要重A柱移动到C柱
* @params: [a, b, c, num] a,b,c表示A,B,C三个柱子,num表示圆盘的个数
* @return: void
* @date: 2023/2/6
*/
public void move(char a, char b, char c,int num){
count++;
//如果只有1个圆盘,那就是从a移动到c结束
if(num == 1){
System.out.println("第"+num+"个圆盘,从"+a+"移动到"+c);
}else{
//num>=2,分治思想
//把圆盘看成两部分,第num个和第num-1组
//1.先到把num-1组从a移动到b,中间会借助c
//2.然后再把num个组a移动到c
//3.最后再把num-1组从b移动到c,中间会借助a
move(a, c, b, num-1);
System.out.println("第"+num+"个圆盘,从"+a+"移动到"+c);
move(b, a, c, num-1);
}
}


}

该算法的优缺点:

缺点:时间复杂度为2^n,效率是低下的.优点:采用分治算法,步骤非常好理解且代码量很低,比较好实现

动态规划算法

场景:01背包存放物品展现背包最大价值.

思路:

需要构建一个横坐标为背包容量,纵坐标为商品编号的二维数组.

1.商品编号为1的商品,如果商品容量大于背包容量,则为0;如果商品容量小于等于背包容量,则背包最大价值等于商品1的价值

2.继续增加编号为2的商品,如果当前背包容量小于当前2号商品的容量,则等于编号为1时的背包容量的最大价值;如果当前背包容量大于等于2号商品的容量,则需要判断(2号商品的价值+背包存放当前编号数量和背包容量减去2号商品容量的最大价值)去和之前最大价值比较取最大值,如图:

如下面示例的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1.先遍历构建商品数量为1时,增加背包容量的情况
//2.再递增商品数量的情况
for(int i=1; i< knapsackVal.length; i++) {
for (int j = 1; j < knapsackVal[i].length; j++) {
//需要对比背包容量是否满足装入新增商品的数量,不过不满足则复用原有的背包容量的最大价值
if(itemCapacity[i-1] > j) {
knapsackVal[i][j] = knapsackVal[i-1][j];
}else{
//需要比较(新增商品的价值+背包容量减去原有背包商品数量所能放入的最大价值)的和与原有商品数量所能放入的最大值相比较
if(knapsackVal[i-1][j]>knapsackVal[i-1][j-itemCapacity[i-1]]+itemValue[i-1]){
knapsackVal[i][j] = knapsackVal[i-1][j];
}else{
knapsackVal[i][j] = knapsackVal[i-1][j-itemCapacity[i-1]]+itemValue[i-1];
knapsackItem[i][j] = 1;
}
}
}
}

完整代码如下,其中代码还包括最大价值背包当中存放的有哪些编号的商品,数组knapsackItem[][]描述,值为1时表示当前坐标下它被放入背包,如图:

整体示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package per.xiaotian.datasort.algorithm;

public class Dynamic {
//背包问题
//构建初始方法
//背包容量的值和存放物品所占背包容量和存放物品对应的价值,返回背包的存放物品的值
public int knapsackCal(int knapsackCapacity, int[] itemCapacity, int[] itemValue){
//构建出一个二维数组,横坐标为背包容量,纵坐标为商品数量,值为背包容量所能装入的商品最大价值
int[][] knapsackVal = new int[itemCapacity.length+1][knapsackCapacity+1];
//对应商品编号放入背包容量的数组,如果放入置为1
int[][] knapsackItem = new int[itemCapacity.length+1][knapsackCapacity+1];

//先要初始化背包容量为0和商品数量为0的情况,但由于该问题比较巧合这样的情况和默认数组初始化的情况相等,所以也可以不用初始化直接跳过
for(int i=0; i<knapsackVal.length; i++){
knapsackVal[i][0] = 0;
}
for(int j=0; j<knapsackVal[0].length; j++){
knapsackVal[0][j] = 0;
}

//1.先遍历构建商品数量为1时,增加背包容量的情况
//2.再递增商品数量的情况
for(int i=1; i< knapsackVal.length; i++) {
for (int j = 1; j < knapsackVal[i].length; j++) {
//需要对比背包容量是否满足装入新增商品的数量,不过不满足则复用原有的背包容量的最大价值
if(itemCapacity[i-1] > j) {
knapsackVal[i][j] = knapsackVal[i-1][j];
}else{
//需要比较(新增商品的价值+背包容量减去原有背包商品数量所能放入的最大价值)的和与原有商品数量所能放入的最大值相比较
if(knapsackVal[i-1][j]>knapsackVal[i-1][j-itemCapacity[i-1]]+itemValue[i-1]){
knapsackVal[i][j] = knapsackVal[i-1][j];
}else{
knapsackVal[i][j] = knapsackVal[i-1][j-itemCapacity[i-1]]+itemValue[i-1];
knapsackItem[i][j] = 1;
}
}
}
}

//打印二维数组knapsackVal
for(int i=0; i<knapsackVal.length; i++){
for (int j = 0; j < knapsackVal[i].length; j++) {
System.out.printf("%4d", knapsackVal[i][j]);
}
System.out.println();
}

System.out.println();
System.out.println();
//打印二维数组knapsackVal
for(int i=0; i<knapsackItem.length; i++){
for (int j = 0; j < knapsackItem[i].length; j++) {
System.out.printf("%4d", knapsackItem[i][j]);
}
System.out.println();
}

//对应背包容量放入的商品编号
int i = knapsackItem.length-1;
int j = knapsackItem[0].length-1;
while (i > 0 && j > 0) {
if(knapsackItem[i][j]==1){
System.out.println("第"+i+"编号商品放入背包");
j -= itemCapacity[i-1];
}
i--;
}
return knapsackVal[itemCapacity.length][knapsackCapacity];
}


// public static void main(String[] args) {
// int knapsackCapacity = 8;
// int[] itemCapacity = {1, 4, 3};
// int[] itemValue = {1, 8, 3};
// Dynamic dynamic = new Dynamic();
// dynamic.knapsackCal(knapsackCapacity, itemCapacity, itemValue);
// }



}

KMP算法(暴力破解算法的优化)

场景:解决字符串匹配的问题

思路:

将需要匹配的目标字符串构建部分匹配表,初始下标的值默认为-1,如下图:

代码如下:

1
2
3
4
5
6
7
8
9
10
11
//kmp算法构建一个数组大小为target.length的数组
int[] targetLength =new int[target.length()];
//对数组进行初始化赋值
for(int i=1, j=0; i<targetLength.length; i++){
if(target.charAt(i) == target.charAt(j)){
j++;
}else{
j=0;
}
targetLength[i] = j;
}

1构建好匹配表后,原字符串和目标字符串都从开始下标位置开始匹配.

1.1 如果相等

1.1.1则将原字符串和目标字符串的下标都对应加1.

1.1.2同时去判断目标数组的下标是否等于目标字符串的长度了.

1.1.2.1如果相等,则返回原字符串的下标减去目标字符串的长度就为对应原匹配字符串的起始下标位置

1.1.2.2如果不相等,则返回到步骤1

1.2如果不相等

1.2.1则去判断目标数组的下标是否为0

1.2.1.1如果为0,则将原数组的下标加1

1.2.2将目标数组的下标赋值为部分匹配数组的值,然后返回到步骤1

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//开始对比source和target是否匹配
int indexSource = 0;
int targetSource = 0;
while(indexSource<source.length()){
if(source.charAt(indexSource) != target.charAt(targetSource)){
//核心逻辑代码
if(targetSource == 0){
indexSource++;
}
targetSource = targetLength[targetSource];
}else{
indexSource++;
targetSource++;
if(targetSource == target.length()){
return indexSource -targetSource;
}
}
}

此算法的特点时原字符的下标匹配的时候永远不会回退,时间复杂度为O{n}

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package per.xiaotian.datasort.algorithm;

import java.util.Arrays;

public class Kmp {

public int findTargetStringIndex(String source, String target){
//kmp算法构建一个数组大小为target.length的数组
int[] targetLength =new int[target.length()];
//对数组进行初始化赋值
for(int i=1, j=0; i<targetLength.length; i++){
if(target.charAt(i) == target.charAt(j)){
j++;
}else{
j=0;
}
targetLength[i] = j;
}

// System.out.println(Arrays.toString(targetLength));

//开始对比source和target是否匹配
int indexSource = 0;
int targetSource = 0;
while(indexSource<source.length()){
if(source.charAt(indexSource) != target.charAt(targetSource)){
if(targetSource == 0){
indexSource++;
}
targetSource = targetLength[targetSource];
}else{
indexSource++;
targetSource++;
if(targetSource == target.length()){
return indexSource -targetSource;
}
}
}
return -1;
}


// public static void main(String[] args) {
// String source = "ABCDABDABDABCEFBBAABDABDABC";
// String target = "ABDABDABC";
// Kmp kmp = new Kmp();
// System.out.println(kmp.findTargetStringIndex(source, target));
//
// }
}

贪心算法

场景: 选择电台覆盖所有城市地区的问题

思路:

将所有电台所能覆盖的城市都汇聚成一个citySet集合,

//贪心算法的思路,每步中选出覆盖最多城市的电台

循环遍历每个电台所覆盖的城市是否在citySet集合中,统计每个电台的城市数量,并找出最大数量的电台.

将这个电台加入挑选出的电台集合中并移除这个电台在citySet集合中的城市并清除原有遍历的Map集合中的这个电台.

直到citySet为空时停止循环遍历

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package per.xiaotian.datasort.algorithm;

import java.util.*;
import java.util.stream.Collectors;

public class Greedy {

//电台覆盖城市问题,选最少的电台覆盖所有城市
//电台所能覆盖的城市,二维数组 {K1:{北京,石家庄,郑州},K2:{北京,上海,广州},
// K3:{南京,苏州,杭州,上海},K4:{南宁,南昌,成都},K5:{重庆,成都,桂林},K6:{广州,成都,南京}}
public List<String> radioGreedy(Map<String, List<String>> allRadioMap){
//创建所有城市的set
Set<String> citySet = new HashSet<>();
allRadioMap.values().stream().collect(Collectors.toList()).forEach(ll->citySet.addAll(ll));
//贪心算法,挑选出电台中覆盖citySet最多的城市,选出电台后放入到电台集合中,并从citySet中移除
List<String> radioList = new ArrayList<>();
while(!citySet.isEmpty()){
String maxCityKey = "";
int maxCityNum = 0;
for(Map.Entry<String,List<String>> entry: allRadioMap.entrySet()){
int keyCityNum = 0;
for(String city: entry.getValue()){
if(citySet.contains(city)){
keyCityNum++;
}
}
if(keyCityNum > maxCityNum){
maxCityNum = keyCityNum;
maxCityKey = entry.getKey();
}
}
radioList.add(maxCityKey);
//清除citySet包含的城市
citySet.removeAll(allRadioMap.get(maxCityKey));
//清除allRadioMap的maxCityKey
allRadioMap.remove(maxCityKey);
}
return radioList;
}

//电台所能覆盖的城市,二维数组 {K1:{北京,石家庄,郑州},K2:{北京,上海,广州},
// K3:{南京,苏州,杭州,上海},K4:{南宁,南昌,成都},K5:{重庆,成都,桂林},K6:{西宁,成都,南京}}
public static void main(String[] args) {
Map<String,List<String>> allRadioMap = new HashMap<>();
ArrayList<String> k1 = new ArrayList<>();
k1.add("北京");
k1.add("石家庄");
k1.add("郑州");
ArrayList<String> k2 = new ArrayList<>();
k2.add("北京");
k2.add("上海");
k2.add("广州");
ArrayList<String> k3 = new ArrayList<>();
k3.add("南京");
k3.add("苏州");
k3.add("杭州");
k3.add("上海");
ArrayList<String> k4 = new ArrayList<>();
k4.add("南宁");
k4.add("南昌");
k4.add("成都");
ArrayList<String> k5 = new ArrayList<>();
k5.add("重庆");
k5.add("桂林");
k5.add("成都");
ArrayList<String> k6 = new ArrayList<>();
k6.add("广州");
k6.add("南京");
k6.add("成都");
allRadioMap.put("K1",k1);
allRadioMap.put("K2",k2);
allRadioMap.put("K3",k3);
allRadioMap.put("K4",k4);
allRadioMap.put("K5",k5);
allRadioMap.put("K6",k6);

Greedy greedy = new Greedy();
System.out.println(Arrays.toString(greedy.radioGreedy(allRadioMap).toArray()));


}
}

普里姆算法

场景: 图平面求各个点之间连通的最短路径的问题

思路:

1.从1个顶点开始,求出这个顶点到其他各个顶点权值最小的路径.

2.把这个权值最小路径的顶点加入到已访问的顶点集合中,然后遍历这两个顶点到其他所有各个顶点权值最小的路径.

3.当然这个权值最小的路径的顶点不能是已加入访问顶点的路径

4.依次循环需要执行顶点总个数减一次,找到的边即为各个顶点的最短路径

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package per.xiaotian.datasort.algorithm;

public class Prim {
//求到各个顶点的最小路径
public int shortWeight(char[] vertex, int[][] weight){
//顶点是否一被访问的数组
int[] visited = new int[vertex.length];
//最短路径之和
int sumWeight = 0;
//默认从0顶点开始访问
for(int m=1,n=0; m<visited.length;m++) {
visited[n] = 1;
int minIndex = -1;
int visitedIndex = -1;
int minWeight = Integer.MAX_VALUE;
//求已访问顶点到未访问顶点间的最小路径
for(int j=0; j<visited.length&&visited[j]==1;j++) {
for (int i = 0; i < weight[j].length; i++) {
if (weight[j][i] < minWeight && visited[i] != 1) {
minWeight = weight[j][i];
minIndex = i;
visitedIndex = j;
}
}
}
System.out.println("边<"+vertex[visitedIndex]+vertex[minIndex]+">,权值为:"+minWeight);
sumWeight = sumWeight + minWeight;
n = minIndex;
}
return sumWeight;
}

public static void main(String[] args) {
char[] vertex = {'A','B','C','D','E','F','G'};
int[][] weight = {{10000,3,6,8,4,1000,1},
{3,10000,10,5,19,8,4},
{6,10,10000,36,7,13,22},
{8,5,36,10000,9,12,33},
{4,19,7,12,10000,23,14},
{1000,8,13,12,23,10000,17},
{1,4,22,33,14,17,10000}};
Prim prim = new Prim();
System.out.println(prim.shortWeight(vertex, weight));

}
}

克鲁斯卡尔算法

场景: 和普里姆算法要解决的问题相同,也是各个顶点连通求最短路径

思路:

1.先把各个顶点所连通的边加入到边的集合中,然后把这个集合从小到大排序

2.排序完成之后,依次遍历,需要加入边不能形成回路(通过终点不能相同去判定)

3.找出顶点个数减一条边后即可求的各个顶点联通的最短路径.

算法核心代码,顶点的终点是否一致判断

1
2
3
4
5
6
7
8
9
10
11
12
//该算法的核心endVertex[]数组,对应添加数据,及判断是否拥有相同的最大顶点
while(endVertex[tempEndVertex] != 0){
tempEndVertex = endVertex[tempEndVertex];
}
if(tempEndVertex != sides[i].endVertex){
//把新加入的边记录到endVertex中
if(tempEndVertex<sides[i].endVertex) {
endVertex[tempEndVertex] = sides[i].endVertex;
}else{
endVertex[sides[i].endVertex] = tempEndVertex;
}
}

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package per.xiaotian.datasort.algorithm;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Kruskal {

//求到各个顶点的最小路径
public int shortWeight(char[] vertex, int[][] weight){
List<Side> sideList = new ArrayList<>();
for(int i=0; i<weight.length; i++){
for(int j=i+1; j<weight[i].length; j++){
sideList.add(new Side(i, j, weight[i][j]));
}
}
Side[] sides = sideList.toArray(new Side[sideList.size()]);
//排序
Arrays.sort(sides);
//记录各个顶点终点的记录
int[] endVertex = new int[vertex.length];
int sumWeight = 0;
for(int i=0; i<sides.length; i++){
//判断终点是否一致
int tempEndVertex = sides[i].startVertex;
//该算法的核心endVertex[]数组,对应添加数据,及判断是否拥有相同的最大顶点
while(endVertex[tempEndVertex] != 0){
tempEndVertex = endVertex[tempEndVertex];
}
if(tempEndVertex != sides[i].endVertex){
//把新加入的边记录到endVertex中
if(tempEndVertex<sides[i].endVertex) {
endVertex[tempEndVertex] = sides[i].endVertex;
}else{
endVertex[sides[i].endVertex] = tempEndVertex;
}
System.out.println("边<"+vertex[sides[i].startVertex]+vertex[sides[i].endVertex]+">,权值为:"+sides[i].weight);
sumWeight = sumWeight + sides[i].weight;
}
}

return sumWeight;
}


//创建一个边的对象
class Side implements Comparable<Side>{
int startVertex;
int endVertex;
int weight;

public Side(int startVertex, int endVertex, int weight) {
this.startVertex = startVertex;
this.endVertex = endVertex;
this.weight = weight;
}

@Override
public int compareTo(Side s) {
if(weight<s.weight){
return -1;
}else if(weight > s.weight){
return 1;
}
return 0;
}
}

public static void main(String[] args) {
char[] vertex = {'A','B','C','D','E','F','G'};
int[][] weight = {{10000,3,6,8,4,1000,1},
{3,10000,10,5,19,8,4},
{6,10,10000,36,7,13,22},
{8,5,36,10000,9,12,33},
{4,19,7,12,10000,23,14},
{1000,8,13,12,23,10000,17},
{1,4,22,33,14,17,10000}};
Kruskal kruskal = new Kruskal();
System.out.println(kruskal.shortWeight(vertex, weight));
}

//边<AG>,权值为:1
//边<AB>,权值为:3
//边<AE>,权值为:4
//边<BD>,权值为:5
//边<AC>,权值为:6
//边<BF>,权值为:8
//27

}

执行结果和普里姆算法的结果一致

迪杰斯特拉算法

场景: 解决目标顶点到各顶点的最短路径

思路:

1.创建一个目标顶点的权值数组,初始化赋值为边与边不通的值

2.创建一个可以被访问顶点的数组

3.初始化count默认为顶点数

4.遍历目标顶点的边的权值,如果小于目标顶点的权值,则赋值为边的权值,并将该下标对应的可以被访问的顶点设置为1,并将count-1

5.循环遍历已被设置被访问顶点,如果目标顶点到被访问顶点的距离加上被访问顶点到遍历点的距离小于目标顶点到遍历点的距离,则将该值替换为目标顶点到遍历点的距离,并将count-1.

6.每次循环遍历结束时,去统计count是否小于等于0,如果小于等于了,则再遍历顶点个数次

7.遍历结束后输出的目标顶点的权值数组就为目标顶点到各顶点的最小权值路径.

代码如下,验证代码结果是正确的(和佛洛依德算法一致),但代码逻辑应该还是可以有优化的地方.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package per.xiaotian.datasort.algorithm;

public class Dijkstra {

//解决问题,目标顶点到各个顶点的最短路径
public void vertexShortWeight(char[] vertex, int[][] weight, int index){
//目标顶点的权值数组
int[] targetWeight = new int[vertex.length];
for(int i=0; i< targetWeight.length; i++){
targetWeight[i] = 10000;
}
//可以被遍历访问的顶点
int[] vertexReady = new int[vertex.length];
int count = vertex.length;
for(int i=0; i<weight[index].length; i++){
if(weight[index][i]<targetWeight[i]){
targetWeight[i] = weight[index][i];
vertexReady[i] = 1;
count--;
}
}

int indexStart = 0;
int endWhile =vertex.length;
boolean flag = true;
while(flag||endWhile>0){
endWhile--;
if(vertexReady[indexStart] ==1){
for(int j=0; j<weight[indexStart].length; j++){
if((weight[indexStart][j]+targetWeight[indexStart])<targetWeight[j]){
targetWeight[j] = weight[indexStart][j] + targetWeight[indexStart];
vertexReady[j] = 1;
count--;
}
}
}
indexStart = (indexStart+1)%vertex.length;
if(flag&&count<=0){
flag = false;
endWhile = vertex.length;
}
}

//从index下标到各个顶点的距离
System.out.println("顶点"+vertex[index]);
for(int i=0; i<vertex.length; i++){
System.out.print("到"+vertex[i]+"的距离("+targetWeight[i]+")");
}

}

// 顶点A
//到A的距离(0)到B的距离(3)到C的距离(6)到D的距离(8)到E的距离(4)到F的距离(11)到G的距离(1)
public static void main(String[] args) {
char[] vertex = {'A','B','C','D','E','F','G'};
int[][] weight = {{0,7,10000,10000,10000,2,6},//A
{7,0,6,10000,10000,10000,10},//B
{10000,6,0,4,10000,10000,8},//C
{10000,10000,4,0,9,10000,8},//D
{10000,10000,10000,9,0,3,10000},//E
{2,10000,10000,10000,3,0,10000},//F
{6,10,10000,8,10000,10000,0}};//G
Dijkstra dijkstra = new Dijkstra();
dijkstra.vertexShortWeight(vertex, weight, 3);

}


// 顶点A
//到A的距离(0)到B的距离(7)到C的距离(13)到D的距离(14)到E的距离(5)到F的距离(2)到G的距离(6
// 顶点G
//到A的距离(6)到B的距离(10)到C的距离(12)到D的距离(8)到E的距离(11)到F的距离(8)到G的距离(0)
// 顶点D
//到A的距离(14)到B的距离(10)到C的距离(4)到D的距离(0)到E的距离(9)到F的距离(12)到G的距离(8)
}

佛洛依德算法

场景: 解决目标顶点到各顶点的最短路径

思路:

1.利用中间节点遍历起始顶点到各个终止顶点的距离

2.如果相比小于起始顶点到终止顶点的距离,则将其是顶点到终止顶点的距离值赋值为利用中间节点的距离

3.如果相比大于起始顶点到终止顶点的距离,则保持不变

优点: 思路代码很好实现,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package per.xiaotian.datasort.algorithm;

public class Floyd {

//解决问题,目标顶点到各个顶点的最短路径
public void vertexShortWeight(char[] vertex, int[][] weight, int index){
//遍历中间顶点
for(int k=0; k<vertex.length; k++){
//遍历起始顶点
for(int i=0; i< vertex.length; i++){
//遍历终止节点
for(int j=0; j< vertex.length; j++){
int len = weight[i][k] + weight[k][j];
if(len < weight[i][j]){
weight[i][j] = len;
}
}
}
}

//从index下标到各个顶点的距离
System.out.println("顶点"+vertex[index]);
for(int i=0; i<vertex.length; i++){
System.out.print("到"+vertex[i]+"的距离("+weight[index][i]+")");
}
}


public static void main(String[] args) {
char[] vertex = {'A','B','C','D','E','F','G'};
int[][] weight = {{0,3,6,8,4,1000,1},
{3,0,10,5,19,8,4},
{6,10,0,36,7,13,22},
{8,5,36,0,9,12,33},
{4,19,7,12,0,23,14},
{1000,8,13,12,23,0,17},
{1,4,22,33,14,17,0}};
Floyd floyd = new Floyd();
floyd.vertexShortWeight(vertex, weight, 0);

}
}

马塔回旋算法

通过递归回溯的算法实现的:

1.先创建一个棋盘的二维数组,创建对应每个点是否已被访问的数组,是否完成的标识

2.创建一个对象,用数值表示横纵坐标的属性值

3.创建一个方法,得到下一步的所有刚才对象的集合,利用棋盘的边界大小去判断

4.写出马踏棋盘的算法的方法,有当前棋盘的行和列和步骤,初始为1.

6.将行和列赋值给对应的二维数组的坐标,值为初始步骤,并将对应已访问的数组的对应值设为已访问

5.创建当前对象,初始化行和列的值,调用下一步的方法.

6.判断得到的集合是否为空,不为空则调用集合的remove(0)方法删除并得到删除的对象,如果对象没有被访问过,则递归调用刚才马踏棋盘的方法.如果已被调用,则继续对集合判断是否为空,重复第六步的操作.

7.退出while循环后,需要判断step是否小于棋盘的格子数并且是未完成,如果满足,则需要将当前对象的行列对应的二维数组置为0,并且访问数组对应的下标设置成位访问.如果不满足,则将是否完成设置为已完成.

优化点,先让马走贴近棋盘边缘的步骤,因为这样得到下一步的组合比较少,计算耗时会比较少

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package per.xiaotian.datasort.algorithm;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HorseChessboard {

private int[][] chessBoard;
private boolean[] visited;

private boolean finished;

public static void main(String[] args) {
HorseChessboard horseChessboard = new HorseChessboard();
int len =6;
horseChessboard.chessBoard = new int[len][len];
horseChessboard.visited = new boolean[len*len];
System.out.println("======start=======");
long curTime = System.currentTimeMillis();
horseChessboard.cal(4, 4, 1);
System.out.println(System.currentTimeMillis()-curTime);
System.out.println("======end=======");
for(int i=0;i<horseChessboard.chessBoard.length; i++){
for(int j=0; j<horseChessboard.chessBoard[i].length; j++){
System.out.printf("%4d", horseChessboard.chessBoard[i][j]);
}
System.out.println();
}

}

public void cal(int x, int y, int step){
chessBoard[x][y] = step;
visited[chessBoard.length*x+y] = true;
ChessBoard cur = new ChessBoard();
cur.x = x;
cur.y = y;
List<ChessBoard> chessBoardList = next(cur);
Collections.sort(chessBoardList);
while(!chessBoardList.isEmpty()){
ChessBoard next = chessBoardList.remove(0);
if(!visited[chessBoard.length*next.x+next.y]){
cal(next.x, next.y, step+1);
}
}

if(step<chessBoard.length*chessBoard[0].length&&!finished){
chessBoard[x][y] = 0;
visited[chessBoard.length*x+y] = false;
}else{
finished = true;
}
}

public List<ChessBoard> next(ChessBoard cur){
List<ChessBoard> chessBoardList = new ArrayList<>();
if(cur.x -2>=0 && cur.y-1>=0){
ChessBoard chessBoard1 = new ChessBoard();
chessBoard1.x = cur.x-2;
chessBoard1.y = cur.y-1;
chessBoardList.add(chessBoard1);
}
if(cur.x -2>=0 && cur.y+1<chessBoard[0].length){
ChessBoard chessBoard1 = new ChessBoard();
chessBoard1.x = cur.x-2;
chessBoard1.y = cur.y+1;
chessBoardList.add(chessBoard1);
}
if(cur.x -1>=0 && cur.y+2<chessBoard[0].length){
ChessBoard chessBoard1 = new ChessBoard();
chessBoard1.x = cur.x-1;
chessBoard1.y = cur.y+2;
chessBoardList.add(chessBoard1);
}
if(cur.x -1>=0 && cur.y-2>=0){
ChessBoard chessBoard1 = new ChessBoard();
chessBoard1.x = cur.x-1;
chessBoard1.y = cur.y-2;
chessBoardList.add(chessBoard1);
}
if(cur.x +2<chessBoard.length && cur.y-1>=0){
ChessBoard chessBoard1 = new ChessBoard();
chessBoard1.x = cur.x+2;
chessBoard1.y = cur.y-1;
chessBoardList.add(chessBoard1);
}
if(cur.x +2<chessBoard.length && cur.y+1<chessBoard[0].length){
ChessBoard chessBoard1 = new ChessBoard();
chessBoard1.x = cur.x+2;
chessBoard1.y = cur.y+1;
chessBoardList.add(chessBoard1);
}
if(cur.x +1<chessBoard.length && cur.y+2<chessBoard[0].length){
ChessBoard chessBoard1 = new ChessBoard();
chessBoard1.x = cur.x+1;
chessBoard1.y = cur.y+2;
chessBoardList.add(chessBoard1);
}
if(cur.x +1<chessBoard.length && cur.y-2>=0){
ChessBoard chessBoard1 = new ChessBoard();
chessBoard1.x = cur.x+1;
chessBoard1.y = cur.y-2;
chessBoardList.add(chessBoard1);
}
return chessBoardList;
}

class ChessBoard implements Comparable<ChessBoard>{
int x;
int y;

@Override
public int compareTo(ChessBoard o) {
int size1 = next(o).size();
int size2 = next(this).size();
return size2-size1;
}
}
}