(마더보드 설정에서 'Hyper-V'는 켜놓은 것을 기본으로 가정한다.)

 

Hyper-V 활성화 하기 (두 가지 방법 중 편한 방법 이용)

1. PowerShell을 관리자 권한으로 실행

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

입력 후 재부팅

 

2. 윈도우 키 누르면 나오는 검색창에 'Windows 기능 켜기/끄기' 검색 후 실행

'Hyper-V'항목 체크 후 재부팅

 

가상이미지 만들기

1. 윈도우 키 누르고 'Hyper'검색 -> 관리자로 들어가서 세부 설정 후 만들거나

2. 'Hyper-V 빨리 만들기'로 가면 MS 홈페이지에서 받을 수 있는 윈도우 이미지를 받을 수 있다. 여기서 받을 수 있는 이미지는 윈도우와 우분투다. 당연히 윈도우는 체험판으로 정식 버전 활성화를 위해서는 별도의 시리얼키를 가지고 있어야 한다. 즉, 호스트 윈도우, 게스트 윈도우 이렇게 적어도 2개의 라이센스가 필요하다.

하지만 이미 받아놨다면 관리자에서 불러와서 실행하면 된다.

주의 : 가상이미지 만들 때 '1세대' '2세대'가 나오는데 '1세대'로 할 것! 2세대 호환성이 안 좋아서 결국 1세대로 설치했다.

 

가상 머신 인터넷 사용하기

기본적으로 Hyper-V는 Parallels나 VMware Workstation와 같은 일반 컴퓨터용으로 개발된 가상화 솔루션이 아니라 VMware ESXi와 같은 워크스테이션용으로 개발된 가상화 솔루션이라 인터넷 연결이 끊어져있다. (VMware ESXi도 기본으로 인터넷 연결을 끊어 놓는지는 모르겠다. VMware ESXi도 무료니까 누군가 받아서 실험을...)

따라서 가상 머신에 인터넷을 이용하기 위해서는 몇 가지 설정이 필요하다.

'Hyper-V 관리자' > 우측 탭에서 '가상 스위치 관리자...'

'새 가상 네트워크 스위치' > '내부' > '가상 스위치 만들기'

이름을 정하고, 연결 형식에 '외부 네트워크'에서 호스트 OS에 설치된 물리 네트워크 카드를 선택 > '적용'

가상 머신을 선택하면 우측 탭에 해당 가상 머신의 '설정' 옵션이 뜬다. 혹은 가상 머신을 우클릭 한 다음 '설정'으로 들어간다.

'네트워크 어댑터' > '가상 스위치'에서 방금 만든 어댑터를 설정 후 '적용'

이제 가상 머신에서 호스트를 공유해 외부 인터넷이 된다.

 

 

 

빠르다... 하지만...

Hyper-V가 거의 Parallels 급으로 빠르다. Cinebench R20을 돌려봤을 때 호스트에서 760점, 게스트에서 700점이 나오니 무려 92%의 효율을 보인다. 게다가 그래픽 가속도 생각보다 좋아 조작에 큰 불편함이 없다. 역시 Azure (애저)에서 사용하기 위해 만들어서 그런가보다. 문제는 편의기능이 떨어져도 너무 떨어진다. 정말 그냥 독립된 운영체제 하나 설치하기에는 빠르고 좋지만, 호스트와 파일을 공유하고 USB를 마운트 하는 등... 안되는 것이 너무 많다.(방법이 있을 것 같긴 한데... 기본 설정을 대충 봐서는 없어보인다... 그렇다고 구글링까지 해가며 찾기엔... 그냥 VMware를 설치하고 말지...) 그래서 저는 결국 VMware로 넘어갑니다. VMware를 쓰면 Parallels에서 쓰던 '동시 실행 모드'와 같은 기능인 Unity??인가도 쓸 수 있고...

- 추가 -

VMware도 게스트에서 700점이 나오니... 사실상 Hyper-V가 속도 면에서는 이점이 없네요. 게다가 그래픽 가속이 VMware쪽이 더 뛰어나서 체감 성능도 더 좋고... 편리함은 뭐 더할 나위 없이 좋구요. 물론... Parallels의 96~99%의 효율은 아니더라도 92%가 어디인가요...

맥에선 부트캠프와 거의 대등한 성능 보이는 Parallels와 달리 VMware Fusion은 어떤 때는 50% 효율까지 떨어져서 50~85%의 낮은 성능 효율을 보일 뿐 아니라, 배터리 효율 마저 나쁘다 보니 Parallels와 체감 차이가 너무도 커서 도저히 못 쓰겠어서 Parallels만 썼는데 윈동용 VMware는 좋네요 -_-;;

 

결론

VMware Player 무료를 쓰자...

여러 블로그를 뒤져봤지만... 그냥 전부 자기 기록이라 제대로 참고할 블로그가 없었다 -_-;;

깃을 터미널로 쓰던 사람이면 모르겠지만 그동안 소스트리로만 써온지라....

 

1. cd 명령어를 이용해 로컬 레포지토리로 이동

2. 커밋하기(이건 소스트리에서 해놔도 됨... 커밋은 에러 안 나더라...)

git commit [option] [-m 입력할 경우 여기다 메시지 입력]
git commit -m "메시지입력"

3. 강제 푸시하기

git push [option] [원격지 이름] [브랜치 이름]
git push --force Java_Study master

 

 

깃... 블로그 찾지 말고 그냥 help 기능 이용하자... 이게 더 좋다.

1. 터미널에서 'git help' 라고 치면 어떤 명령어들이 있는지 보여준다.

 

2. commit이 궁금하면 'git help commit', push가 궁금하면 'git help push'라고 치면 상세하게 나온다.

import java.util.Comparator;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;

class Employee implements Comparable<Employee> {
    private int id;
    private String name;

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // Two Employees are equal if their IDs are equal
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return id == employee.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    // Compare employees based on their IDs
    @Override
    public int compareTo(Employee employee) {
        return this.getId() - employee.getId();
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}


public class TreeSetUserDefinedObjectExample {
    public static void main(String[] args) {
        // Creating a TreeSet of User Defined Objects.

        /*
           The requirement for a TreeSet of user defined objects is that

           1. Either the class should implement the Comparable interface and provide
              the implementation for the compareTo() function.
           2. Or you should provide a custom Comparator while creating the TreeSet.
        */

        SortedSet<Employee> employees = new TreeSet<>();

        // TreeSet uses the compareTo() method of the Employee class to compare two employees and sort them
        employees.add(new Employee(1010, "Rajeev"));
        employees.add(new Employee(1005, "Sachin"));
        employees.add(new Employee(1008, "Chris"));

        System.out.println("Employees (sorted based on Employee class's compareTo() function)");
        System.out.println(employees);

        // Providing a Custom Comparator (This comparator compares the employees based on their Name)
        employees = new TreeSet<>(Comparator.comparing(Employee::getName));

        employees.add(new Employee(1010, "Rajeev"));
        employees.add(new Employee(1005, "Sachin"));
        employees.add(new Employee(1008, "Chris"));

        System.out.println("\nEmployees (sorted based on the supplied Comparator)");
        System.out.println(employees);

    }
}

// 결과
# Output
Employees (sorted based on Employee class's compareTo() function)
[Employee{id=1005, name='Sachin'}, Employee{id=1008, name='Chris'}, Employee{id=1010, name='Rajeev'}]

Employees (sorted based on the supplied Comparator)
[Employee{id=1008, name='Chris'}, Employee{id=1010, name='Rajeev'}, Employee{id=1005, name='Sachin'}]

 

출처 : www.callicoder.com/java-treeset/

 

Java TreeSet Tutorial with Examples

Java TreeSet class is part of Java's collections framework. It implements the NavigableSet interface, which in turn extends the SortedSet interface. The TreeSet class internally uses a TreeMap to store elements. The elements in a TreeSet are ordered accord

www.callicoder.com

controller.txt
0.01MB
message.jsp
0.00MB

 

DB.java
0.02MB

 

요 파일 3개를 받아다 만드는거 해보기... 내가 한거는 내가 보기 좋게 변수명도 바꾸고 해서... 다른 파일 받아다 연습해보자.

C 태그는 가장 아래에 포함

파일 그대로 가져다 수정해서 아래와 같이 작동하도록 빠르게 만든다.

DB생성(create), 데이터 삽입(insert), 조회(select), 수정(update) 만들어보기

 

삽입

조회

수정

 

HomeController

더보기
package com.kopo.memo;

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
	
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
	
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		DB db = new DB<Memo>("/Users/saebyul/SqliteDB/0916memo.db", "memo");
		
		model.addAttribute("test", new Memo());
		
		Memo memo = new Memo();
	    memo.setIdx(10);
	    System.out.println(memo.getIdx());
		
		if (db.open()) {
			ArrayList<Memo> list = db.selectData(new Memo());
			model.addAttribute("list", list);
			db.close();
		}
		return "home";
	}
	
	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public String create(Locale locale, Model model) {
		DB db = new DB<Memo>("/Users/saebyul/SqliteDB/0916memo.db", "memo");
		
		if (db.open()) {
			if (db.createTable(new Memo())) {
				model.addAttribute("message", "테이블이 생성되었습니다.");
			} else {
				model.addAttribute("message", "테이블 생성에 실패하였습니다.");
			}
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

	// insert를 위해 사용
	@RequestMapping(value = "/i1", method = RequestMethod.GET)
	public String i1(Locale locale, Model model) {
		return "i1";
	}
	// insert
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		DB db = new DB<Memo>("/Users/saebyul/SqliteDB/0916memo.db", "memo");
		
		if (db.open()) {
			if (request.getParameter("title") != null
					&& request.getParameter("memo") != null
					&& db.insertData(new Memo(request.getParameter("title"), request.getParameter("memo")))) {				
				model.addAttribute("message", "새 데이터를 추가했습니다.");
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			}
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}
	
	// select
	@RequestMapping(value = "/list", method = RequestMethod.GET)
	public String select(Locale locale, Model model) {
		DB db = new DB<Memo>("/Users/saebyul/SqliteDB/0916memo.db", "memo");
		
		if (db.open()) {
			String htmlString = db.selectStringData(new Memo());
			model.addAttribute("list", htmlString);
			db.close();
		}
		
		return "list";
	}
	
	// update를 위해 사용
	@RequestMapping(value = "/u1", method = RequestMethod.GET)
	public String u1(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		DB db = new DB<Memo>("/Users/saebyul/SqliteDB/0916memo.db", "memo");
		
		if (db.open()) {
			if (request.getParameter("idx") != null && DB.isIntegerString(request.getParameter("idx"))) {
				Memo memo = (Memo) db.selectData(Integer.parseInt(request.getParameter("idx")), new Memo());
				model.addAttribute("idx", memo.idx);
				model.addAttribute("title", memo.title);
				model.addAttribute("memo", memo.memo);
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			}
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		return "u1";
	}
	@RequestMapping(value = "/update", method = RequestMethod.GET)
	public String update(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
//		if (request.getParameter("idx") == null) {
//			model.addAttribute("message", "입력 데이터가 잘못 되었습니다.");
//			return "message";
//		}
		
		DB db = new DB<Memo>("/Users/saebyul/SqliteDB/0916memo.db", "memo");
		
		if (db.open()) {
			if (request.getParameter("idx") != null && DB.isIntegerString(request.getParameter("idx"))
					&& request.getParameter("title") != null
					&& request.getParameter("memo") != null
					&& db.updateData(new Memo(Integer.parseInt(request.getParameter("idx"))
							, request.getParameter("title"), request.getParameter("memo")))) {				
				model.addAttribute("message", "데이터를 수정했습니다.");
			} else {
				model.addAttribute("message", "데이터 수정에 실패했습니다.");
			}
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}	
}

DB

더보기
package com.kopo.memo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

import org.sqlite.SQLiteConfig;

public class DB<T> {
	private String dbFileName;
	private String tableName;
	private Connection connection;
	static {
		try {
			Class.forName("org.sqlite.JDBC");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	public DB(String dbFileName, String tableName) {
		this.dbFileName = dbFileName;
		this.tableName = tableName;
	}
	
	public boolean open() {
		SQLiteConfig config = new SQLiteConfig();
		try {
			this.connection = DriverManager.getConnection("jdbc:sqlite:/" + this.dbFileName, config.toProperties());
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public void close() {
		if (this.connection != null) {
			try {
				this.connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
//	public void createTable() {
//		String fieldString = "";
//		fieldString = fieldString + "idx INT PRIMARY KEY AUTOINCREMENT";
//		fieldString = fieldString + ", name TEXT";
//		fieldString = fieldString + ", middleScore INT";
//		fieldString = fieldString + ", finalScore INT";
//		String query = "CREATE TABLE " + this.tableName + " (" + fieldString + ")";
//		try {
//			Statement statement = this.connection.createStatement();
//			statement.executeUpdate(query);
//			statement.close();
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//	}
	
	public boolean createTable(T t) {
		Class<?> dataClass = t.getClass();
		Field[] dataClassFields = dataClass.getDeclaredFields();
		
		String fieldString = "";
		
		for (Field field: dataClassFields) {
			if (!fieldString.isEmpty()) {
				fieldString = fieldString + ",";
			}
			String fieldName = field.getName();
			String fieldType = field.getType().toString();
			fieldString = fieldString + fieldName;
			if (fieldName.matches("idx")) {
				fieldString = fieldString + " INTEGER PRIMARY KEY AUTOINCREMENT";
			} else if (fieldType.matches("(int|long|short)")) {
				fieldString = fieldString + " INTEGER";
			} else if (fieldType.matches("(float|double)")) {
				fieldString = fieldString + " REAL";
			} else if (fieldType.matches(".*String")) {
				fieldString = fieldString + " TEXT";
			}
		}
		
		String query = "CREATE TABLE " + this.tableName + " (" + fieldString + ")";
		try {
			Statement statement = this.connection.createStatement();
			statement.executeUpdate(query);
			statement.close();
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}
	
//	public void insertData(Student student) {
//		String fieldString = "";
//		fieldString = fieldString + "name";
//		fieldString = fieldString + ", middleScore";
//		fieldString = fieldString + ", finalScore";
//		String valueString = "";
//		valueString = valueString + "'" + student.name + "'";
//		valueString = valueString + ", " + student.middleScore;
//		valueString = valueString + ", " + student.finalScore;
//		String query = "INSERT INTO " + this.tableName + " (" + fieldString + ") VALUES(" + valueString + ")";
//		try {
//			Statement statement = this.connection.createStatement();
//			statement.executeUpdate(query);
//			statement.close();
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//	}
	
//	public boolean insertData(T t) {
//		Class<?> dataClass = t.getClass();
//		Field[] dataClassFields = dataClass.getDeclaredFields();
//		
//		String fieldString = "";
//		String valueString = "";
//
//		for (Field field: dataClassFields) {
//			String fieldName = field.getName();
//			String fieldType = field.getType().toString();
//			if (fieldName.matches("idx")) {
//				continue;
//			}
//			if (!fieldString.isEmpty()) {
//				fieldString = fieldString + ",";
//			}
//			if (!valueString.isEmpty()) {
//				valueString = valueString + ",";
//			}
//			fieldString = fieldString + fieldName;
//			try {
//				if (fieldType.matches(".*String")) {
//					valueString = valueString + "'" + field.get(t) + "'";
//				} else {
//					valueString = valueString + field.get(t);
//				}
//			} catch (IllegalArgumentException e) {
//				e.printStackTrace();
//			} catch (IllegalAccessException e) {
//				e.printStackTrace();
//			}
//		}
//		
//		String query = "INSERT INTO " + this.tableName + " (" + fieldString + ") VALUES(" + valueString + ")";
//		try {
//			Statement statement = this.connection.createStatement();
//			statement.executeUpdate(query);
//			statement.close();
//			return true;
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//		return false;
//	}
	
	public boolean insertData(T t) {
		Class<?> dataClass = t.getClass();
		Field[] dataClassFields = dataClass.getDeclaredFields();
		
		String fieldString = "";
		String valueString = "";

		class PreparedValue {
			int type = 0;
			int intValue = 0;
			double floatValue = 0;
			String stringValue = "";
			
			PreparedValue(int intValue) {
				this.type = 1;
				this.intValue = intValue;
			}

			PreparedValue(double floatValue) {
				this.type = 2;
				this.floatValue = floatValue;
			}

			PreparedValue(String stringValue) {
				this.type = 3;
				this.stringValue = stringValue;
			}
		}
		
		ArrayList<PreparedValue> preparedValue = new ArrayList<PreparedValue>();
		
		for (Field field: dataClassFields) {
			String fieldName = field.getName();
			String fieldType = field.getType().toString();
			if (fieldName.matches("idx")) {
				continue;
			}
			if (!fieldString.isEmpty()) {
				fieldString = fieldString + ",";
			}
			if (!valueString.isEmpty()) {
				valueString = valueString + ",";
			}
			fieldString = fieldString + fieldName;
			try {
				if (fieldType.matches("(int|long|short)")) {
					preparedValue.add(new PreparedValue(field.getInt(t)));
					valueString = valueString + "?";
				} else if (fieldType.matches("(float|double)")) {
					preparedValue.add(new PreparedValue(field.getDouble(t)));
					valueString = valueString + "?";
				} else if (fieldType.matches(".*String")) {
					preparedValue.add(new PreparedValue(field.get(t).toString()));
					valueString = valueString + "?";
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		
		String query = "INSERT INTO " + this.tableName + " (" + fieldString + ") VALUES(" + valueString + ")";
		try {
			PreparedStatement statement = this.connection.prepareStatement(query);
			for (int i = 0; i < preparedValue.size(); i++) {
				if (preparedValue.get(i).type == 1) {
					statement.setInt(i + 1, preparedValue.get(i).intValue);
				} else if (preparedValue.get(i).type == 2) {
					statement.setDouble(i + 1, preparedValue.get(i).floatValue);
				} else if (preparedValue.get(i).type == 3) {
					statement.setString(i + 1, preparedValue.get(i).stringValue);
				}
			}
			statement.executeUpdate();
			statement.close();
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}

//	public void updateData(int idx, String memo) {
//		String setString = "memo='" + memo + "'";
//		String whereString = "idx=" + idx;
//		String query = "UPDATE " + this.tableName + " SET " + setString + " WHERE " + whereString;
//		try {
//			Statement statement = this.connection.createStatement();
//			statement.executeUpdate(query);
//			statement.close();
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//	}
	
//	public boolean updateData(T t) {
//		Class<?> dataClass = t.getClass();
//		Field[] dataClassFields = dataClass.getDeclaredFields();
//		String setString = "";
//		String whereString = "";
//		for (Field field : dataClassFields) {
//			if (!setString.isEmpty()) {
//				setString = setString + ",";
//			}
//			String fieldName = field.getName();
//			String fieldType = field.getType().toString();
//			try {
//				if (fieldName.matches("idx")) {
//					whereString = "idx=" + field.get(t);
//				} else if (fieldType.matches(".*String")) {
//					setString = setString + fieldName + "=" + "'" + field.get(t) + "'";
//				} else {
//					setString = setString + fieldName + "=" + field.get(t);
//				}
//			} catch (IllegalArgumentException e) {
//				e.printStackTrace();
//			} catch (IllegalAccessException e) {
//				e.printStackTrace();
//			} catch (Exception e) {
//				e.printStackTrace();
//			}
//		}
//		
//		String query = "UPDATE " + this.tableName + " SET " + setString + " WHERE " + whereString;
//		try {
//			Statement statement = this.connection.createStatement();
//			statement.executeUpdate(query);
//			statement.close();
//			return true;
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//		return false;
//	}

	public boolean updateData(T t) {
		Class<?> dataClass = t.getClass();
		Field[] dataClassFields = dataClass.getDeclaredFields();
		String setString = "";
		String whereString = "";
		
		ArrayList<Object> preparedValue = new ArrayList<Object>();
		
		for (Field field : dataClassFields) {
			if (!setString.isEmpty()) {
				setString = setString + ",";
			}
			String fieldName = field.getName();
			String fieldType = field.getType().toString();
			try {
				if (fieldName.matches("idx")) {
					whereString = "idx=" + field.get(t);
				} else {
					preparedValue.add(field.get(t));
					setString = setString + fieldName + "=?";
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		String query = "UPDATE " + this.tableName + " SET " + setString + " WHERE " + whereString;
		try {
			PreparedStatement statement = this.connection.prepareStatement(query);
			for (int i = 0; i < preparedValue.size(); i++) {
				statement.setObject(i + 1, preparedValue.get(i));
			}
			statement.executeUpdate();
			statement.close();
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}
	
//	public String selectData() {
//		String query = "SELECT * FROM " + this.tableName;
//		String htmlTxt = "";
//		try {
//			Statement statement = this.connection.createStatement();
//			ResultSet result = statement.executeQuery(query);
//			while(result.next()) {
//				htmlTxt = htmlTxt + "<tr>";
//				htmlTxt = htmlTxt + "<td>" + result.getInt("idx") + "</td>";
//				htmlTxt = htmlTxt + "<td>" + result.getString("name") + "</td>";
//				htmlTxt = htmlTxt + "<td>" + result.getInt("middleScore") + "</td>";
//				htmlTxt = htmlTxt + "<td>" + result.getInt("finalScore") + "</td>";
//				htmlTxt = htmlTxt + "</tr>";
//			}
//			result.close();
//			statement.close();
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//		return htmlTxt;
//	}
	
	public String selectStringData(T t) {
		Class<?> dataClass = t.getClass();
		Field[] dataClassFields = dataClass.getDeclaredFields();

		String query = "SELECT * FROM " + this.tableName;
		ArrayList<T> resultDataSet = new ArrayList<T>(); 
		try {
			Statement statement = this.connection.createStatement();
			ResultSet result = statement.executeQuery(query);
			while(result.next()) {
				T rowData = (T)dataClass.getDeclaredConstructor().newInstance();

				for(Field field : dataClassFields) {
					String fieldName = field.getName();
					String fieldType = field.getType().toString();
						
					if (fieldType.matches("(int)")) {
						field.setInt(rowData, result.getInt(fieldName));
					} else if (fieldType.matches("(long)")) {
						field.setLong(rowData, result.getLong(fieldName));
					} else if (fieldType.matches("(float|double)")) {
						field.setDouble(rowData, result.getDouble(fieldName));
					} else if (fieldType.matches(".*String")) {
						field.set(rowData, result.getString(fieldName));
					}
				}
				resultDataSet.add(rowData);
			}
			result.close();
			statement.close();

			Method toHtmlStringMethod = dataClass.getDeclaredMethod("toHtmlString");
			StringBuffer htmlString = new StringBuffer();
			for (T row : resultDataSet) {
				htmlString.append((String) toHtmlStringMethod.invoke(row));
			}
			return htmlString.toString();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}
	
	public ArrayList<T> selectData(T t) {
		Class<?> dataClass = t.getClass();
		Field[] dataClassFields = dataClass.getDeclaredFields();

		String query = "SELECT * FROM " + this.tableName;
		ArrayList<T> resultDataSet = new ArrayList<T>(); 
		try {
			Statement statement = this.connection.createStatement();
			ResultSet result = statement.executeQuery(query);
			while(result.next()) {
				T rowData = (T)dataClass.getDeclaredConstructor().newInstance();

				for(Field field : dataClassFields) {
					String fieldName = field.getName();
					String fieldType = field.getType().toString();
						
					if (fieldType.matches("(int)")) {
						field.setInt(rowData, result.getInt(fieldName));
					} else if (fieldType.matches("(long)")) {
						field.setLong(rowData, result.getLong(fieldName));
					} else if (fieldType.matches("(float|double)")) {
						field.setDouble(rowData, result.getDouble(fieldName));
					} else if (fieldType.matches(".*String")) {
						field.set(rowData, result.getString(fieldName));
					}
				}
				resultDataSet.add(rowData);
			}
			result.close();
			statement.close();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return resultDataSet;
	}
	
	public T selectData(int idx, T t) {
		Class<?> dataClass = t.getClass();
		Field[] dataClassFields = dataClass.getDeclaredFields();

		String query = "SELECT * FROM " + this.tableName + " WHERE idx=" + idx;
		try {
			Statement statement = this.connection.createStatement();
			ResultSet result = statement.executeQuery(query);
			T rowData = (T)dataClass.getDeclaredConstructor().newInstance();
			if(result.next()) {
				for(Field field : dataClassFields) {
					String fieldName = field.getName();
					String fieldType = field.getType().toString();
						
					if (fieldType.matches("(int)")) {
						field.setInt(rowData, result.getInt(fieldName));
					} else if (fieldType.matches("(long)")) {
						field.setLong(rowData, result.getLong(fieldName));
					} else if (fieldType.matches("(float|double)")) {
						field.setDouble(rowData, result.getDouble(fieldName));
					} else if (fieldType.matches(".*String")) {
						field.set(rowData, result.getString(fieldName));
					}
				}
			}
			result.close();
			statement.close();
			
			return rowData;
			
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return (T)(new Object());
	}
	
	
	
	public static boolean isIntegerString(String numericString) {
		try {
			int result = Integer.parseInt(numericString);
			return true;
		} catch (Exception e) {
		}
		return false;
	}

	public static boolean isFloatString(String numericString) {
		try {
			double result = Double.parseDouble(numericString);
			return true;
		} catch (Exception e) {
		}
		return false;
	}
}

Memo.class

더보기
package com.kopo.memo;

public class Memo {
//	private int idx;
//	private String title;
//	private String memo;
	
	int idx;
	String title;
	String memo;
	
	Memo() {
		
	}
	
	public Memo(String title, String memo) {
		this.title = title;
		this.memo = memo;
	}
	
	public Memo(int idx, String title, String memo) {
		this.idx = idx;
		this.title = title;
		this.memo = memo;
	}
	
	public String toHtmlString() {
		StringBuffer htmlString = new StringBuffer();
		htmlString.append("<tr>");
		htmlString.append("<td>" + this.idx + "</td>");
		htmlString.append("<td>" + this.title + "</td>");
		htmlString.append("<td>" + this.memo + "</td>");
		htmlString.append("<td>" + "<a href=u1?idx=" + this.idx + ">상세보기</a>" + "</td>");
		htmlString.append("</tr>");
		
		return htmlString.toString();
	}

	public int getIdx() {
		return idx;
	}

	public void setIdx(int idx) {
		this.idx = idx;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getMemo() {
		return memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

	
}

message.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>

<head>
	<title>Message</title>
</head>
<body>

<P>${message}</P>
</body>
</html>

i1.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
  </head>
  <body>
    <form action="insert">
      <input type="text" placeholder="title" name="title" />
      <textarea name="memo" style="width: 100%;"></textarea>
      <input type="submit" value="입력 완료" />
    </form>
    <br /><br />
    <a href="list" style="padding: 10px 20px; background: #eee;">리스트로</a>
  </body>
</html>

list.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
  </head>
  <body>
    <table style="width: 100%;">
      <thead>
        <tr>
        <th>idx</th>
        <th>title</th>
        <th>memo</th>
        <th></th>
      </thead>
      ${list }
    </table>
    <a href="i1" style="padding: 10px 20px; background: #eee;">글쓰기</a>
  </body>
</html>

u1.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
  </head>
  <body>
    <form action="update">
      <input type="hidden" name="idx" value="${idx }" />
      <input type="text" placeholder="title" name="title" value="${title }"/>
      <textarea name="memo" style="width: 100%;">${memo }</textarea>
      <input type="submit" value="수정 완료" />
    </form>
    <br /><br />
    <a href="list" style="padding: 10px 20px; background: #eee;">리스트로</a>
  </body>
</html>

 

 

JSP 파일 안에 자바코드

<!DOCTYPE html>
  <head>
	<title>Home</title>
  </head>
  <body>
    <h1>
	  Hello world!  
    </h1>
    <%
    int a = 10;
    int b = 5;
    int c = a + b;
    
    
    // jsp 안에 java코드 사용(이제는 하지 말 것)
    // jstl : jsp에서 태그 <> 형태로 제공하는 기능 (jsp 기반)(한국은 아직 여기...)
    // thymeleaf (<html> 기반) (spring boot 기본 방식 가장 최신이고 표준이다.)
    
    %>
    <P> axb+c=<%=a*b+c %>. </P> <!-- 대문자로 써도 되긴 하지만 html 표준은 태그를 소문자로 사용한다. <p> 이런식으로 사용 -->
        
  </body>
</html>

이렇게 <% %> 사이에 자바 코드를 넣을 수 있다. (<!-- --> 사이에 주석을 넣을 수 있는 것처럼...)

하지만 매우매우 옛날 방식이다. 20~15년 전 사용되던 방식이라고 한다. HTML은 코드에 모든게 다 드러나기 때문에 자바 코드를 그대로 사용하면 보안에 치명적이기 때문이다. 다만... 외국 기준이고.... IT 기술에 뒤쳐진 한국에서는 5년 전 까지도 이대로 유지보수 되는 곳이 있었다고... 지금도 있을지 모른다고 한다 -_-;;

절대 사용하지는 말되 혹시 이런게 나오면 아... 자바코드구나... 하고 사용하진 말라고...

 

 

C 태그

우선 C 태그를 사용하려면 다음을 임포트 해야한다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

HomeController

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		DB db = new DB<Memo>("/Users/saebyul/SqliteDB/0916memo.db", "memo");
		
		model.addAttribute("test", new Memo());	// jsp파일. 즉, HTML에서 객체를 사용하기 위해 test라는 변수에 담아 넘겨준다.
		
		if (db.open()) {
			ArrayList<Memo> list = db.selectData(new Memo());
			model.addAttribute("list", list);
			db.close();
		}
		return "home";
	}

 

home.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>

<!DOCTYPE html>
  <head>
	<title>Home</title>
  </head>
  <body>
    <!-- test라는 변수가 HomeController에게 Memo라는 객체를 넘겨 받아 그 객체의 title, memo 안에 원하는 value를 넣는다. -->
    <c:set target="${test }" property="title" value="야호호호"></c:set>
    <c:set target="${test }" property="memo" value="코로나야끝나라아아아"></c:set>
    <p> title :  <c:out value="${test.title }"></c:out> </p>
    <p> memo :  <c:out value="${test.memo }"></c:out> </p>
    
     <table style="width: 100%;">
      <tbody>
        <c:forEach var="each" items="${list }">
          <tr>
            <td>${each.getIdx() }</td>                        <!-- getter를 쓸 수도 있고, -->
            <td>${each.title }</td>                           <!-- c:out 태그를 생략하고 사용할 수도 있다. (편리!) -->
            <td><c:out value="${each.memo }"></c:out></td>    <!-- Full로 쓰면 c:out 태그를 넣어야 한다. -->
            <td><a href="u1?idx=${each.idx }">수정하기</a>
          </tr>
        </c:forEach>
      </tbody>
    </table>
        
  </body>
</html>

 

지금 여기 2가지가 있는데,

<!-- test라는 변수가 HomeController에게 Memo라는 객체를 넘겨 받아 그 객체의 title, memo 안에 원하는 value를 넣는다. -->
<c:set target="${test }" property="title" value="야호호호"></c:set>
<c:set target="${test }" property="memo" value="코로나야끝나라아아아"></c:set>
<p> title :  <c:out value="${test.title }"></c:out> </p>
<p> memo :  <c:out value="${test.memo }"></c:out> </p>

이 부분은 홈컨트롤러로부터 Memo라는 객체를 test라는 변수로 넘겨 받아, setter, getter를 이용해 객체를 활용하는 것이다. 이때 C 태그를 이용했다.

주의 : new Memo()라는 객체를 넘겨받았다. 값이 들어 있는 객체를 받은 것이 아니다.

 

<table style="width: 100%;">
  <tbody>
    <c:forEach var="each" items="${list }">
      <tr>
        <td>${each.getIdx() }</td>                        <!-- getter를 쓸 수도 있고, -->
        <td>${each.title }</td>                           <!-- c:out 태그를 생략하고 사용할 수도 있다. (편리!) -->
        <td><c:out value="${each.memo }"></c:out></td>    <!-- Full로 쓰면 c:out 태그를 넣어야 한다. -->
        <td><a href="u1?idx=${each.idx }">수정하기</a>
      </tr>
    </c:forEach>
  </tbody>
</table>

이 부분은 홈컨트롤러가 DB한테 selectData 메소드를 실행시켜 데이터베이스로부터 쿼리를 받아 ArrayList 안에 Memo 객체를 넣어 list라는 변수로 넘겨 받아 for each를 이용해 테이블을 출력하는 것이다.

<c:out value="${each.memo }"></c:out> 이렇게 C태그 getter를 이용할 수도 있고,

${each.title } 그냥 이렇게 C태그를 생략할 수도 있으며

${each.getIdx() } 이렇게 넘겨 받은 객체에 포함된 getter를 이용할 수도 있다.

물론... 자바 내에서 실행되는게 아니라 객체 내에 getter는 반드시 포함되어 있어야 한다.

7z : 압축도 많이 되고, 64비트에 멀티코어 지원을 잘 해서 좋은데 문제는... 오류정정이 되지 않아 파일이 크고 많아지면..... 좋지 않다...

파일 개수가 수천 개 정도로 적으면 괜찮은데... 10만 단위 급으로 많을 경우 열심히 압축 했는데 제대로 못 푸는 상황이 발생할 가능성이 높다. 심지어 저장모드인데도 오류 발생해서 몇 시간의 노력이 물거품이 되는 것을 몇 번 겪고 나니..... 못 쓰겠다......

 

zip : 사실... 7z 쓰기 시작한 후로 zip 무시했는데... 범용성은 물론, 안정성에서 zip이 최강이다. bzip도 50만건이 넘어가니 저장모드에서조차 가끔 실패하는 경우가 있는데 zip은... 단 한 번도 실패한 적이 없다. 속도도 너무 느리지 않고... 가장 안정적이고 딱히 모난 구석 없는 최고의 압축인 듯 하다.

 

tar : 무압축의 대명사... 다른 포맷에서 저장 모드로 해도 tar의 속도를 따라오지는 못한다. 압도적인 속도!! 다만... 문제는 파일이 많아지면 오류가 있다;; 파일이 7z과 마찬가지로 파일이 작고 개수가 적을 때만 쓰는게 좋을 것 같다.

 

bzip2 : gzip은 사실상 bzip2가 나와서 쓸 일이 없고... 그래서 처음부터 bzip2를 쓴건데 생각보다 안정적이다. 문제는 저장모드로 해도 시간이 꽤나 걸린다;; 저장모드로 해도 용량이 상당히 줄어드는걸 보면 기본적으로 완전 저장모드는 없는 듯 하다. 50만개 이상의 파일도 잘 저장된다.

 

 

결론은.... 무조건 안정성이 최고로 중요할 때는 zip!!

안정성도 필요하면서 용량도 줄여야 할 때는 bzip2!!

크러스컬 : 엣지를 오름차순으로 정렬하고, 비용이 최소인 엣지부터 유효한 노드를 찾아나간다. > 엣지를 이용해서 노드를 찾음

프림 : 임의의 노드를 하나 잡고 그 노드를 중심으로 엣지의 최솟값을 찾는다. > 노드를 이용해서 엣지를 찾음

 

Prim's Algorithm2020/09/21 - [개발자/Algorithm] - Prim's Algorithm

 

1. Spanning subgraph (Spanning subtree)

정의 : 모든 꼭짓점을 포함하는 부분 그래프이다.

위 그림에서 보면 좌측의 그래프는 우측의 노란색으로 표시된 것과 같은 8개의 Spanning subtree를 갖는다.

더 큰 그래프를 보면 아래와 같다.

이 그래프의 특징은 모든 꼭짓점을 포함하도록 변이 이어지나, 순환하지 않는다.

 

Spanning subgraph 의 종류

Spanning subgraph 가운데  r-정규 그래프인 것을 r-factor라고 한다.

0차 인자 : 꼭짓점 집합 위의 무변 그래프. 즉, 그냥 점 하나... 0차원이다.

1차 인자 : 완벽 부합(Perfect matching)이라고 한다.

2차 인자 이상 : 아래 그래프를 확인하자.

페테르센 그래프에서, 붉은 변들은 1차 인자(완벽 부합)를 이루며, 푸른 변들은 2차 인자를 이룬다.

데자르그 그래프에서, 붉은 색의 변들 · 푸른 색의 변들 · 녹색의 변들은 각각 1차 인자(완벽 부합)를 이룬다.

 

 

2. Minimum cost spanning tree

여러 꼭짓점(Node, Vertex)과 각 변(Edge)이 있다. 이 모든 꼭짓점을 연결하는 방법(Spanning subtree)는 무수히 많다. 그 중, 변의 비용이 최소가 되는 경우를 '최소 비용 신장 나무(Minimum cost spanning tree)'라 한다.

이를 증명하려면 '선택 공리'가 필요하다는데... 수학과가 아니니 넘어가도록 한다.

이러한 그래프에서 최소 비용을 찾는 대표적인 알고리즘이 Prim's Algorithm(프림 알고리즘)과 Kruskal' Algorithm(크러스컬 알고리즘)이 있다.

꼭짓점의 개수를 V, 변의 개수를 E라고 할 때,

프림 알고리즘은 Binary heap(이진 힙)을 이용해 O(E log V)의 시간복잡도를 가지고, 그래프가 충분히 빽빽한 경우(E = Ω(V log V))에는 Fibonacci heap(피보나치 힙)을 이용해 훨씬 빠르게 할 수 있다. 이 방법은 복잡도가 O(E + V log V)까지 떨어진다.

또한, 크러스컬 알고리즘은 O (E log V)의 시간 복잡도를 갖는다.

음... 솔직히 무슨 말인지 잘 모르겠다 -_-;; 이진 로그인지, 자연 로그인지, 상용 로그인지 설명도 없고... 수학과면 뭔지 알겠지만... 아마 사용로그 같기는 한데..... 그냥 비교 설명이 잘 된 블로그를 하나 소개한다.

mini-ko.tistory.com/8

 

최소비용신장트리(MST), 크루스칼, 프림 알고리즘

신장트리 (Spanning Tree) 일단, 최소비용신장트리를 설명하기전 신장트리가 먼지 알아보자. 신장트리란 트리의 특수한 형태로 그래프의 모든 정점을 포함하면서 그 모든 정점들이 연결되어야 있어

mini-ko.tistory.com

크러스컬 : 엣지를 오름차순으로 정렬하고, 비용이 최소인 엣지부터 유효한 노드를 찾아나간다.

프림 : 임의의 노드를 하나 잡고 그 노드를 중심으로 엣지의 최솟값을 찾는다.

크러스컬은 평균적이고, 프림은 코드를 얼마나 효율적으로 짰는지에 따라 효율이 극대화 되거나 극악이 된다.

이론적으로는 '엣지 >> 노드'인 경우 프림이 훨씬 빠르지만 잘못 짜면 망한다... 그래서 알고리즘 메달리스트들은 크러스컬만 쓴다고...

We can achieve this bound as follows: first sort the edges by weight using a comparison sort in O(E log E) time; this allows the step "remove an edge with minimum weight from S" to operate in constant time. Next, we use a disjoint-set data structure to keep track of which vertices are in which components. We place each vertex into its own disjoint set, which takes O(V) operations. Finally, in worst case, we need to iterate through all edges, and for each edge we need to do two 'find' operations and possibly one union. Even a simple disjoint-set data structure such as disjoint-set forests with union by rank can perform O(E) operations in O(E log V) time. Thus the total time is O(E log E) = O(E log V).

뭔소리죠...?

코드 최적화... 이 압축하는게 그림으로 볼 때 그래프의 모양(MST)이 바뀌는 것은 아니다. 단, 컴퓨터라 사람처럼 한 눈에 볼 수 없어 '자식 - 부모'의 노드 관계를 구하기 위해서는 '1 - 3 - 5 - 7' 이라는 그래프에서 7의 부모를 찾기 위해서 5를 찾고, 다시 5의 부모가 있는지 찾기 위해서 3을 찾고.. 이런식으로 3번 재귀함수를 돌아야 한다. 이게 꼭짓점이 많아지고, 노드가 일렬로 이어지면 시간복잡도가 매우 높아지기 때문에 재귀에서 return이 될 때 3, 5, 7의 부모를 모두 1이라 업데이트를 해준다.

 

 

Kruskal.java

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Scanner;


public class Kruskal {
    static int V, E;                    // V : 점, E : 변
    static ArrayList<Edge> mst;         // result값 Minimum spanning tree
    static int[] arr ;                  // 각 노드의 부모가 누구인지를 저장한다. ex) arr[0] = 1 이면 0의 부모(root node)는 1이다.
    static PriorityQueue<Edge> pq;      // 모든 노드(점)을 힙에 넣고 시작.

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // Vertex : 전체 점의 개수를 입력 받는다. (샘플에서 6에 해당)
        V = sc.nextInt();
        // Edge : 전체 변의 개수를 입력 받는다. (샘플에서 9에 해당)
        E = sc.nextInt();

        mst = new ArrayList<>();
        arr = new int[V+1]; // array는 인덱스가 0부터 시작하는데 문제의 점이 1부터 6까지라 인덱스 0번을 제외하고 1~6을 쓰기 위헤 V+1개의 배열 생성.
        // Queue는 기본적으로 FIFO 구조를 갖는다. Priority queue는  '최대 우선순위 큐'와 '최소 우선순위 큐'로 나뉜다.
        pq = new PriorityQueue<>();

        // vertex의 개수 + 1개 만큼 반복. (i = 1부터 시작하면 안 된다. 0번을 안 쓰더라도 배열은 반드시 0번부터 채워야한다. 비워둘 수 없다.)
        for (int i = 0; i <= V; i++) {
            // 각 vertex는 시작할 때는 자기 자신을 root로 초기화.
            arr[i] = i;
        }

        // 각 노드의 연결 관계와 비용을 입력 받는다. (샘플에서 1 2 5...  1 3 4 에 해당)
        for (int i = 0; i < E; i++) {
            int v1 = sc.nextInt();      // 1번 노드와...  1번 노드와
            int v2 = sc.nextInt();      // 2번 노드의...  3번 노드의
            int value = sc.nextInt();   // 비용은 5다...  비용은 4다

            // 우선순위 큐에 Edge라는 객체를 하나씩 추가한다. Edge는 변으로, 자기 자신의 좌우의 노드와 비용에 대한 정보를 갖는다.
            pq.add(new Edge(v1, v2, value));
        }

        // V개의 노드를 연결하는 최소 Edge의 수는 '노드-1'이므로 mst.size()가 'V-1'이 될 때까지 반복한다.
        // 즉, mst.size()가 'V-2'개일 때 반복하다, 마지막 하나를 더 찾아 'V-1'개가 되면 반복문 조건이 false가 된다.
        while(mst.size() < (V-1)) {
            // PQ에 하나씩 넣었고, 이제 하나씩 뺀다. PQ로 넣어서 FIFO이 아닌 오름차순으로 나온다.(value 기준)
            Edge edge = pq.poll();
            // Edge(변)의 앞뒤 각각 노드의 최종 부모가 서로 같지 않다면(= 서로 다른 그룹이라면 혹은 아직 그룹이 아니라면) 실행. (Edge의 좌우 노드의 최종 부모가 같다면 이미 같은 그룹이니 해당 엣지는 로직을 돌지 않고 버린다.)
            if(find(edge.start)!=find(edge.end))
            {
                // 같은 그룹이 아니라면 MST 로직을 만족(True)하니까 MST ArrayList에 Edge 객체를 추가한다.
                mst.add(edge);
                // MST에 추가한 후 이 Edge가 속한 그룹(Union)이 새 그룹이든, 기존 그룹에 결합된 그룹이든간에 Edge 객체의 좌우 노드의 최종 부모(root)를 갱신해준다.
                union(edge.start, edge.end);
            }
        }

        System.out.println(mst);
    }

    static int find(int n) {            // 입력 받은 노드의 최종 부모를 구한다.
        if (n == arr[n]) {              // 자기 자신이 root면 자기 자신을 반환. root가 바뀌어 자기 자신이 root가 아니면 자신의 부모 노드가 root인지를 재귀를 통해 확인. 이 경우 아래 else문을 탄다.
            return n;
        } else {
            int p = find(arr[n]);       // 각 노드의 부모를 찾는 함수를 재귀로 호출한다. 자기 자신이 root가 되어 if문을 탈 때까지 재귀를 반복한다. 재귀를 돌 때 arr[n]을 넣어 자기의 부모를 넣는다.
                                        // 그러면 그 부모는 또 자기의 부모를 찾는다. 그리고 더이상 부모가 없고 자기 자신이 root면 return을 한다. // ex) arr[0] = 1, arr[1] = 5, arr[5] = 5 자기 자신인 최종 root가 나오면 3번 호출되어 if문을 타고 return된다.
            arr[n]=p;                   // path compression : 코드 최적화. 불필요한 재귀 호출을 줄이기 위해 재귀를 들어가 return 되어 나올 때 각 노드의 최종 부모(root)를 가리키도록 저장한다.
                                        // 즉, arr[0] = 3, arr[1] = 3, arr[5] = 3. 그러면 다음번에는 1+1번만 호출되어 return된다.
            return p;                   // 최종 부모(root)를 return.
        }
    }

    // Union 로직 함수. 노드1과 노드2의 최종 부모가 다르다면 같은 그룹이 아니므로 하나의 그룹으로 결합한다. 이 때 root는 뒤쪽 노드가 된다.
    static void union(int n1, int n2) {
        int p1 = find(n1);  // p1 : n1의 부모, n1 : 앞쪽 인자 node
        int p2 = find(n2);  // p2 : n2의 부모, n2 : 뒤쪽 인자 node

        if (p1 != p2) {
            arr[p1]= p2;
        }
    }

    // Edge 객체를 클래스로 정의. 2개의 노드와 1개의 비용을 변수로 갖는다.
    static class Edge implements Comparable<Edge>{
        int start, end, value;

        Edge(int s, int e, int v) {
            this.start = s;
            this.end = e;
            this.value = v;
        }
        @Override
        public int compareTo(Edge o) {
            return this.value - o.value;
        }
        @Override
        public String toString() {
            return "Edge [start=" + start + ", end=" + end + ", value=" + value + "]\n";
        }
    }
}

/*
6
9
1 2 5
1 3 4
2 3 2
2 4 7
3 4 6
4 6 8
3 5 11
4 5 3
5 6 8
*/
// source : https://velog.io/@dudrkdl777/Graph-최소신장트리-MSTKruskal알고리즘

수업때 한 원본 소스...

여기서 보면 union 함수 안에

if (p1 != p2) {
	arr[p1]= p2;
}

이 부분이 Path Compression(경로 압축)을 하는 과정... 즉, 코드 최적화다.

 

Kruskal2.java

// 기존 1번 코드 원본에서 불필요하게 반복되는 find 함수의 실행을 줄임.

import java.io.IOException;
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Scanner;


public class Kruskal2 {
    static int V, E;                    // V : 점, E : 변
    static ArrayList<Edge> mst;         // result값 Minimum spanning tree
    static int[] arr ;                  // 각 노드의 부모가 누구인지를 저장한다. ex) arr[0] = 1 이면 0의 부모(root node)는 1이다.
    static PriorityQueue<Edge> pq;      // 모든 노드(점)을 힙에 넣고 시작.

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);

        long startTime = System.nanoTime();

        // Vertex : 전체 점의 개수를 입력 받는다. (샘플에서 6에 해당)
        V = sc.nextInt();

        // Edge : 전체 변의 개수를 입력 받는다. (샘플에서 9에 해당)
        E = sc.nextInt();

        mst = new ArrayList<>();
        arr = new int[V+1]; // array는 인덱스가 0부터 시작하는데 문제의 점이 1부터 6까지라 인덱스 0번을 제외하고 1~6을 쓰기 위헤 V+1개의 배열 생성.
        // Queue는 기본적으로 FIFO 구조를 갖는다. Priority queue는  '최대 우선순위 큐'와 '최소 우선순위 큐'로 나뉜다.
        pq = new PriorityQueue<>();

        // vertex의 개수 + 1개 만큼 반복. (i = 1부터 시작하면 안 된다. 0번을 안 쓰더라도 배열은 반드시 0번부터 채워야한다. 비워둘 수 없다.)
        for (int i = 0; i <= V; i++) {
            // 각 vertex는 시작할 때는 자기 자신을 root로 초기화.
            arr[i] = i;
        }

        // 각 노드의 연결 관계와 비용을 입력 받는다. (샘플에서 1 2 5...  1 3 4 에 해당)
        for (int i = 0; i < E; i++) {
            int v1 = sc.nextInt();      // 1번 노드와...  1번 노드와
            int v2 = sc.nextInt();      // 2번 노드의...  3번 노드의
            int value = sc.nextInt();   // 비용은 5다...  비용은 4다

            // 우선순위 큐에 Edge라는 객체를 하나씩 추가한다. Edge는 변으로, 자기 자신의 좌우의 노드와 비용에 대한 정보를 갖는다.
            pq.add(new Edge(v1, v2, value));
        }

        // V개의 노드를 연결하는 최소 Edge의 수는 '노드-1'이므로 mst.size()가 'V-1'이 될 때까지 반복한다.
        // 즉, mst.size()가 'V-2'개일 때 반복하다, 마지막 하나를 더 찾아 'V-1'개가 되면 반복문 조건이 false가 된다.
        while(mst.size() < (V-1)) {
            // PQ에 하나씩 넣었고, 이제 하나씩 뺀다. PQ로 넣어서 FIFO이 아닌 오름차순으로 나온다.(value 기준)
            Edge edge = pq.poll();
            // Edge의 양 끝 각 노드(start, end라 명명함)의 최종 부모(root)를 구한다.
            int parentEdgeStart = find(edge.start);
            int parentEdgeEnd = find(edge.end);

//            int parentEdgeStart = -1;
//            int parentEdgeEnd = -1;
//            // 기본 변수 타입인 int는 null을 받아들일 수 없다. edge 객체의 int 변수를 Integer로 바꿨다.
//            // 의미가 있는지는 모르겠음. 그냥 위에 바로 int에 find 돌려서 넣고 edge 인스턴스 변수는 그대로 int로 해도 될 것 같은데...
//            if (edge.start != null && edge.end != null) {
//                parentEdgeStart = find(edge.start);
//                parentEdgeEnd = find(edge.end);
//            }

            // Edge(변)의 앞뒤 각각 노드의 최종 부모가 서로 같지 않다면(= 서로 다른 그룹이라면 혹은 아직 그룹이 아니라면) 실행. (Edge의 좌우 노드의 최종 부모가 같다면 이미 같은 그룹이니 해당 엣지는 로직을 돌지 않고 버린다.)
            if(parentEdgeStart != parentEdgeEnd)
            {
                // 같은 그룹이 아니라면 MST 로직을 만족(True)하니까 mst ArrayList에 edge 객체를 추가한다.
                mst.add(edge);
                // MST에 추가한 후 이 Edge가 속한 그룹(Union)이 새 그룹이든, 기존 그룹에 결합된 그룹이든간에 Edge 객체의 좌우 노드의 최종 부모(root)를 갱신해준다.
                union(parentEdgeStart, parentEdgeEnd);
            }
        }
        System.out.println(mst);
        long endTime = System.nanoTime(); // (종료점)
        long timeInterval = (endTime - startTime) / 1000000; // (시간차)
        System.out.printf("Code Time : %d (ms) \n", timeInterval);
    }

    static int find(int n) {            // 입력 받은 노드의 최종 부모를 구한다.
        if (n == arr[n]) {              // 자기 자신이 root면 자기 자신을 반환. root가 바뀌어 자기 자신이 root가 아니면 자신의 부모 노드가 root인지를 재귀를 통해 확인. 이 경우 아래 else문을 탄다.
            return n;
        } else {
            int p = find(arr[n]);       // 각 노드의 부모를 찾는 함수를 재귀로 호출한다. 자기 자신이 root가 되어 if문을 탈 때까지 재귀를 반복한다. 재귀를 돌 때 arr[n]을 넣어 자기의 부모를 넣는다.
                                        // 그러면 그 부모는 또 자기의 부모를 찾는다. 그리고 더이상 부모가 없고 자기 자신이 root면 return을 한다. // ex) arr[0] = 1, arr[1] = 5, arr[5] = 5 자기 자신인 최종 root가 나오면 3번 호출되어 if문을 타고 return된다.
            arr[n]=p;                   // path compression : 코드 최적화. 불필요한 재귀 호출을 줄이기 위해 재귀를 들어가 return 되어 나올 때 각 노드의 최종 부모(root)를 가리키도록 저장한다.
                                        // 즉, arr[0] = 3, arr[1] = 3, arr[5] = 3. 그러면 다음번에는 1+1번만 호출되어 return된다.
            return p;                   // 최종 부모(root)를 return.
        }
    }

    // Union 로직 함수. 노드1과 노드2의 최종 부모가 다르다면 같은 그룹이 아니므로 하나의 그룹으로 결합한다. 이 때 root는 뒤쪽 노드가 된다.
    static void union(int p1, int p2) {
        if (p1 != p2) {
            arr[p1]= p2;
        }
    }

    // Edge 객체를 클래스로 정의. 2개의 노드와 1개의 비용을 변수로 갖는다.
    static class Edge implements Comparable<Edge>{
        int start, end, value;
//        Integer start, end, value;

        Edge(int s, int e, int v) {
            this.start = s;
            this.end = e;
            this.value = v;
        }
        @Override
        public int compareTo(Edge o) {
            return this.value - o.value;
        }
        @Override
        public String toString() {
            return "Edge [start=" + start + ", end=" + end + ", value=" + value + "]\n";
        }
    }
}

MST를 구할 때마다 union함수 내에서 find 함수를 불필요하게 다시 호출하는 문제가 있어 해당 로직을 줄였다.

 

Kruskal3.java

// 2번 코드에서 Scanner를 BufferedReader, InputStreamReader로 바꿈.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.PriorityQueue;


public class Kruskal3 {
    static int V, E;                    // V : 점, E : 변
    static ArrayList<Edge> mst;         // result값 Minimum spanning tree
    static int[] arr ;                  // 각 노드의 부모가 누구인지를 저장한다. ex) arr[0] = 1 이면 0의 부모(root node)는 1이다.
    static PriorityQueue<Edge> pq;      // 모든 노드(점)을 힙에 넣고 시작.

    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        // throws IOException을 해주거나,
        // String var = null; 을 미리 인스턴스화 시킨 후
        // try {
        //      var = bufferedReader.readLine();
        // } catch (IOException | NumberFormatException e) {
        //      e.printStackTrace();
        // }
        // 을 해줘야한다.

        long startTime = System.nanoTime();

        // Vertex : 전체 점의 개수를 입력 받는다. (샘플에서 6에 해당)
        V = Integer.parseInt(bufferedReader.readLine());

        // Edge : 전체 변의 개수를 입력 받는다. (샘플에서 9에 해당)
        E = Integer.parseInt(bufferedReader.readLine());

        mst = new ArrayList<>();
        arr = new int[V+1]; // array는 인덱스가 0부터 시작하는데 문제의 점이 1부터 6까지라 인덱스 0번을 제외하고 1~6을 쓰기 위헤 V+1개의 배열 생성.
        // Queue는 기본적으로 FIFO 구조를 갖는다. Priority queue는  '최대 우선순위 큐'와 '최소 우선순위 큐'로 나뉜다.
        pq = new PriorityQueue<>();

        // vertex의 개수 + 1개 만큼 반복. (i = 1부터 시작하면 안 된다. 0번을 안 쓰더라도 배열은 반드시 0번부터 채워야한다. 비워둘 수 없다.)
        for (int i = 0; i <= V; i++) {
            // 각 vertex는 시작할 때는 자기 자신을 root로 초기화.
            arr[i] = i;
        }

        // Edge의 정보는 양쪽 Vertex와 비용, 3개의 정보만 고정적으로 존재하기 때문에 3으로 고정 선언.
        String[] inputTmp = new String[3];
        int v1;
        int v2;
        int value;

        // 각 노드의 연결 관계와 비용을 입력 받는다. (샘플에서 1 2 5...  1 3 4 에 해당)
        for (int i = 0; i < E; i++) {
            inputTmp = bufferedReader.readLine().split(" ");
            v1 = Integer.parseInt(inputTmp[0]);     // 1번 노드와...  1번 노드와
            v2 = Integer.parseInt(inputTmp[1]);     // 2번 노드의...  3번 노드의
            value = Integer.parseInt(inputTmp[2]);  // 비용은 5다...  비용은 4다

            // 우선순위 큐에 Edge라는 객체를 하나씩 추가한다. Edge는 변으로, 자기 자신의 좌우의 노드와 비용에 대한 정보를 갖는다.
            pq.add(new Edge(v1, v2, value));
        }

        // V개의 노드를 연결하는 최소 Edge의 수는 '노드-1'이므로 mst.size()가 'V-1'이 될 때까지 반복한다.
        // 즉, mst.size()가 'V-2'개일 때 반복하다, 마지막 하나를 더 찾아 'V-1'개가 되면 반복문 조건이 false가 된다.
        while(mst.size() < (V-1)) {
            // PQ에 하나씩 넣었고, 이제 하나씩 뺀다. PQ로 넣어서 FIFO이 아닌 오름차순으로 나온다.(value 기준)
            Edge edge = pq.poll();
            // Edge의 양 끝 각 노드(start, end라 명명함)의 최종 부모(root)를 구한다.
            int parentEdgeStart = find(edge.start);
            int parentEdgeEnd = find(edge.end);

//            int parentEdgeStart = -1;
//            int parentEdgeEnd = -1;
//            // 기본 변수 타입인 int는 null을 받아들일 수 없다. edge 객체의 int 변수를 Integer로 바꿨다.
//            // 의미가 있는지는 모르겠음. 그냥 위에 바로 int에 find 돌려서 넣고 edge 인스턴스 변수는 그대로 int로 해도 될 것 같은데...
//            if (edge.start != null && edge.end != null) {
//                parentEdgeStart = find(edge.start);
//                parentEdgeEnd = find(edge.end);
//            }

            // Edge(변)의 앞뒤 각각 노드의 최종 부모가 서로 같지 않다면(= 서로 다른 그룹이라면 혹은 아직 그룹이 아니라면) 실행. (Edge의 좌우 노드의 최종 부모가 같다면 이미 같은 그룹이니 해당 엣지는 로직을 돌지 않고 버린다.)
            if(parentEdgeStart != parentEdgeEnd)
            {
                // 같은 그룹이 아니라면 MST 로직을 만족(True)하니까 mst ArrayList에 edge 객체를 추가한다.
                mst.add(edge);
                // MST에 추가한 후 이 Edge가 속한 그룹(Union)이 새 그룹이든, 기존 그룹에 결합된 그룹이든간에 Edge 객체의 좌우 노드의 최종 부모(root)를 갱신해준다.
                union(parentEdgeStart, parentEdgeEnd);
            }
        }
        bufferedReader.close();
        System.out.println(mst);
        long endTime = System.nanoTime(); // (종료점)
        long timeInterval = (endTime - startTime) / 1000000; // (시간차)
        System.out.printf("Code Time : %d (ms) \n", timeInterval);
    }

    static int find(int n) {            // 입력 받은 노드의 최종 부모를 구한다.
        if (n == arr[n]) {              // 자기 자신이 root면 자기 자신을 반환. root가 바뀌어 자기 자신이 root가 아니면 자신의 부모 노드가 root인지를 재귀를 통해 확인. 이 경우 아래 else문을 탄다.
            return n;
        } else {
            int p = find(arr[n]);       // 각 노드의 부모를 찾는 함수를 재귀로 호출한다. 자기 자신이 root가 되어 if문을 탈 때까지 재귀를 반복한다. 재귀를 돌 때 arr[n]을 넣어 자기의 부모를 넣는다.
            // 그러면 그 부모는 또 자기의 부모를 찾는다. 그리고 더이상 부모가 없고 자기 자신이 root면 return을 한다. // ex) arr[0] = 1, arr[1] = 5, arr[5] = 5 자기 자신인 최종 root가 나오면 3번 호출되어 if문을 타고 return된다.
            arr[n]=p;                   // path compression : 코드 최적화. 불필요한 재귀 호출을 줄이기 위해 재귀를 들어가 return 되어 나올 때 각 노드의 최종 부모(root)를 가리키도록 저장한다.
            // 즉, arr[0] = 3, arr[1] = 3, arr[5] = 3. 그러면 다음번에는 1+1번만 호출되어 return된다.
            return p;                   // 최종 부모(root)를 return.
        }
    }

    // Union 로직 함수. 노드1과 노드2의 최종 부모가 다르다면 같은 그룹이 아니므로 하나의 그룹으로 결합한다. 이 때 root는 뒤쪽 노드가 된다.
    static void union(int p1, int p2) {
        if (p1 != p2) {
            arr[p1]= p2;
        }
    }

    // Edge 객체를 클래스로 정의. 2개의 노드와 1개의 비용을 변수로 갖는다.
    static class Edge implements Comparable<Edge>{
        int start, end, value;
//        Integer start, end, value;

        Edge(int s, int e, int v) {
            this.start = s;
            this.end = e;
            this.value = v;
        }
        @Override
        public int compareTo(Edge o) {
            return this.value - o.value;
        }
        @Override
        public String toString() {
            return "Edge [start=" + start + ", end=" + end + ", value=" + value + "]\n";
        }
    }
}

위 2번 코드에서 Scanner를 BufferedReader, InputStreamReader로 바꿨다.

 

Kruskal4.java

더보기
// 3번 코드에서 ArrayList<Edge> mst를 HashSet<Edge>로, PriorityQueue<Edge> pq를 TreeSet<Edge>로 바꿈.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.TreeSet;

public class Kruskal4 {
    static int V, E;                // V : 점, E : 변
    static HashSet<Edge> mst;       // result값 Minimum spanning tree
    static int[] arr ;              // 각 노드의 부모가 누구인지를 저장한다. ex) arr[0] = 1 이면 0의 부모(root node)는 1이다.
    static TreeSet<Edge> ts;        // 모든 노드(점)을 힙에 넣고 시작.

    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));

        long startTime = System.nanoTime();

        // Vertex : 전체 점의 개수를 입력 받는다. (샘플에서 6에 해당)
        V = Integer.parseInt(bufferedReader.readLine());

        // Edge : 전체 변의 개수를 입력 받는다. (샘플에서 9에 해당)
        E = Integer.parseInt(bufferedReader.readLine());

        mst = new HashSet<>();
        arr = new int[V+1]; // array는 인덱스가 0부터 시작하는데 문제의 점이 1부터 6까지라 인덱스 0번을 제외하고 1~6을 쓰기 위헤 V+1개의 배열 생성.
        ts = new TreeSet();

        // vertex의 개수 + 1개 만큼 반복. (i = 1부터 시작하면 안 된다. 0번을 안 쓰더라도 배열은 반드시 0번부터 채워야한다. 비워둘 수 없다.)
        for (int i = 0; i <= V; i++) {
            arr[i] = i;
        }

        // Edge의 정보는 양쪽 Vertex와 비용, 3개의 정보만 고정적으로 존재하기 때문에 3으로 고정 선언.
        String[] inputTmp = new String[3];
        int v1;
        int v2;
        int value;

        // 각 노드의 연결 관계와 비용을 입력 받는다. (샘플에서 1 2 5...  1 3 4 에 해당)
        // 즉, mst.size()가 'V-2'개일 때 반복하다, 마지막 하나를 더 찾아 'V-1'개가 되면 반복문 조건이 false가 된다.
        for (int i = 0; i < E; i++) {
            inputTmp = bufferedReader.readLine().split(" ");
            v1 = Integer.parseInt(inputTmp[0]);     // 1번 노드와...  1번 노드와
            v2 = Integer.parseInt(inputTmp[1]);     // 2번 노드의...  3번 노드의
            value = Integer.parseInt(inputTmp[2]);  // 비용은 5다...  비용은 4다

            // TreeSet에 Edge라는 객체를 하나씩 추가한다. Edge는 변으로, 자기 자신의 좌우의 노드와 비용에 대한 정보를 갖는다.
            ts.add(new Edge(v1, v2, value));
        }

        // Edge(변)의 앞뒤 각각 노드의 최종 부모가 서로 같지 않다면(= 서로 다른 그룹이라면 혹은 아직 그룹이 아니라면) 실행. (Edge의 좌우 노드의 최종 부모가 같다면 이미 같은 그룹이니 해당 엣지는 로직을 돌지 않고 버린다.)
        while(mst.size() < (V-1)) {
            // TreeSet 잘 될까!?
            Edge edge = ts.pollFirst();
            int parentEdgeStart = find(edge.start);
            int parentEdgeEnd = find(edge.end);

            if(parentEdgeStart != parentEdgeEnd) {
                // 같은 그룹이 아니라면 MST 로직을 만족(True)하니까 mst ArrayList에 edge 객체를 추가한다.
                mst.add(edge);
                // MST에 추가한 후 이 Edge가 속한 그룹(Union)이 새 그룹이든, 기존 그룹에 결합된 그룹이든간에 Edge 객체의 좌우 노드의 최종 부모(root)를 갱신해준다.
                union(parentEdgeStart, parentEdgeEnd);
            }
        }
        bufferedReader.close();
        System.out.println(mst);
        long endTime = System.nanoTime(); // (종료점)
        long timeInterval = (endTime - startTime) / 1000000; // (시간차)
        System.out.printf("Code Time : %d (ms) \n", timeInterval);
    }

    static int find(int n) {            // 입력 받은 노드의 최종 부모를 구한다.
        if (n == arr[n]) {              // 자기 자신이 root면 자기 자신을 반환. root가 바뀌어 자기 자신이 root가 아니면 자신의 부모 노드가 root인지를 재귀를 통해 확인. 이 경우 아래 else문을 탄다.
            return n;
        } else {
            int p = find(arr[n]);       // 각 노드의 부모를 찾는 함수를 재귀로 호출한다. 자기 자신이 root가 되어 if문을 탈 때까지 재귀를 반복한다. 재귀를 돌 때 arr[n]을 넣어 자기의 부모를 넣는다.
            // 그러면 그 부모는 또 자기의 부모를 찾는다. 그리고 더이상 부모가 없고 자기 자신이 root면 return을 한다. // ex) arr[0] = 1, arr[1] = 5, arr[5] = 5 자기 자신인 최종 root가 나오면 3번 호출되어 if문을 타고 return된다.
            arr[n]=p;                   // path compression : 코드 최적화. 불필요한 재귀 호출을 줄이기 위해 재귀를 들어가 return 되어 나올 때 각 노드의 최종 부모(root)를 가리키도록 저장한다.
            // 즉, arr[0] = 3, arr[1] = 3, arr[5] = 3. 그러면 다음번에는 1+1번만 호출되어 return된다.
            return p;                   // 최종 부모(root)를 return.
        }
    }

    // Union 로직 함수. 노드1과 노드2의 최종 부모가 다르다면 같은 그룹이 아니므로 하나의 그룹으로 결합한다. 이 때 root는 뒤쪽 노드가 된다.
    static void union(int p1, int p2) {
        if (p1 != p2) {
            arr[p1]= p2;
        }
    }

    // Edge 객체를 클래스로 정의. 2개의 노드와 1개의 비용을 변수로 갖는다.
    static class Edge implements Comparable<Edge>{
        int start, end, value;

        Edge(int s, int e, int v) {
            this.start = s;
            this.end = e;
            this.value = v;
        }
        @Override
        public int compareTo(Edge o) { return this.value - o.value; }
        @Override
        public String toString() { return "Edge [start=" + start + ", end=" + end + ", value=" + value + "]\n"; }
    }
}

3번 코드에서 ArrayList<Edge> mst를 HashSet<Edge>로, PriorityQueue<Edge> pq를 TreeSet<Edge>로 바꿨다.

 

Kruskal5.java

더보기
// 3번 코드에서 ArrayList<Edge> mst와 PriorityQueue<Edge> pq를 모두 TreeSet<Edge>로 바꿈.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.TreeSet;

public class Kruskal5 {
    static int V, E;                // V : 점, E : 변
    static TreeSet<Edge> mst;       // result값 Minimum spanning tree
    static int[] arr ;              // 각 노드의 부모가 누구인지를 저장한다. ex) arr[0] = 1 이면 0의 부모(root node)는 1이다.
    static TreeSet<Edge> ts;        // 모든 노드(점)을 힙에 넣고 시작.

    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));

        long startTime = System.nanoTime();

        // Vertex : 전체 점의 개수를 입력 받는다. (샘플에서 6에 해당)
        V = Integer.parseInt(bufferedReader.readLine());

        // Edge : 전체 변의 개수를 입력 받는다. (샘플에서 9에 해당)
        E = Integer.parseInt(bufferedReader.readLine());

        mst = new TreeSet<>();
        arr = new int[V+1]; // array는 인덱스가 0부터 시작하는데 문제의 점이 1부터 6까지라 인덱스 0번을 제외하고 1~6을 쓰기 위헤 V+1개의 배열 생성.
        ts = new TreeSet<>();

        // vertex의 개수 + 1개 만큼 반복. (i = 1부터 시작하면 안 된다. 0번을 안 쓰더라도 배열은 반드시 0번부터 채워야한다. 비워둘 수 없다.)
        for (int i = 0; i <= V; i++) {
            arr[i] = i;
        }

        // Edge의 정보는 양쪽 Vertex와 비용, 3개의 정보만 고정적으로 존재하기 때문에 3으로 고정 선언.
        String[] inputTmp = new String[3];
        int v1;
        int v2;
        int value;

        // 각 노드의 연결 관계와 비용을 입력 받는다. (샘플에서 1 2 5...  1 3 4 에 해당)
        // 즉, mst.size()가 'V-2'개일 때 반복하다, 마지막 하나를 더 찾아 'V-1'개가 되면 반복문 조건이 false가 된다.
        for (int i = 0; i < E; i++) {
            inputTmp = bufferedReader.readLine().split(" ");
            v1 = Integer.parseInt(inputTmp[0]);     // 1번 노드와...  1번 노드와
            v2 = Integer.parseInt(inputTmp[1]);     // 2번 노드의...  3번 노드의
            value = Integer.parseInt(inputTmp[2]);  // 비용은 5다...  비용은 4다

            // TreeSet에 Edge라는 객체를 하나씩 추가한다. Edge는 변으로, 자기 자신의 좌우의 노드와 비용에 대한 정보를 갖는다.
            ts.add(new Edge(v1, v2, value));
        }

        // Edge(변)의 앞뒤 각각 노드의 최종 부모가 서로 같지 않다면(= 서로 다른 그룹이라면 혹은 아직 그룹이 아니라면) 실행. (Edge의 좌우 노드의 최종 부모가 같다면 이미 같은 그룹이니 해당 엣지는 로직을 돌지 않고 버린다.)
        while(mst.size() < (V-1)) {
            // TreeSet 잘 될까!?
            Edge edge = ts.pollFirst();
            int parentEdgeStart = find(edge.start);
            int parentEdgeEnd = find(edge.end);

            if(parentEdgeStart != parentEdgeEnd) {
                // 같은 그룹이 아니라면 MST 로직을 만족(True)하니까 mst ArrayList에 edge 객체를 추가한다.
                mst.add(edge);
                // MST에 추가한 후 이 Edge가 속한 그룹(Union)이 새 그룹이든, 기존 그룹에 결합된 그룹이든간에 Edge 객체의 좌우 노드의 최종 부모(root)를 갱신해준다.
                union(parentEdgeStart, parentEdgeEnd);
            }
        }
        bufferedReader.close();
        System.out.println(mst);
        long endTime = System.nanoTime(); // (종료점)
        long timeInterval = (endTime - startTime) / 1000000; // (시간차)
        System.out.printf("Code Time : %d (ms) \n", timeInterval);
    }

    static int find(int n) {            // 입력 받은 노드의 최종 부모를 구한다.
        if (n == arr[n]) {              // 자기 자신이 root면 자기 자신을 반환. root가 바뀌어 자기 자신이 root가 아니면 자신의 부모 노드가 root인지를 재귀를 통해 확인. 이 경우 아래 else문을 탄다.
            return n;
        } else {
            int p = find(arr[n]);       // 각 노드의 부모를 찾는 함수를 재귀로 호출한다. 자기 자신이 root가 되어 if문을 탈 때까지 재귀를 반복한다. 재귀를 돌 때 arr[n]을 넣어 자기의 부모를 넣는다.
            // 그러면 그 부모는 또 자기의 부모를 찾는다. 그리고 더이상 부모가 없고 자기 자신이 root면 return을 한다. // ex) arr[0] = 1, arr[1] = 5, arr[5] = 5 자기 자신인 최종 root가 나오면 3번 호출되어 if문을 타고 return된다.
            arr[n]=p;                   // path compression : 코드 최적화. 불필요한 재귀 호출을 줄이기 위해 재귀를 들어가 return 되어 나올 때 각 노드의 최종 부모(root)를 가리키도록 저장한다.
            // 즉, arr[0] = 3, arr[1] = 3, arr[5] = 3. 그러면 다음번에는 1+1번만 호출되어 return된다.
            return p;                   // 최종 부모(root)를 return.
        }
    }

    // Union 로직 함수. 노드1과 노드2의 최종 부모가 다르다면 같은 그룹이 아니므로 하나의 그룹으로 결합한다. 이 때 root는 뒤쪽 노드가 된다.
    static void union(int p1, int p2) {
        if (p1 != p2) {
            arr[p1]= p2;
        }
    }

    // Edge 객체를 클래스로 정의. 2개의 노드와 1개의 비용을 변수로 갖는다.
    static class Edge implements Comparable<Edge>{
        int start, end, value;

        Edge(int s, int e, int v) {
            this.start = s;
            this.end = e;
            this.value = v;
        }

        @Override
        public int compareTo(Edge o) { return this.value - o.value; }
        @Override
        public String toString() { return "Edge [start=" + start + ", end=" + end + ", value=" + value + "]\n"; }
    }
}

3번 코드에서 ArrayList<Edge> mst와 PriorityQueue<Edge> pq를 모두 TreeSet<Edge>로 바꿨다.

 

참고 : 2 ~ 5번 코드에 보면 시간 측정 로직이 들어갔는데, 시작시간이 내가 지정한 시점에서 시작되지 않고, new Scanner(System.in);나 new BufferedReader(new InputStreamReader(System.in)); 이쪽에서 System.in이 시작된 때의 시간을 가져와서 제대로 된 시간 측정이 되지 않는다.

 

 

 

읽어보면 좋은 자료

자료구조 힙(heap)

 

 

Reference

ko.wikipedia.org/wiki/신장_부분_그래프

 

신장 부분 그래프 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 그래프의 신장 부분 나무 그래프 왼쪽의 그래프는 오른쪽과 같이 총 8개의 신장 부분 나무 그래프들을 갖는다. 그래프 이론에서, 신장 부분 그래프(身長部分grap

ko.wikipedia.org

ko.wikipedia.org/wiki/크러스컬_알고리즘

 

크러스컬 알고리즘 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 컴퓨터 과학에서, 크러스컬 알고리즘(영어: Kruskal’s algorithm)은 최소 비용 신장 부분 그래프를 찾는 알고리즘이다. 변의 개수를 E {\displaystyle E} , 꼭짓점의 개수�

ko.wikipedia.org

ko.wikipedia.org/wiki/프림_알고리즘

 

프림 알고리즘 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 프림 알고리즘(Prim's algorithm)은 가중치가 있는 연결된 무향 그래프의 모든 꼭짓점을 포함하면서 각 변의 비용의 합이 최소가 되는 부분 그래프인 트리, 즉 최소

ko.wikipedia.org

 

Tag.

bufferedreader, buffered reader, Buffered Reader, 버퍼드리더, 버퍼드 리더, inputstreamreader, input stream reader, inputstream reader, Input Stream Reader, 인풋스트림리더, 인풋 스트림 리더, 인풋스트림 리더, 크러스컬 알고리즘, 크루스칼 알고리즘, kruskal algorithm, kruskal's algorithm, kruskal's, 프림 알고리즘, prim's algorithm, prim's, prim algorithm, Prim Algorithm, Prim's Algorithm

 

'개발자 > Algorithm' 카테고리의 다른 글

Kruskal & Prim 비교  (0) 2020.09.22
Prim's Algorithm  (0) 2020.09.21
05. 장기판 포 말잡기  (0) 2020.08.30
04. 토끼 잡기  (0) 2020.08.28
02. 수열 A와 수열 B가 같은지 확인하기  (0) 2020.08.24

그냥 주단위로 쓸까 싶다...

자바 스프링의 세계는 정말 알면 알수록 더 복잡하고 미궁으로 가는 것 같다.

하나씩 새로 정리하는게 생각보다 오래걸린다... 자바스크립트랑 노드도 해야하는데...

알고리즘도 풀어야 하는데... 알고리즘을 신입 뽑는데 시험 본다는게 참 그렇다... 면접에서 요구를 하니까 준비 해야겠지만 신입한테 알고리즘 능력까지 요구하는건 너무한거 아닌가.. 'ㅅ'

'개발자 > TIL' 카테고리의 다른 글

TIL 20.09.21 ~ 20.09.25  (0) 2020.09.25
TIL 20.09.14 ~ 20.09.20  (0) 2020.09.20
TIL 20.09.04 ~ 20.09.07  (0) 2020.09.08
TIL 20.09.03  (0) 2020.09.04
TIL 20.09.02  (0) 2020.09.03

'잡다구리 정보' 카테고리의 다른 글

아이클라우드 자모분리  (0) 2020.09.27
맥 Parallels ENG 언어 삭제하기  (0) 2020.08.25
Win PE 포맷  (0) 2020.07.30

+ Recent posts