[내일배움캠프] 260212 TIL

Bambu·2026년 2월 12일

내배캠 TIL

목록 보기
36/52

1. 알고리즘 문제 풀이

1) 모닝스터디

가. 가장 큰 수

lv 2. 가장 큰 수

기존 코드

func solution(_ numbers:[Int]) -> String {
    // [6, 10, 2]일 때, "610" > "106" 중 큰 수를 비교해 큰 것대로 나열
    let result = numbers.sorted(by: { 
        ($0.description + $1.description) > ($1.description + $0.description) 
    }).map(String.init).joined()
    
    // 주어진 numbers가 [0, 0, 0, 0]이라면 결과가 "0000" -> "0"으로 변환
    return Int(result) == 0 ? "0" : result
}

변경 코드

func solution(_ numbers:[Int]) -> String {
    // sorted 내에서 문자열로 변환하여 비교하지 말고 애초에 문자열로 변환해서 sorted에서 비교
    let result = numbers.map(String.init).sorted(by: {
        ($0 + $1) > ($1 + $0)
    })

    // prefix가 0을 포함하는지 확인
    return result.joined().hasPrefix("0") ? "0" : result.joined()
}

-> Int 타입인 numbers의 요소를 Int -> String, String -> Int 타입으로의 변환이 많이 일어나서 타입 변환의 횟수를 줄이는 방향으로 개선

나. 프로세스

lv 2. 프로세스

기존 코드

func solution(_ priorities:[Int], _ location:Int) -> Int {
    var queue = priorities.enumerated().map { (location: $0, priority: $1) }
    var turn: [(location: Int, priority: Int)] = []
    
    while !queue.isEmpty {
        let run = queue.removeFirst()
        
        if queue.contains(where: { $0.priority > run.priority }) { // 큐에 우선순위 큰 게 남아있는지?
            // 있을 경우 - 큐에 재삽입
            queue.append(run)
        } else {
            // 없을 경우 - turn 배열에 삽입
            turn.append(run)
        }
    }
    return (turn.firstIndex(where: { $0.location == location }) ?? 0) + 1
}

변경 코드

func solution(_ priorities:[Int], _ location:Int) -> Int {
    var queue = priorities.enumerated().map { (location: $0, priority: $1) }
    var count = 0
    
    while !queue.isEmpty {
        let run = queue.removeFirst()
        let maxPriority = queue.map { $0.priority }.max() ?? 0 // 프로세스 대기열 큐에서 가장 큰 우선순위
        
        if run.priority < maxPriority { // 큐에 우선순위 큰 게 남아있는지? -- contains가 아닌 비교 연산자로 비교하여 더 빠르게 연산 가능
            // 있을 경우 - 큐에 재삽입
            queue.append(run)
        } else {
            // 없을 경우 - while문 탈출
            count += 1
            if run.location == location { break }
        }
    }
    return count
}

-> while문 내에서 contains로 조건문을 판단하였는데, maxPriority 상수에 가장 큰 우선순위를 저장하여 비교문으로 조건을 판단하는 것으로 개선

-> 기존에는 while문 탈출 조건이 없음 - else문에 break를 추가하여 조건 충족시 바로 반복문을 탈출하여 쓸데없는 반복을 줄임

💡count 변수를 사용하지 않은 풀이

func solution(_ priorities:[Int], _ location:Int) -> Int {
    var queue = priorities.enumerated().map { (location: $0, priority: $1) }
    
    while !queue.isEmpty {
        let run = queue.removeFirst()
        let maxPriority = queue.map { $0.priority }.max() ?? 0 // 프로세스 대기열 큐에서 가장 큰 우선순위
        
        if run.priority < maxPriority { // 큐에 우선순위 큰 게 남아있는지?
            // 있을 경우 - 큐에 재삽입
            queue.append(run)
        } else {
            // 없을 경우 - while문 탈출
            // 기존 프로세스 수 - 대기중인 프로세스 수 = 현재 꺼낸 프로세스의 실행 순서
                return priorities.count - queue.count
            }
        }
    }
    return 0
}

스터디에서 소개 받은 풀이.
count 변수를 사용하는 대신 priorities.count - queue.count를 계산하여 바로 반환 가능하다.

2) 개인 공부

가. 공백으로 구분하기 2

lv 0. 공백으로 구분하기 2

'공백으로 구분하기 1' 문제에서는 주어지는 문자열에 공백이 한 칸으로 제한되어 있었다. (eg. "i love you")

그래서 components(separatedBy: .whitespaces)로 풀어도 문제가 없었다.

func solution(_ my_string: String) -> [String] {
	return my_string.components(separatedBy: .whitespaces)
}
// "i love you" -> ["i", "love", "you]

하지만 '공백으로 구분하기 2'에서는 주어지는 문자열에 공백이 여러 칸이라 1과 동일하게 components를 활용하여 풀면 의도한 정답이 나오지 않았다.

func solution(_ my_string: String) -> [String] {
	return my_string.components(separatedBy: .whitespaces)
}
// "i   love   you" -> ["i", "", "", "love", "", "you"]

반면 split을 사용하면 빈 문자열 없이 반환값을 얻을 수 있었다.

func solution(_ my_string: String) -> [String] {
	return my_string.split(separator: " ").map (String.init)
} // "i   love  you" -> ["i", "love", "you"]

둘의 차이점은 무엇일까?

단순히 두 메서드의 차이점을 본다면 꽤나 많을 것이라 생각된다. 대표적으로 반환 타입이 다르다. (split[Self.Subsequences] 타입, components[String] 타입이다.)

이 풀이에서의 차이점은 '빈 문자열의 생략 여부'라고 생각한다.

split에는 omittingEmptySubsequences라는 옵션이 존재한다.

이 옵션은 '빈 문자열의 생략 여부'를 나타내는 옵션으로, 기본적으로 true 값을 갖고 있다.

이 옵션을 false로 둬보자.

func solution(_ my_string: String) -> [String] {
	return my_string.split(separator: " ", omittingEmptySubsequences: false).map(String.init)
}
// "i   love   you" -> ["i", "", "", "love", "", "you"]

이번에는 components의 실행 결과와 동일한 값이 반환되었다!

즉, split 내부적으로는 components과 거의 유사한 방식으로 문자열을 구분하여 split에서도 빈 문자열이 생성되지만, 기본적으로 빈 문자열을 생략하기에 애초에 생성하지 않는 것으로 보이는 것이다.

components 메서드에는 빈 문자열을 생략하는 옵션이 없으므로 문제의 의도에 맞게 구현하려면 split을 사용하는 것이 더 적절할 것으로 보인다.

2. 실습 - 날씨앱 만들기

1) UICollectionView backgroundColor 바꾸기

날씨앱 만들기 실습을 하면서 기존 테이블뷰로 구현된 부분을 컬렉션뷰로 구현해보았다.

데이터는 잘 나오는데 배경 색상에 문제가 생겼다.

테이블 위에 하얀 건 뭐지???

UI 디버깅을 해보니 UICollectionViewListLayoutSectionBackgroundColorDecorationView라는 길다란 녀석이란다.

ListLayout의 SectionBackground라면, 리스트 섹션을 만들 때 configuration에서 바꿔줄 수 있지 않을까?

func makeCompositionalLayout() -> UIcollectionViewCompositionalLayout {
	return UICollectionViewCompositionalLayout { sectionIndex, environment in
    	// appearnce 변경 .grouped -> .plain => 리스트 위에 흰 여백 없애기 위함
    	var configuration = UICollectionLayoutListConfiguration(appearance: .plain)
        // configuration.backgroundColor 변경
        configuration.backgroundColor = .black
        let section = NSCollectionLayoutSection.list(using: configuration, layoutEnvironment: environment)
        
        return section
    }
 }

-> 제시된 대로 배경 색상이 까맣게 잘 나온다!

💡 UICollectionView 자체의 backgroundColor 바꾸기
처음에는 컬렉션뷰 자체의 배경 색상을 바꿔야한다고 생각해서 찾아보았는데, decorationView를 만들어서 올리라는 내용이 대다수였다..

아무리 생각해도 기본 background 색상 설정이 있지 않을까 해서 찾아보았더니 backgroundView 속성을 찾았다.

UICollectionView.backgroundView를 접근할 수 있지만, 기본적으로 backgroundViewnil값이다.

그래서 backgroundView를 사용하려면 뷰를 만들어 넣어주어야 한다.

	collectionView.backgroundView = UIView()
    collectionView.backgroundView?.backgroundColor = .black

위 처럼 사용하면 데코레이션 뷰를 올리지 않고도 컬렉션 뷰 자체의 배경 색상을 변경해줄 수 있다.

profile
안녕하세요, iOS 개발을 공부하고 있는 Bambu입니다. (프로필: Swifticons)

0개의 댓글