算法思想
克鲁斯卡尔算法与我们之前说的普利姆算法一样都是用来求最小生成树算法。所不同的是我们的普利姆算法是以点为中心的,但是我们克鲁斯卡尔算法是以边为中心的。
在这两个个算法中我们也是需要进行是否构成回路判断的,在我们当前的这个算法中第一个要解决的问题就是,根据我们传递进去的数据将我们的数据变成边的形式,要注意的是我们面对的是无向图,其次我们这里用到了排序的算法,这里我们用的排序器。
在两个求最小生成树的算法种算法的一个关键点都是判断是否构成了回路,一个采用的是所谓的重点概念(相连接的点中ASCIL编码值最大的那个点即为其他点的终点),一个判断的标准则是当前构成最短路径的这个点是否已尽存在在有效点集合中。普利姆算法在计算的时候所有已尽访问过的点都要考虑其中,寻找的是他们所有的联通点中的权值最小点。
代码分析
类排序相关知识扩展
有关我们的Java自定义数据类型的排序我们可以使用两种方式一种是通过重写Comparable接口中的compareTo()方法并在这个方法中定义我们这个对象的比较规则来实现,这种实现的方式属于内部实现,我们的这个类直接有了可排序的能力。
public class TestMain {
public static void main(String[] args) {
Student student1 = new Student(1, "ab", 12388);
Student student2 = new Student(1, "ab", 1234);
Student student3 = new Student(1, "as", 132);
Student student4 = new Student(3, "as", 234);
ArrayList studentArrays = new ArrayList();
studentArrays.add(student1);
studentArrays.add(student4);
studentArrays.add(student3);
studentArrays.add(student2);
Collections.sort(studentArrays);
for (Object student:studentArrays) {
System.out.println(student.toString());
}
}
}
class Student implements Comparable<Student> {
public int age;
public String name;
public int code;
public Student(int age, String name, int code) {
this.age = age;
this.name = name;
this.code = code;
}
//比较的是三个字符串
@Override
public int compareTo(Student o) {
if(this.age>o.age){
return 1;
}else if(this.age==o.age){
if (this.name.compareTo(o.name)>0){
return 1;
}else if(this.name.compareTo(o.name)==0){
if (this.code>o.code){
return 1;
}else if(this.code==o.code){
return 0;
}else if(this.code<o.code){
return -1;
}
}else if(this.name.compareTo(o.name)<0){
return -1;
}
}else if(this.age<o.age){
return -1;
}
return 0;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", code=" + code +
'}';
}
}
对一个类实现可以排序除过我们上面说过的直接在类的内部实现我们还可以通过定义一个比较器,在调用sort方法的时候将我们的这个比较器当做参数传递进去即可。这个中的比较方同样可以使用与第三方类。
class StudentCompartor implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
if(o1.age>o2.age){
return 1;
}else if(o1.age==o2.age){
if (o1.name.compareTo(o2.name)>0){
return 1;
}else if(o1.name.compareTo(o2.name)==0){
if (o1.code>o2.code){
return 1;
}else if(o1.code==o2.code){
return 0;
}else if(o1.code<o2.code){
return -1;
}
}else if(o1.name.compareTo(o2.name)<0){
return -1;
}
}else if(o1.age<o2.age){
return -1;
}
return 0;
}
}
比较规则:内部中this指代的对象,外部定义方式中o1指代的对象如果大于另一个我们返回1小于返回-1,等于的返回0.按照这样的规则设计出来的排序器是升序排序的,如果想实现的降序我们可以将这个规则反过来。
comparator与comparable的区别:
omparator位于java.util包下,属于Collection的一员;Comparable位于java.lang包下
Comparable是一个内部比较器,实现该接口的对象相当于具有了排序的能力;Comparator是一个外部比较器,可以将两个没有实现排序的对象实现Comparator接口来实现排序,内部与外部是相对于排序代码是否在实现的排序的对象中实现的
算法代码
public class KruskalCase {
public static void main(String[] args) {
//测试看看图是否创建ok
String[] data = {"A","B","C","D","E","F","G"};
//邻接矩阵的关系使用二维数组表示,0这个大数,表示两个点不联通
int[][] weight = new int[][]{
{0, 5, 7, 0, 0, 0, 2},
{5, 0, 0, 9, 0, 0, 3},
{7, 0, 0, 0, 8, 0, 0},
{0, 9, 0, 0, 0, 4, 0},
{0, 0, 8, 0, 0, 5, 4},
{0, 0, 0, 4, 5, 0, 6},
{2, 3, 0, 0, 4, 6, 0},};
CopyOnWriteArrayList<PathClass> pathClassCopyOnWriteArrayList = kruskalCase(data,weight);
System.out.println("Result~~~~~~~~~~~~");
for (PathClass temp:pathClassCopyOnWriteArrayList) {
System.out.println(temp.toString());
}
}
public static CopyOnWriteArrayList<PathClass> kruskalCase(String[] nodeArrays,int[][] weight){
//创建一个节点终点的HashMap结构,同时进行初始化
HashMap<String,String> targetToTerminus = new HashMap<>();
for (int i = 0; i < nodeArrays.length; i++) {
targetToTerminus.put(nodeArrays[i],nodeArrays[i]);
}
//将临界矩阵中权重数据以及节点名称数据按照权重从大到小的顺序进行排序
CopyOnWriteArrayList<PathClass> weightListOrdered = new CopyOnWriteArrayList<>();
int weightSize = weight.length;
for (int i = 0; i < weightSize; i++) {
for (int j = i; j < weightSize; j++) {
if (weight[i][j]!=0){
weightListOrdered.add(new PathClass(nodeArrays[i],nodeArrays[j],weight[i][j]));
}
}
}
PathComparator pathComparator = new PathComparator();
Collections.sort(weightListOrdered,pathComparator);
//对排好序的边从小到大进行一个遍历,一次选取出符合条件的data.lenght条边
int counter = 0;
for (PathClass temp:weightListOrdered) {
String start = temp.getStartNode();
String end = temp.getEndNode();
String startTerminus = targetToTerminus.get(start);
String endTerminus = targetToTerminus.get(end);
if(getTerminusNode(targetToTerminus,start)!=
getTerminusNode(targetToTerminus,end)){//判断是否构成了回路,没有构成回路我们就给相应的数据
//更新终点
if (startTerminus.compareTo(endTerminus)==1){
//给对应的点赋值相应的终点,将start大于end将start对应的点赋值给end
targetToTerminus.put(end,targetToTerminus.get(start));
}else if(startTerminus.compareTo(endTerminus)==0){
}else{
targetToTerminus.put(start,targetToTerminus.get(end));
}
counter++;
}else{
weightListOrdered.remove(temp);
}
if (counter==weightSize-1){
break;
}
}
//将后面多余出来的边进行删除
for (int i = 0; i < weightListOrdered.size(); i++) {
if(i<weightSize-1){
continue;
}else{
weightListOrdered.remove(i);
}
}
return weightListOrdered;
}
/**
*@returns: 目标节点的终点节点
*/
public static String getTerminusNode(Map<String,String> targetToTerminus,String targetNode){
return targetToTerminus.get(targetNode);
}
}
class PathClass {
private String startNode;
private String endNode;
private int wight;
public PathClass(String startNode, String endNode, int wight) {
this.startNode = startNode;
this.endNode = endNode;
this.wight = wight;
}
public String getStartNode() {
return startNode;
}
public String getEndNode() {
return endNode;
}
public int getWight() {
return wight;
}
@Override
public String toString() {
return "PathClass{" +
"startNode='" + startNode + '\'' +
", endNode='" + endNode + '\'' +
", wight=" + wight +
'}';
}
}
//这是一种外排序的实现方式,我们还可以通过实现Comparable接口来实现我们的内排序
//从Comparable的字面意思我们就容易理解为是可排序、可比较的
class PathComparator implements Comparator<PathClass>{
@Override
public int compare(PathClass o1, PathClass o2) {
return o1.getWight()-o2.getWight();
}
}