문제 링크 - https://school.programmers.co.kr/learn/courses/30/lessons/43165

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


문제 설명

주어진 정수 배열을 적절히 더하거나 뺀 값이 주어진 타겟 넘버와 같은 경우의 수를 출력

 

풀이법 구상

처음에는 이중 반복문을 돌면서 각 숫자를 음수로 바꾼 후의 총 합이 타겟과 같은지 판별하는 로직으로 구현했다가 실패

-> +4-1+2-1 과 같이 음수 사이에 양수가 존재하는 경우는 고려할 수 없음

 

각 숫자가 양수이거나 음수인 경우만 고려하면 되고 그러면 2^N의 시간 복잡도가 발생하는데, N의 최대값은 20으로 약 1,050,000이 됨

각 숫자가 양수이거나 음수가 되는 경우를 모두 고려하려면 각 숫자에 +,-를 붙인 노드로 이루어진 트리로 시각화할 수 있음

각 노드를 탐색하며 더한 합계값을 타겟 넘버와 비교하면 결과값 도출 가능

탐색에는 BFS/DFS를 모두 사용할 수 있으나 제출 코드는 BFS사용

 

 

코드 구현 (스위프트 사용)

import Foundation

func solution(_ numbers:[Int], _ target:Int) -> Int {
    var result = 0
    var sum_arr = [0]   // 반복문 시작을 위해 0 하나 추가
    
    for n in numbers {
        var temp: [Int] = []
        for s in sum_arr {
            temp.append(s+n)
            temp.append(s-n)
        }
        sum_arr = temp
    }
    
    for s in sum_arr { result += (s == target) ? 1 : 0 }
    
    return result
}

 

문제 링크 - https://school.programmers.co.kr/learn/courses/15008/lessons/121683?language=python3

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


문제 설명

알파벳 소문자로만 이루어진 문자열이 주어짐

해당 문자열 중 같지만 바로 옆에 붙어있지 않은 알파벳이 존재하는 경우 '외톨이 알파벳'으로 간주

외톨이 알파벳으로 이루어지고 알파벳 순으로 정렬된 새로운 문자열을 출력

 

풀이법 구상

반복문으로 각 자리의 글자를 대상으로 다른 똑같은 글자가 존재하는지 확인

단, 기준 글자의 바로 옆에 똑같은 다른 글자가 존재하는 경우 기준 인덱스 += 1 -> 그냥 바로 다음 글자로 넘어간다는 의미

기준 인덱스로부터 2자리 이상 떨어진 경우 외톨이 알파벳으로 간주

 

코드 구현 (파이썬 사용)

def solution(input_string):
    answer = ''

    for p in range(0,len(input_string)):
        if input_string[p] in answer: continue
        for q in range(p+1,len(input_string)):
            if input_string[p] == input_string[q]:
                if ((q-p) == 1): break
                elif ((q-p) > 1):
                    answer += input_string[p]
                    break
                
    if len(answer) == 0 : return 'N'
    else: return ''.join(sorted(answer))

 

문제 링크 - https://school.programmers.co.kr/learn/courses/30/lessons/120875

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


문제 설명

x,y 좌표값으로 구성된 점 4개의 정보가 2차원 정수형 배열로 주어짐

4개의 점들이 조합되어 만들 수 있는 직선 중 서로가 평행한 경우가 있다면 결과값으로 1을, 아니라면 0을 반환해야함

 

 

풀이법 구상

점의 정보는 4개로 고정된 만큼만 주어짐

직선의 x,y 좌표간 차이값의 절대값이 같으면 평행한 것으로 간주하고 풀었다가 틀림 -> 방향 고려 안됨

방향을 포함하는 기울기값(y차이 / x차이)이 같으면 평행

 

주어진 점 4개로 서로 다른 직선을 구성할 수 있는 경우의 수를 아래와 같음

(0번째 점, 1번째 점)

(0번째 점, 2번째 점)

(0번째 점, 3번째 점)

(1번째 점, 2번째 점)

(1번째 점, 3번째 점)

(2번째 점, 3번째 점)

 

서로 다른 직선 6가지 경우의 각 기울기 값을 구하여 반복문을 돌며 같은 값이 존재하는지 판단 -> 틀림(12번 케이스부터 실패)

 

위와 같은 로직으로 기울기들을 판별할 경우 불가능한 경우까지 비교하게됨

ex. (0번째 점, 1번째 점)으로 이루어진 직선과 (0번째 점, 2번째 점)으로 이루어진 직선 간의 기울기를 비교

 

즉 주어진 점 4개로 '서로 다른 2개의 직선'을 구성하여 비교하기 위한 경우는 아래와 같음

(0번째 점, 1번째 점) VS (2번째 점, 3번째 점)

(0번째 점, 2번째 점) VS (1번째 점, 3번째 점)

(0번째 점, 3번째 점) VS (1번째 점, 2번째 점)

 

 

코드 구현 (스위프트 사용)

func solution(_ dots:[[Int]]) -> Int {
    if (getInc(dots[0], dots[1]) == getInc(dots[2], dots[3])) {
        return 1
    } else if  (getInc(dots[0], dots[2]) == getInc(dots[1], dots[3])) {
        return 1
    } else if  (getInc(dots[0], dots[3]) == getInc(dots[1], dots[2])) {
        return 1
    } else {
        return 0
    }
}

func getInc(_ m:[Int], _ n: [Int]) -> Double {
    let diffX = max(m[0], n[0]) - min(m[0], n[0])
    let diffY = max(m[1], n[1]) - min(m[1], n[1])
    let inc = ((n[0]-m[0])*(n[1]-m[1]) < 0) ? -1.0 : 1.0
    return inc*(Double(diffY)/Double(diffX))
}

 

문제 링크 - https://www.acmicpc.net/problem/2012

 

2012번: 등수 매기기

첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 500,000) 둘째 줄부터 N개의 줄에 걸쳐 각 사람의 예상 등수가 순서대로 주어진다. 예상 등수는 500,000 이하의 자연수이다.

www.acmicpc.net


 

문제 설명

n명에게 각각 예상 등수값을 입력받고, 그 값들을 토대로 불만족 지수가 최소로 나오도록 구성하여 불만족 지수의 최소값을 출력

불만족 지수란 예상 등수가 1등이고, 실제 순위 구성 후 등수가 3등인 경우 (|1-3|)으로 2의 값이 됨

(n의 범위는 1이상 500,000이하)

 

풀이법 구상

예상 순위보다 실제 순위가 높든 낮든 무관하게 그 차이값만큼이 불만족 수치가 됨

최대한 예상 순위가 실제 순위가 되도록 해야하므로 정렬 후 각 예상 순위값을 돌며 실제 순위와의 차이값을 구해야함

정렬 + 반복문이 필요함

-> 스위프트 기본 정렬 메소드의 시간복잡도 - O(n log n)

-> 반복문 시간 복잡도 - O(N)

-> 동시에 일어나지 않고 정렬 후 반복문 돌게됨

 

코드 구현 (스위프트 사용)

import Foundation

func solution(_ arr: [Int]) -> Void {
    var sum = 0
    let temp = arr.sorted()
    for i in 1...arr.count { sum += abs(i - temp[i-1]) }
    print(sum)
}

var arr: [Int] = []
let cnt = Int(readLine()!)!
for _ in 0..<cnt { arr.append(Int(readLine()!)!) }

solution(arr)

문제 링크 - https://school.programmers.co.kr/learn/courses/30/lessons/131533

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


 

문제 설명

판매중인 상품 정보(ID, 상품코드, 가격)를 담은 PRODUCT 테이블과 오프라인 판매 정보를 담은 OFFLINE_SALE 테이블이 주어짐

그 중 상품 코드 당 총 판매금액 (판매한 개수*판매액)을 출력 

(총 판매금액을 기준으로 내림차순 정렬, 같다면 상품코드를 기준으로 오름차순 정렬)

 

풀이법 구상

ID값을 기준으로 Join을 이용하여 PRODUCT 테이블 내 가격정보와 OFFLINE_SALE 테이블 내 판매량 정보를 곱한 SALES 열 생성

 

코드 구현 (MySQL 사용)

SELECT PRODUCT_CODE, SUM(PRICE*SALES_AMOUNT) AS SALES
    FROM PRODUCT JOIN OFFLINE_SALE
    ON PRODUCT.PRODUCT_ID = OFFLINE_SALE.PRODUCT_ID
    GROUP BY PRODUCT_CODE
    ORDER BY SALES DESC, PRODUCT_CODE ASC

 

+) 삽질

처음에 이렇게 작성했다가 중복 행이 있는 줄 알고 DISTINCT 썼는데 틀렸음

 

혹시나하고 OFFLINE_SALE_ID 같이 출력해봤더니 중복행이 아니었음

-> 각각 다른 판매건수이므로 상품코드별로 다 합쳐야 총 판매금액을 구할 수 있어서 GROUP BY 사용

 

문제 링크 - https://www.acmicpc.net/problem/2751

 

2751번: 수 정렬하기 2

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 수가 주어진다. 이 수는 절댓값이 1,000,000보다 작거나 같은 정수이다. 수는 중복되지 않는다.

www.acmicpc.net


문제 설명

랜덤한 숫자 n개가 입력되면 해당 숫자들을 오름차순으로 정렬하여 한 줄에 하나씩 출력

- 입력되는 숫자의 개수는 1개 이상 1,000,000개 이하

- 들어오는 숫자는 절대값이 1,000,000이하인 정수

 

풀이법 구상

스위프트에 있는 sort 메소드 사용하기

-> 기본 정렬 메소드의 시간 복잡도는 O(n log n) 이므로 시간 제한에 문제되지않음

 

코드 구현 (스위프트 사용)

import Foundation

func solution(_ arr: [Int]) -> Void {
    let result = arr.sorted()
    for n in result { print(n) }
}

var arr: [Int] = []
let cnt = Int(readLine()!)!
for _ in 0..<cnt { arr.append(Int(readLine()!)!) }

solution(arr)

 

문제 링크 - https://www.acmicpc.net/problem/2581

 

2581번: 소수

M이상 N이하의 자연수 중 소수인 것을 모두 찾아 첫째 줄에 그 합을, 둘째 줄에 그 중 최솟값을 출력한다.  단, M이상 N이하의 자연수 중 소수가 없을 경우는 첫째 줄에 -1을 출력한다.

www.acmicpc.net


문제 설명

주어진 두 수 m, n을 포함하여 그 사이에 존재하는 소수의 합계값과 그 소수들 중 최소값을 출력 (소수가 없는 경우 -1 출력)

 

풀이법 구상

m이상 n이하를 범위로 하는 반복문을 돌면서 각 수가 소수인지 판별

 

코드 구현 (스위프트 사용)

import Foundation

func solution(_ m:Int, _ n:Int) -> Void {
    var sum = 0
    var min = -1

    for p in m...n {
        if isDecimal(p) {
            sum += p
            if (min == -1 || min > p) { min = p }
        }
    }
    if (sum == 0) { print(-1) }
    else {
        print(sum)
        print(min)
    }

}

func isDecimal(_ n:Int) -> Bool {
    if (n == 1) { return false }
    for i in 2..<n { if (n%i == 0) { return false } }
    return true
}

let m = Int(readLine()!)!  
let n = Int(readLine()!)!  

solution(m,n)

 

문제 링크 - https://school.programmers.co.kr/learn/courses/30/lessons/86051?language=swift

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


문제 설명

주어진 정수 배열 중 0에서 9 사이에 빠진 숫자들을 합한 값을 반환

 

풀이법 구상

0 이상 9 이하의 범위를 정하여 각 숫자마자 numbers에 포함되어 있는지를 파악

 

코드 구현 (스위프트 사용)

import Foundation

func solution(_ numbers:[Int]) -> Int {
    var sum = 0
    for n in 0...9 { sum += numbers.contains(n) ? 0 : n }
    return sum
}

구현 결과

 

구현 코드

https://github.com/ryr0121/UIKitPractice/tree/main/StackViewPractice

 

사용한 라이브러리

Snapkit, Then

 

구현 과정

구현에 참고한 이미지

1. 구현 영역 분리

- 모서리가 둥근 전체 컨테이너 역할의 UIView

- 좌측 상단의 도시명, 현재 기온을 포함한 UIStackView (수직 방향)

- 우측 상단의 구름 이미지, 날씨, 최고/최저 기온을 포함한 UIStackView (수직 방향)

- 하단의 각 시간대별 날씨를 포함한 UIStackView (수평 방향)

 

 

2. 사용될 UI 컴포넌트 정의

2-1. 모서리가 둥근 전체 컨테이너 역할의 UIView 정의

lazy var backView = UIView().then {
    $0.backgroundColor = .gray
    $0.layer.cornerRadius = 20
    $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapWeatherView)))
}

 

2-2. 좌측 상단의 도시명, 현재 기온을 포함한 수직 UIStackView

 

lazy var leftTitleLabel1 = getLabelView(titleStr: "서울특별시", fontSize: 20.0, fontWeight: .bold, fontColor: .white)
lazy var leftTitleLabel2 = getLabelView(titleStr: "9°", fontSize: 40.0, fontWeight: .medium, fontColor: .white)

lazy var leftStackView = UIStackView().then {
    $0.backgroundColor = .green
    
    // 스택 요소로 포함할 subView들을 선언
    let stackView = UIStackView(arrangedSubviews: [leftTitleLabel1, leftTitleLabel2])
    
    $0.axis = .vertical		// 수직 방향으로 스택을 구성
    $0.distribution = .fill	// 요소들이 스택을 채울 수 있게 분포되도록 구성
    $0.alignment = .leading	// 왼쪽 끝에 붙어서 요소가 나열되도록 구성
    $0.spacing = 0		// 요소 간의 간격 0
}

 
// helper methods - 컨테이너 뷰를 가진 UILabel 컴포넌트를 반환
func getLabelView(titleStr: String, fontSize: Double, fontWeight: UIFont.Weight, fontColor: UIColor) -> UIView {
    let titleLabel = UILabel().then {
        $0.text = titleStr
        $0.font = .systemFont(ofSize: fontSize, weight: fontWeight)
        $0.textColor = fontColor
    }
    let containerView = UIView().then {
        $0.backgroundColor = .systemPink
        $0.backgroundColor = .clear
    }

    containerView.addSubview(titleLabel)
    titleLabel.snp.makeConstraints { make in
        make.top.bottom.leading.trailing.equalToSuperview()
    }
    return containerView
}

 

2-3. 우측 상단의 구름 이미지, 날씨, 최고/최저 기온을 포함한 수직 UIStackView

let cloudImgView = UIImageView(image: UIImage(systemName: "cloud.fill")).then {
    $0.tintColor = .white
}

// getLabelView는 상단 helper method 참조
lazy var rightTitleLabel1 = getLabelView(titleStr: "대체로 흐림", fontSize: 18.0, fontWeight: .medium, fontColor: .white)
lazy var rightTitleLabel2 = getLabelView(titleStr: "최고: 21° 최저 7°", fontSize: 18.0, fontWeight: .medium, fontColor: .white)
    
lazy var rightStackView = UIStackView().then {
    $0.backgroundColor = .blue
    let stackView = UIStackView(arrangedSubviews: [cloudImgView, rightTitleLabel1, rightTitleLabel2])
    $0.axis = .vertical
    $0.distribution = .fill
    $0.alignment = .trailing	// 요소들이 오른쪽 끝에 붙어 나열될 수 있도록 구성
    $0.spacing = 3
}

 

좌우측 상단에 스택뷰를 추가한 후

 

2-4. - 하단의 각 시간대별 날씨를 포함한 수평 UIStackView 

let weatherWithTimeList = [
    ["오전 8시", "10°"],
    ["오전 9시", "12°"],
    ["오전 10시", "15°"],
    ["오전 11시", "17°"],
    ["오후 12시", "19°"],
    ["오후 1시", "20°"],
]

lazy var weatherViews: [UIView] = []

// helper method - 시간 및 기온 정보 배열을 토대로 하단 수평 스택뷰의 요소로 쓰이는 UIView 반환
// viewDidLoad 내에서 호출하여 weatherViews 배열 구성
func configureWeatherOfTime() {
    weatherWithTimeList.map { info in
        weatherViews.append(getWeatherViewOfTime(timeStr: info[0], temper: info[1]))
    }
}

lazy var bottomStackView = UIStackView().then {
    let stackView = UIStackView(arrangedSubviews: weatherViews)
    $0.axis = .horizontal	// 수평 방향으로 요소가 나열되도록 지정
    $0.distribution = .fillEqually	// 모든 요소가 스택 내에서 동등한 크기를 가지도록 지정
    $0.alignment = .leading
    $0.spacing = 5
}

하단 스택뷰를 추가한 후

 

 

3. 레이아웃 구성

private func setLayouts() {
    // add views
    self.view.addSubview(backView)

	// 전체 컨테이너 뷰에 좌측 상단, 우측 상단, 하단 스택뷰를 추가
    backView.addSubview(leftStackView)
    backView.addSubview(rightStackView)
    backView.addSubview(bottomStackView)

    // add views in StackView
    // 좌측 상단 스택뷰 내에 요소로 사용될 sub view들을 추가
    leftStackView.addArrangedSubview(leftTitleLabel1)
    leftStackView.addArrangedSubview(leftTitleLabel2)

    // 우측 상단 스택뷰 내에 요소로 사용될 sub view들을 추가
    rightStackView.addArrangedSubview(cloudImgView)
    rightStackView.addArrangedSubview(rightTitleLabel1)
    rightStackView.addArrangedSubview(rightTitleLabel2)

    // 하단 스택뷰 내에 요소로 사용될 sub view들을 추가
    weatherViews.map { view in
        bottomStackView.addArrangedSubview(view)
    }

    // set constraints
    backView.snp.makeConstraints { make in
        make.top.equalToSuperview().offset(130)
        make.leading.equalToSuperview().offset(20)
        make.trailing.equalToSuperview().inset(20)
    }

    leftStackView.snp.makeConstraints { make in
        make.leading.top.equalToSuperview().offset(15)
    }

    cloudImgView.snp.makeConstraints { make in
        make.top.equalToSuperview()
    }
    rightStackView.snp.makeConstraints { make in
        make.top.trailing.equalToSuperview().inset(15)
        make.bottom.equalTo(leftStackView.snp.bottom)
    }
    bottomStackView.snp.makeConstraints { make in
        make.top.equalTo(leftStackView.snp.bottom).offset(30)
        make.leading.trailing.equalToSuperview().inset(10)
        make.bottom.equalToSuperview().inset(15)
    }
}

 

4. 추가) 수직 스택뷰 요소 간 spacing 달리 주기

// 구름 이미지 UIImageView를 감싸는 컨테이너 뷰 정의
let cloudImgContainerView = UIView().then {
    $0.backgroundColor = .clear
}

let cloudImgView = UIImageView(image: UIImage(systemName: "cloud.fill")).then {
    $0.tintColor = .white
    $0.contentMode = .scaleAspectFill
}

// 레이아웃 구성 변경
cloudImgView.snp.makeConstraints { make in
    make.top.bottom.leading.trailing.equalToSuperview()
}

// 구름 이미지 컴포넌트 하단 요소에 대해 top 방향으로 원하는 만큼의 여백값을 추가
rightTitleLabel1.snp.makeConstraints { make in
    make.top.equalTo(cloudImgView.snp.bottom).offset(20)
}

 

5. 추가) 전체 컨테이너 영역 내에 대한 탭 이벤트 정의

lazy var backView = UIView().then {
    $0.backgroundColor = .gray
    $0.layer.cornerRadius = 20
    
    // 탭 제스처를 감지하는 객체를 추가
    $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapWeatherView)))
}

@objc func didTapWeatherView() {
    let detailVC = WeatherDetailViewController()
    self.navigationController?.pushViewController(detailVC, animated: true)
}

*참고*

SceneDelegate.swift 파일 내 willConnectTo 메소드

코드 베이스로 UIKit을 사용할 경우,

가장 처음 시작점이 되는 ViewController 내에서 NavigationController를 사용하기 위해서는 

해당 ViewController가 UINavigationContoller를 가질 수 있도록 정의해주어야 함

 

 

 

 

구현 결과

 

구현 코드

https://github.com/ryr0121/UIKitPractice/tree/main/CustomCollectionViewPractice

 

구현 과정

1. Storyboard에 CollectionView 추가

컴포넌트 추가 단축키 - cmd + shift + L

 

2. 추가한 CollectionView를 @IBOutlet으로 연결

 

3. CollectionView에 사용할 커스텀 Cell을 Xib으로 생성 및 정의

  3-(1) CollectionViewCell을 xib으로 생성

파일 추가 단축키 - cmd + N
"Also create XIB file"을 반드시 체크해주어야 .xib 생성됨
생성 완

  3-(2) Cell 내부 정의

우측에 있는 width / height로 셀의 크기를 조정하여 스토리보드 사용 가능
파일을 듀얼로 띄우기 - option을 누른 채로 왼쪽 파일 탭에서 띄울 파일을 클릭

 

4. CollectionView를 사용하기 위한 코드 추가 (프로토콜 채택, 커스텀 셀 등록 등)

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupCollectionView()
    }

    func setupCollectionView() {
        // CollectionView를 사용하기 위한 필수 프로토콜 채택
        collectionView.dataSource = self
        collectionView.delegate = self
        // CollectionView에 사용할 커스텀 셀 등록
        collectionView.register(UINib(nibName: "MainCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "MainCollectionViewCell")
    }
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // CollectionView에 보여질 셀의 개수 반환
        return 10
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // CollectionView에 보여질 셀 반환
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainCollectionViewCell", for: indexPath) as? MainCollectionViewCell else { return UICollectionViewCell() }
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // 셀의 크기 반환
        return CGSize(width: 100, height: 100)
    }
}

 

 

추가

Storyboard에서 CollectionView의 스크롤 방향 조정하기

Vertical 수직 스크롤 (기본값)
Horizontal 수평 스크롤

 

* 한 줄짜리 수평 스크롤이 가능한 CollectionView를 만들기 위해서는, CollectionView의 높이가 셀의 높이의 2배보다 작아야 함

높이를 130으로 지정한 수평 스크롤이 가능한 CollectionView

+ Recent posts