Problem


주어진 nums 에서 val 원소를 지우는 문제입니다.


nums 배열 값을 직접 변화시키면 되며, 리턴값은 배열의 길이입니다.




Solution

단순하게 val 값이 아닐때에만 값을 넣어주고 인덱스를 증가시켜주면 됩니다.




Java Code

class Solution {
    public int removeElement(int[] nums, int val) {
        int index = 0;
        
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != val) {
                nums[index++] = nums[i];
            }
        }
        
        return index;
    }
}


Problem


배열에 0 이 존재하면 0 인덱스부터 오른쪽 쉬프트 합니다.


모든 쉬프트를 하고난 뒤의 배열의 모습을 구하면 됩니다.



Solution

투 포인터로 풀 수 있습니다.


0 의 갯수를 미리 카운트합니다.


추가 공간을 사용하지 않고 배열을 덮어써야 하기 때문에 마지막 인덱스부터 시작합니다.


zeroCount 는 쉬프트 횟수와 같기 때문에 arr[i + zeroCount] = arr[i] 로 값을 갱신해줍니다.


단 i + zeroCount 인덱스가 배열의 길이보다 작아야 합니다.


만약 arr[i] 가 0 이라면 그 즉시 arr[i + zeroCount] 에 0 을 넣고 인덱스를 하나 당겨줍니다.


시간복잡도는 O(n) 입니다.



Java Code

class Solution {
    public void duplicateZeros(int[] arr) {
        int zeroCount = 0;
        
        for (int a : arr) {
            if (a == 0) zeroCount++;
        }
        
        int n = arr.length;
        
        for (int i = n-1; i >= 0; i--) {
            int j = i + zeroCount;
            
            if (arr[i] == 0) {
                if (j < n) arr[j] = 0;
                zeroCount--;
                j--;
            }
            
            if (j < n) arr[j] = arr[i];
        }
    }
}


Git Directory 이름 변경

Algorithm repo 에서 디렉토리 이름을 변경할 필요가 생겼습니다.


별거 아닌 이유지만 boj -> BOJ 로 leetcode -> LeetCode 로 변경하고 싶었습니다.


우선 Github 에서 디렉토리 이름을 변경하는 방법을 검색했습니다.


  1. 이름을 변경하려는 디렉토리의 상위로 이동
  2. git mv oldName newName 입력
  3. git add git commit 하면 반영



그런데 막상 사용하려니 문제점이 하나 있었습니다.


바로 대소문자를 구분하지 않는다 는 점


$ git mv boj BOJ
fatal: bad source, source=boj, destination=BOJ



해결법은 단순했습니다.


다른 이름으로 변경 후 다시 변경하면 됩니다.


$ git mv boj baekjoon
$ git mv baekjoon BOJ
$ git add .
$ git commit -m "Rename boj to BOJ"

[master 9f7487f] Rename boj to BOJ
 68 files changed, 0 insertions(+), 0 deletions(-)
 rename {boj => BOJ}/1005.java (100%)
 rename {boj => BOJ}/1057.java (100%)
 rename {boj => BOJ}/1074.java (100%)
 .
 .
 .
 rename {boj => BOJ}/image/5397_4.PNG (100%)


'공부 > Git' 카테고리의 다른 글

Git Merge (feat. Github)  (0) 2022.06.20
Gitmoji (for Git Commit Convention)  (0) 2022.06.18

Map

KeyValue 형태로 데이터를 저장하는 자료구조



HashMap

선언

일반적으로 변수 부분은 인터페이스로 선언하는게 확장에 유리하다.


Map<String, String> map = new HashMap<>();



데이터 삽입

V put(K key, V value) 로 값을 넣을 수 있다.


put(k, v) 을 했을 때 이미 키값이 존재한다면 데이터를 덮어쓴다.


putIfAbsent 을 이용하면 Map 에 Key 값이 없을 때에만 데이터를 넣을 수 있다.


map.put("animal", "cat");           // {animal=cat}
map.put("food", "pizza");           // {animal=cat, food=pizza}


// 이미 "animal" 키값이 있기 때문에 "dog" 로 갱신되지 않음
map.putIfAbsent("animal", "dog");   // {animal=cat, food=pizza}
map.putIfAbsent("animal2", "dog");  // {animal=cat, animal2=dog, food=pizza}



데이터 가져오기

V get(Object key) 로 value 값을 가져올 수 있다.


V getOrDefault(Object key, V defaultValue) 을 사용하면 key 값이 없을 때 null 대신 설정된 값을 리턴한다.


map.get("animal");  // "cat"

map.getOrDefault("food", "chicken");     // "pizza"
map.getOrDefault("food2", "chicken");    // "chicken"



데이터 삭제

V remove(Object key) 로 데이터를 삭제 할 수 있다.


map.remove("animal2");



데이터 확인

boolean containsKey(Object key) 또는 boolean containsValue(Object value) 로 key 나 value 값이 존재하는 지 확인할 수 있다.


map.containsKey("food");    // true
map.containsValue("dog");   // false



크기(길이) 확인

map.size();



Key, Value 묶음 가져오기

Set<K> keySet() 은 Key들로 이루어진 Set 자료구조를 리턴한다.


Collection<V> values() 은 Value 들로 이루어진 Collection 을 리턴한다.


// map: {animal=cat, food=pizza}

map.keySet();   // [animal, food]
map.values();   // [cat, pizza]



Map 순회

forEach 를 사용해서 Map 을 순회할 수 있다.


람다식을 이용하면 좀더 간단하게 나타낼 수 있으며 코드가 한줄이라면 중괄호 { } 를 생략할 수 있다.


// 출력
// animal: cat
// food: pizza
map.forEach((k, v) -> {
    System.out.println(k + ": " + v);
});

// 한 줄이면 중괄호 { } 생략 가능
map.forEach((k, v) -> System.out.println(k + ": " + v));

// 람다식 안쓰고 for 문으로 구현. forEach 도 내부적으로는 이렇게 구현되어있음
for (Map.Entry entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}



Compute

compute 를 사용해 원하는 로직을 실행하고 데이터를 넣을 수 있다.


만약 Key 가 없으면 새로운 데이터를 넣어주고 Key 값이 있으면 데이터를 갱신해준다.


만약 기존에 없는 데이터로 compute 연산을 하게 될 때 value 값은 null 이 된다.


map.compute("animal", (k, v) -> {
    System.out.println(k + "'s value is " + v  + " -> lion");      // animal's value is cat -> lion
return "lion"; }); // map: {animal=lion, food=pizza}



computeIfAbsent 와 computeIfPresent 는 조건을 걸어서 compute 연산을 실행한다.


computeIfAbsent 는 Key 가 없을 때만 실행되기 때문에 람다식으로도 key 값 하나 밖에 받지 않는다.


computeIfPresent 는 Key 값이 존재할 때만 실행된다.


만약 Key 가 없거나 있어서 조건이 일치하지 않으면 로직이 아예 실행되지 않는다.


map.computeIfAbsent("fruit", (k) -> {
    System.out.println("New value of " + k + " is apple");      // New value of fruit is apple
    return "apple";
});
// map: {fruit=apple, animal=lion, food=pizza}


map.computeIfPresent("animal", (k, v) -> {
    System.out.println(k + "'s value is " + v +  " -> tiger");      // animal's value is lion -> tiger
    return "tiger";
});
// map: {fruit=apple, animal=tiger, food=pizza}




LinkedHashMap

HashMap 은 hashcode 를 사용하기 때문에 순서가 일정하지 않다.


LinkedHashMap 은 내부를 Double-Linked List 로 구성하여 HashMap 의 순서를 유지한다.


HashMap 에서 상속받기 때문에 HashMap 의 모든 메소드를 사용할 수 있다.



순서 유지

데이터는 먼저 들어간 데이터가 무조건 앞에 위치하게 된다.


forEach 문에서도 동일하다.


Map<String, String> map = new LinkedHashMap<>();

map.put("animal", "cat");
map.put("fruit", "apple");

System.out.println(map);        // {animal=cat, fruit=apple}

map.put("animal", "dog");       
System.out.println(map);        // {animal=dog, fruit=apple}

map.forEach((k, v) -> System.out.print(k + ": " + v + ", "));       // animal: dog, fruit: apple, 



접근 빈도에 따른 순서 변경

LinkedHashMap 은 생성자 파라미터로 accessOrder 라는 값을 받는다.


accessOrder 는 기본값이 false 인데, 만약 true 로 설정한다면 LinkedHashMap 의 접근 빈도에 따라서 순서가 바뀌게 된다.


public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}



아래 코드는 위와 완전히 동일하지만 accessOrder 만 true 로 생성한 LinkedHashMap 이다.


"animal" 키를 먼저 넣었지만 중간에 put 메소드로 "animal" 키에 접근을 했기 때문에 순서가 바뀌었다.


put 뿐만 아니라 get 이나 compute 에도 동일하게 동작한다 (containsKey 는 바뀌지 않음)


Map<String, String> map = new LinkedHashMap<>(16, 0.75f, true); map.put("animal", "cat"); map.put("fruit", "apple"); System.out.println(map); // {animal=cat, fruit=apple} map.put("animal", "dog"); System.out.println(map); // {fruit=apple, animal=dog} map.forEach((k, v) -> System.out.print(k + ": " + v + ", ")); // fruit: apple, animal: dog,



accessOrder 를 이용하면 가장 첫번째에 존재하는, 즉 가장 사용되지 않은 Entry 를 알 수 있다.


Map.Entry leastUsedEntry = map.entrySet().iterator().next();
int leastUsedKey = map.keySet().iterator().next();
int leastUsedValue = map.values().iterator().next();

// 아래처럼 구할 수도 있다.
leastUsedKey = leastUsedEntry.getKey();
leastUsedValue = leastUsedEntry.getValue();


Problem


Singly Linked List 가 주어지면 리스트의 가장 중간부터 끝까지를 리턴하면 됩니다.


만약 List 의 길이가 짝수라면 뒤의 절반을 리턴합니다.




Solution

간단하게 two pointer 로 해결할 수 있습니다.


slow 는 한번, fast 는 두번 씩 전진하면 fast 가 끝에 도달했을 때 slow 의 위치가 List 의 중간입니다.




Java Code

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        
        return slow;
    }
}


Problem

07-counting-elements


배열 안에 있는 임의의 수 n 에 대해서 n+1 가 배열 안에 존재한다면 그 갯수를 전부 카운트해서 응답하는 문제입니다.




Solution 1

HashSet 을 선언하여 배열 안의 값을 전부 담아둡니다.


다시 for 문을 돌며 num + 1 값이 Set 에 존재한다면 갯수를 세줍니다.




Java Code 1

class Solution {
    public int countElements(int[] arr) {
        Set<Integer> set = new HashSet<>();
        
        for (int num : arr) {
            set.add(num);
        }
        
        int sum = 0;
        
        for (int num : arr) {
            if (set.contains(num + 1)) {
                sum++;
            }
        }
        
        return sum;
    }
}





Solution 2

0 <= arr[i] <= 1000 조건을 이용하여 배열의 각 숫자들의 갯수를 담아두고 더해주는 방법도 있습니다.


counting[i+1] > 0 이라면i+1 인 숫자가 존재한다는 뜻이므로 i 의 갯수인 counting[i] 를 전부 더해줍니다.



Java Code 2

class Solution {
    public int countElements(int[] arr) {
        int[] counting = new int[1002];
        
        for (int num : arr) {
            counting[num]++;
        }
        
        int sum = 0;
        
        for (int i=0; i<=1000; i++) {
            if (counting[i+1] > 0) {
                sum += counting[i];
            }
        }
        
        return sum;
    }
}


Problem


pattern 과 str 이 주어질 때 두 문자열의 패턴이 일치해야 합니다.


예시로 abba 가 주어지면 str 은 아래의 규칙을 가지면 됩니다.


  1. 네 개의 단어로 이루어진다
  2. 맨 앞과 맨 끝의 단어가 똑같다 (a)
  3. 가운데의 두 단어가 똑같다 (b)




Solution

String[] words = str.split(" ") 코드로 문자들을 나누고 시작합니다.


pattern 의 길이와 words 배열의 길이는 같아야 합니다.


각 패턴 c 와 각 문자열 word 를 키값으로 하는 HashMap 을 각각 선언하여 담아둡니다.


만약 c 키값이 존재하는데 map 에 있는 map.get(c) 와 현재 위치의 word 가 일치하지 않는다면 패턴 불일치입니다.


pattern = "abba", str = "dog dog dog dog" 으로 주어지는 케이스가 있기 때문에 word 를 키값으로 한 HashMap 에서도 한번 더 검사해줍니다.




Java Code

class Solution {
    public boolean wordPattern(String pattern, String str) {
        Map<Character, String> charMap = new HashMap<>();
        Map<String, Character> strMap = new HashMap<>();
        String[] words = str.split(" ");
        
        if (pattern.length() != words.length) return false;
        
        for (int i=0; i<words.length; i++) {
            char c = pattern.charAt(i);
            String word = words[i];
            
            if (charMap.containsKey(c) && !charMap.get(c).equals(word)) {
                return false;
            }
            
            if (strMap.containsKey(word) && strMap.get(word) != c) {
                return false;
            }
            
            charMap.put(c, word);
            strMap.put(word, c);
        }
        
        return true;
    }
}


Problem


동전의 리스트가 주어지면 합이 amount 가 되는 경우의 수를 구하는 문제입니다.




Solution

dp 문제입니다.


dp[i] i 원까지 동전의 경우의 수 입니다.


dp[i] += dp[i - coin] 식은 j 원이 되기 위해서 추가되는 동전의 수라고 생각하며 됩니다.


예를 들어 j 가 10 이고 동전 1, 2, 5 를 갖고 있을 때 경우의 수는


  1. dp[9] + 1 원
  2. dp[8] + 2 원
  3. dp[5] + 5 원


이렇게 세가지 경우의 수가 됩니다.


dp[9], d[8], dp[5] 또한 각각 해당 인덱스까지의 모든 경우의 수 이기 때문에 전부 더해주면


dp[10] 의 총 경우의 수가 됩니다.




Java Code

class Solution {
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount+1];
        
        dp[0] = 1;
        
        for (int coin : coins) {
            for (int i = coin; i <= amount; i++) {
                dp[i] += dp[i - coin];
            }
        }
        
        return dp[amount];
    }
}


+ Recent posts