기록

99클럽 코테 스터디 21일차 TIL C++ 정렬, 람다함수 본문

코딩테스트/cpp

99클럽 코테 스터디 21일차 TIL C++ 정렬, 람다함수

youngyin 2024. 11. 19. 22:24

문제 소개

오늘 풀어본 문제는 LeetCode 문제 중 하나로, 각 행을 내림차순 정렬한 후 열별로 가장 큰 값을 합산하는 문제입니다. 이 문제를 풀면서 자연스럽게 정렬과 람다 함수를 어떻게 활용할 수 있는지에 대해 학습할 수 있었습니다.

이 글에서는 문제 풀이 과정에서 사용한 정렬 및 람다 함수에 대해 설명하고, 기존 풀이를 어떻게 더 간결하게 개선할 수 있는지 보여드리겠습니다.

문제 풀이: 열별로 최대값을 더하기

이 문제의 핵심은 다음과 같습니다:

  1. 각 행을 내림차순으로 정렬: 각 행에서 가장 큰 값을 쉽게 선택할 수 있도록 내림차순 정렬합니다.
  2. 각 열에서 가장 큰 값을 선택하여 합산: 열별로 가장 큰 값을 찾아 결과에 더하는 방식입니다.

전체 코드 풀이

우선 기본적인 풀이 방법을 살펴보겠습니다. C++에서 각 행을 내림차순으로 정렬한 뒤, 각 열에서 최대값을 찾아 더하는 방식으로 구현합니다.

#include <vector>
#include <algorithm>
#include <iostream>

class Solution {
public:
    int deleteGreatestValue(std::vector<std::vector<int>>& grid) {
        // 각 행을 정렬 (내림차순)
        for (auto& row : grid) {
            std::sort(row.begin(), row.end(), std::greater<int>());
        }

        int ans = 0;
        int numColumns = grid[0].size();

        // 각 열의 최대값을 선택
        for (int col = 0; col < numColumns; ++col) {
            int maxValue = 0;
            for (auto& row : grid) {
                // 각 행의 현재 열에서 값을 가져옴
                maxValue = std::max(maxValue, row[col]);
            }
            ans += maxValue;
        }

        return ans;
    }
};

이 코드에서는 각 행을 내림차순으로 정렬한 뒤, 각 열의 최대값을 더해주는 방식으로 ans를 계산하고 있습니다.

람다 함수란?

람다 함수는 간단하고 임시적인 함수 로직을 작성할 때 유용합니다. C++에서는 C++11부터 람다 함수를 지원하기 시작했으며, 자바스크립트(JavaScript), Python, 자바(Java) 등 다양한 언어에서도 흔히 사용되고 있습니다.

람다 함수의 기본 구조

람다 함수의 기본적인 구조는 다음과 같습니다:

C++ 람다 함수

[capture](parameters) -> return_type {
    // 함수 내용
};
  • capture: 외부 변수(스코프)를 람다 내부로 가져오는 방법.
    • [=]: 외부 변수를 값으로 캡처합니다.
    • [&]: 외부 변수를 참조로 캡처합니다.
    • [var]: 특정 변수만 선택적으로 캡처할 수 있습니다.
  • parameters: 함수의 매개변수.
  • return_type: 반환 타입 (생략 가능, 자동으로 추론됩니다).

JavaScript 람다 함수 (화살표 함수)
JavaScript에서는 람다 함수를 화살표 함수라고 부르며, 다음과 같은 구조를 가집니다:

(parameters) => {
    // 함수 내용
};
  • 매개변수가 하나일 경우 괄호를 생략할 수 있습니다.
  • return 문이 하나인 경우 중괄호와 return 키워드를 생략할 수 있습니다.

예시:

const add = (a, b) => a + b;

 

Java 람다 함수
Java에서는 Java 8부터 람다 표현식을 지원하며, 기본적인 구조는 다음과 같습니다:

(parameters) -> {
    // 함수 내용
};
  • 람다는 주로 함수형 인터페이스와 함께 사용됩니다. 함수형 인터페이스는 하나의 추상 메소드만을 가지는 인터페이스를 의미합니다.

예시:

(int a, int b) -> a + b;

 

람다는 Stream API와 같은 Java의 함수형 프로그래밍을 지원하는 다양한 기능과 결합하여 사용됩니다.

  • capture: 외부 변수(스코프)를 람다 내부로 가져오는 방법.
    • [=]: 외부 변수를 값으로 캡처합니다.
    • [&]: 외부 변수를 참조로 캡처합니다.
    • [var]: 특정 변수만 선택적으로 캡처할 수 있습니다.
  • parameters: 함수의 매개변수.
  • return_type: 반환 타입 (생략 가능, 자동으로 추론됩니다).

기존 코드의 개선: 람다 함수 사용하기

위에서 작성한 기존 코드를 개선해 보겠습니다. 이번에는 람다 함수STL 함수를 사용하여 코드를 더욱 간결하고 직관적으로 변경해 보았습니다.

개선된 코드

람다 함수와 STL 알고리즘(std::for_each, std::max_element)을 활용하여 각 행을 정렬하고 열의 최대값을 선택하는 방식으로 개선한 코드입니다:

#include <vector>
#include <algorithm>
#include <iostream>

class Solution {
public:
    int deleteGreatestValue(std::vector<std::vector<int>>& grid) {
        // 각 행을 정렬 (내림차순)
        std::for_each(grid.begin(), grid.end(), [](std::vector<int>& row) {
            std::sort(row.begin(), row.end(), std::greater<int>());
        });

        int ans = 0;
        int numColumns = grid[0].size();

        // 각 열의 최대값을 선택
        for (int col = 0; col < numColumns; ++col) {
            // 반복자를 이용해 각 열에서 최대값 선택
            auto maxRowIt = std::max_element(
                grid.begin(), grid.end(),
                [col](const std::vector<int>& row1, const std::vector<int>& row2) {
                    return row1[col] < row2[col];
                });

            // 반복자에서 값을 추출하여 합산
            ans += (*maxRowIt)[col];
        }

        return ans;
    }
};

개선된 코드 설명

  1. std::for_each와 람다 함수 사용: 각 행을 내림차순으로 정렬할 때, std::for_each와 람다를 사용하여 코드를 더 간결하게 만들었습니다.
  2. std::max_element와 람다 함수 사용: 각 열의 최대값을 선택할 때, 람다를 이용한 커스텀 비교 연산을 통해 직관적으로 열의 최대값을 찾아낼 수 있도록 했습니다.

람다 함수의 장점과 단점

람다 함수는 다음과 같은 장점을 가집니다:

  1. 간결함: 코드가 짧고 직관적이기 때문에 반복적인 로직을 간단하게 작성할 수 있습니다.
  2. 지역성: 지역적으로 사용되는 간단한 로직을 쉽게 함수화할 수 있습니다.
  3. 캡처: 외부 변수에 쉽게 접근할 수 있어 유연한 코드를 작성할 수 있습니다.
  4. STL과의 결합: STL 함수(std::for_each, std::sort, std::max_element)와 결합하면 더욱 간결하고 유지보수하기 쉬운 코드를 작성할 수 있습니다.

하지만 람다 함수에도 단점이 있습니다:

  • 특정 경우에는 람다 함수의 사용이 직접 for문을 사용하는 것보다 성능 면에서 좋지 않을 수 있습니다. for문은 람다에 비해 명확하고 컴파일러 최적화가 용이할 수 있기 때문에, 반복 횟수가 많거나 성능이 중요한 코드에서는 for문이 더 효율적입니다.
  • 복잡한 로직을 처리할 때는 가독성이 떨어질 수 있습니다.
  • 캡처의 사용이 잘못될 경우, 의도치 않은 동작이나 메모리 누수와 같은 심각한 문제가 발생할 수 있습니다. 특히 참조로 캡처할 때는 캡처된 변수가 람다의 생명 주기보다 먼저 소멸될 수 있으므로 주의가 필요합니다. 동작이나 메모리 문제가 발생할 수 있습니다.

오늘의 회고

이번 학습을 통해 C++에서 람다 함수를 어떻게 활용할 수 있는지, 특히 STL 알고리즘과 결합했을 때 얼마나 코드가 간결해질 수 있는지를 배울 수 있었습니다.

  1. 코드의 간결성과 유연성: 람다 함수는 코드의 간결성을 높이기 위해 매우 유용한 도구입니다. STL과 결합하여 불필요한 반복을 줄이고, 더 직관적인 코드를 작성할 수 있습니다.
  2. 적절한 사용: 그러나, 람다 함수의 남용은 코드 가독성을 해칠 수 있기 때문에 복잡한 로직보다는 간단한 작업을 처리할 때 사용하는 것이 좋습니다.
  3. STL 활용의 중요성: std::for_eachstd::max_element 같은 STL 함수는 반복문을 대체하여 가독성 높은 코드를 작성하는 데 큰 도움이 됩니다.
Comments