10 minute read

1차원 배열 탐색

1. 수열 축소

  • 길이가 n인 수열이 주어지면 인접한 두 수의 차이를 이용해 길이가 N-1인 수열을 만듭니다.

  • 만약 수열이 [5, 3, 7, 9, -2]라면 [(3-5), (7-3), (9-7), (-2-9)] => [-2, 4, 2, -11]로 수열의 길이를 줄일 수 있습니다.
  • 이런 과정을 길이축소작업이라 하겠습니다.

  • n길이의 수열 nums 주어지면 m번의 길이축소작업을 한 결과인 배열을 반환하는 프로그램을 작성하세요.

제한사항

  • nums의 길이 3<= n <=30
  • m(m<n)

풀이

def solution(nums, m):
    answer = nums

    for _ in range(m):
        for i in range(len(answer)-1):
            answer[i] = answer[i+1]-answer[i]

        del answer[-1]

    return answer


print(solution([5, 3, 7, 9, -2], 2))

리뷰

  • i+1번째 항목에 i번째 항목을 빼서 i번째 항목의 값으로 갱신한다.

  • 해당 행위를 m번 반복한다.

2. 제곱수 정렬

  • 오름차순 정렬되어 있는 길이가 n인 수열이 주어집니다. 수열의 원소를 제곱하여 오름차순 정렬한 배열을 반환하는 프로그램을 작성하세요.

제한사항

  • 주의 : sort 함수를 사용하면 안됩니다.
  • nums의 길이 3<= n <=100,000

풀이 1

def solution(nums):
    answer = []

    answer = [x**2 for x in nums]

    for i in range(len(answer)):
        for j in range(i,len(answer)):
            if answer[i] > answer[j]:
                tmp = answer[i]
                answer[i] = answer[j]
                answer[j] = tmp

    return answer


print(solution([-7, -3, 2, 3, 11]))

리뷰

  • 주어진 배열의 각 항목을 전부 제곱하고, 수동으로 정렬했다.
  • sort함수를 쓰지 말라니까 수동으로 sort를 돌렸다.
  • 내가 해놓고도 어이가 없는 풀이법이지만, 일단 답은 잘 나오기에 기록은 해두었다.

풀이 2 (정렬 없는 풀이)


def solution(nums):
    answer = [0]*len(nums)
    left = 0
    right = len(nums)-1
    for i in range(len(nums)-1,-1,-1):
        if abs(nums[left])<abs(nums[right]):
            tmp=nums[right]
            right-=1
        else:
            tmp=nums[left]
            left += 1
        answer[i]=tmp*tmp

    return answer


print(solution([-7, -3, 2, 3, 11]))

리뷰

  • left와 right 포인터를 양쪽 끝에 생성하고, 리스트에 nums[left]와 nums[right] 중 절댓값이 큰 값을 넣어가며 좁혀나간다.

  • 주어진 배열은 오름차순 정렬 되어있으므로, 절댓값이 가장 작은 값은 배열의 가운데에 위치한다.

  • 따라서 리스트에 넣은 값들을 제곱해준 후, 리스트를 역순으로 돌리면 제곱수들이 정렬되어 나오게 된다.

3. 수열의 높이 조정

  • n길이의 수열이 주어집니다.
  • 수열의 높이 조정은 수열의 원소값 중 가장 큰 원소에서 1을 빼 가장 작은 원소에 더해주는 것을 말합니다.
  • 가장 큰 원소와 가장 작은 원소가 여러개면 그 중 아무거나 선택하면 됩니다.
  • 만약 수열이 [2, 1, 3, 7, 5]라면 1회의 높이조정을 거치면 [2, 2, 3, 6, 5]가 됩니다.
  • n길이의 수열 nums가 주어지면 높이조정을 m회 한 후 가장 큰 값과 가장 작은 값을 차를 반환하는 프로그램을 작성하세요.
  • 단 m회의 높이조정을 하던 중 모든 값이 같아지면 이 조정을 중단하고 0을 반환합니다.

제한사항

  • nums의 길이 3<= n <=100,000
  • 배열 nums의 원소는 1,000을 넘지 않습니다.
  • m(1<=m<=10,000)

풀이

def solution(nums, m):
    answer = 0

    for _ in range(m):
        if min(nums) == max(nums):
            break
        else:
            min_idx = nums.index(min(nums))
            max_idx = nums.index(max(nums))
            nums[min_idx] += 1
            nums[max_idx] -= 1

    answer = max(nums) - min(nums)

    return answer


print(solution([3, 2, 3, 3, 4] , 3))

리뷰

  • 배열의 가장 큰 값과 가장 작은값이 같아진다면, 배열 내 모든 값이 같은것이므로 높이 조정을 중단한다.

  • 높이 조정이 끝나면, 배열의 최대값과 최소값의 차를 리턴한다.

4. 가장 높은 증가 수열

  • 길이가 n인 수열이 주어지면 이 수열에서 연속된 부분 증가수열을 찾습니다.
  • 각 부분증가수열은 높이가 있습니다.
  • 증가수열의 높이란 증가수열의 첫항과 마지막항의 차를 의미합니다.
  • 수열이 주어지면 여러 증가수열 중 가장 높은 부분증가수열을 찾는 프로그램을 작성하세요.
  • 만약 수열이 [5, 2, 4, 7, 7, 3, 9, 10, 11]이 주어지면 가장 높은 부분증가수열은 [3, 9, 10, 11]이고, 높이는 8입니다.

제한사항

  • 주의 : 이웃하는 두 수가 같을 경우 증가수열로 보지 않습니다.
  • nums의 길이 3<= n <=100,000
  • 배열 nums의 원소는 자연수입니다.

풀이

def solution(nums):
    answer = 0

    head = 0
    tail = 0
    max_gap = 0
    for i in range(1, len(nums)):
        if nums[i - 1] < nums[i]:
            tail += 1
        else:
            if max_gap < nums[tail] - nums[head]:
                max_gap = nums[tail] - nums[head]
            head = i
            tail = i

    if max_gap < nums[tail] - nums[head]:
        max_gap = nums[tail] - nums[head]

    answer = max_gap

    return answer


print(solution([8, 12, 2, 3, 7, 6, 20, 3]))

리뷰

  • head와 tail 두 포인터를 설정한다.

  • nums가 증가하고 있다면(nums의 i-1번보다 i번 항목이 크다면) tail을 증가시킨다.

  • nums가 더이상 증가하지 않는다면, 찾아낸 증가수열의 각 끝에 위치한 포인터들인 tail과 head에서의 값의 차를 구하고, 최대값보다 클 경우 최대값을 갱신한다.

    • 해당 작업이 끝난 후, head와 tail은 증가수열이 종료된 인덱스로 초기화를 진행한다.
  • nums가 마지막 항목까지 증가하고 있었다면, tail과 head에서의 값의 차를 구하고, 최대값보다 클 경우 최대값을 갱신한다.

5. 가장 긴 수열

  • 길이가 N인 수열이 주어지면 이 수열에서 연속으로 증가하거나, 또는 연속으로 작아지는 부분 수열 중 가장 길이가 긴 수열을 찾으려고 합니다.
  • 만약 [5, 3, 6, 7, 9, 8, 5, 3, 1, 2]이 주어지면 우리가 찾는 가장 긴 수열은 [9, 8, 5, 3, 1]입니다.

  • 수열 [1, 2, 3, 3, 4, 5, 6]과 같이 같은 값이 연속으로 있는 것은 증가 또는 감소로 보지 않기 때문에 가장 긴 수열은 [3, 4, 5, 6]이 됩니다.

  • 매개변수 nums에 수열이 주어지면 nums에서 연속 증가하거나 연속 감소하는 부분수열 중 가장 긴 수열의 길이를 반환하는 프로그램을 작성하세요.

제한사항

  • nums의 길이 3<= n <=100,000
  • 배열 nums의 원소는 자연수입니다.

풀이

def solution(nums):
    answer = 0

    ih = 0
    it = 0
    dh = 0
    dt = 0
    max_len = 0

    for i in range(1,len(nums)):
        if nums[i-1] < nums[i]:
            it += 1
            if max_len < dt-dh+1:
                max_len = dt-dh+1
            dh = i
            dt = i
        elif nums[i-1] > nums[i]:
            if max_len < it-ih+1:
                max_len = it-ih+1
            ih = i
            it = i
            dt += 1
        else:
            if max_len < it-ih+1:
                max_len = it-ih+1
            if max_len < dt-dh+1:
                max_len = dt-dh+1
            dh = i
            dt = i
            ih = i
            it = i

    answer = max_len

    return answer


print(solution([1, 2, 3, 3, 4, 5, 6, 7, 7]))

리뷰

  • 강사님은 반복문 두번으로 증가수열과 감소수열을 각각 구하셨지만, 나는 코드가 다소 더러워지더라도 반복문 한번으로 끝내고 싶어 두개를 한번에 구했다.

  • 증가수열과 감소수열의 최대 길이를 각각 구한후, 비교해서 최대값을 리턴한다.

6. 바이토닉 수열

  • 바이토닉 수열이란 수열이 증가했다가 감소하는 수열을 의미합니다.
  • 길이가 n인 수열이 매개변수 nums에 주어지면 이 수열이 바이토닉 수열인지 판별하는 프로그램을 작성하세요.

  • 만약 [1, 2, 3, 4, 2, 1]이면 바이토닉 수열입니다.
  • 하지만 [1, 2, 2, 3, 2, 1]과 같이 같은 값이 연속으로 있으면 바이토닉 수열이라 하지 않습니다.

제한사항

  • nums의 길이 3<= n <=30
  • 배열 nums의 원소는 자연수입니다.

풀이

def solution(nums):
    answer = ''

    max_idx = nums.index(max(nums))
    head = max_idx
    tail = max_idx

    while head > 0 or tail < len(nums)-1:
        if head > 0:
            if nums[head-1] >= nums[head]:
                return "NO"
            head -= 1

        if tail < len(nums)-1:
            if nums[tail+1] >= nums[tail]:
                return "NO"
            tail += 1

    if tail == max_idx or head == max_idx:
        return "NO"

    return "YES"


print(solution([1, 2, 3, 4, 5]))

리뷰

  • 배열 내에서 최대값을 잡고, 그 최대값을 기준으로 양 옆으로 감소 수열이 유지되는지 검사한다.

  • 감소수열이 유지되지 않는다면 해당 수열은 바이토닉 수열이 아니므로, NO를 리턴한다.

  • 최대값이 양쪽 끝에 존재하는 경우, 반대쪽으로 가는 수열이 존재할수 없으므로 NO를 리턴한다.

  • 나머지 경우에는 YES를 리턴한다.

7. 거리 두기

  • 현수는 영화관에 도착했습니다.
  • 영화상영 시간보다 약간 늦은 현수는 남은 좌석을 빨리 선택 하고 영화를 보려고 합니다.
  • 일렬로된 n개의 좌석정보가 매개변수 nums에 주어지면, 이미 앉아 있는 사람들 중 가장 가까운 사람과 최대한 멀리 떨어져 앉을 자석을 선택해야 그 거리를 반환하는 프로그램을 작성하세요.

제한사항

  • nums의 길이 3<= n <=100
  • 배열 nums의 원소 1은 이미 사람이 앉은 좌석이고, 0은 빈 좌석입니다.

풀이

def solution(nums):
    answer = 0

    head = 0
    tail = 0
    max_len = 0
    max_head = 0
    max_tail = 0
    for i in range(len(nums)):
        if nums[i] == 1:
            if max_len < tail - head+1:
                max_len = tail - head+1
                max_head = head
                max_tail = tail+1
            head = i
            tail = i
        else:
            tail += 1

    answer = (max_head + max_tail) // 2

    return answer


print(solution([1, 0, 0, 0, 1, 0, 0, 1, 0, 1]))

리뷰

  • head와 tail을 설정하고, 1이 나올때마다 해당 지점에서 head와 tail을 해당 인덱스로 초기화한다.

  • 최대 길이가 갱신될때마다 해당 부분에서의 tail과 head의 인덱스를 저장한다.

  • 최대 길이의 리스트를 구했다면, 해당 부분의 거리인 (tail + head의 절반)을 구해 리턴한다.

8. 빈도수 구하기

  • 길이가 n인 수열이 nums주어집니다.
  • 자연수 k가 주어지면 수열의 원소 중 빈도수가 가장 많은 것 순으로 K개를 배열형태로 반환하는 프로그램을 작성하세요.

제한사항

  • nums의 길이 3<= n <=30
  • m(m<n)

풀이

from collections import Counter


def solution(nums, m):
    answer = [x for y, x in sorted([(y, x) for x, y in Counter(nums).items()], reverse=True)][:m]

    return answer


print(solution([1, 1, 1, 2, 2, 3], 2))

리뷰

  • 원래라면 포인터를 이용한 탐색이 주가 되었겠지만, 또 도져버린 한줄코딩병으로 인해 list comprehension을 주로 사용한 코드가 되어버렸다.

    • 사실 한줄코딩병이 아니더라도 Counter를 이용해 풀었을것같아 어차피 강사님의 의도와는 멀어졌을것 같긴 하다.
  • Counter의 items에서 key,value값을 꺼내 값과 그 값의 빈도수를 구하고, 빈도수가 많은 순서로 정렬한다.

  • 정렬된 배열에서 키값만을 빼낸 후, m번째 순서까지만 표시하여 리턴한다.

9. 최대길이 바이토닉

  • 바이토닉 수열이란 수열이 증가했다가 감소하는 수열을 의미합니다.

  • 길이가 n인 수열이 주어지면 이 수열의 연속부분수열 중 가장 긴 바이토닉 수열을 찾아 그 길이를 반환하는 프로그램을 작성하세요.

  • 만약 [1, 3, 2, 5, 7, 4, 2, 5, 1]수열이 주어지면 이 수열의 연속부분수열 중 가장 긴 바이토닉 수열은 [2, 5, 7, 4, 2]이고, 답은 5입니다.

제한사항

  • nums의 길이 3<= n <=10,000
  • 배열 nums의 원소는 자연수입니다.

풀이 (내 풀이)

def solution(nums):
    answer = 0
    max_len = 0
    
    for i in range(len(nums)):
        head = i
        tail = i
        flag = False
        for j in range(i + 1, len(nums)):
            if nums[j - 1] >= nums[j]:
                flag = True
            elif flag:
                if nums[j - 1] <= nums[j]:
                    if max_len < tail - head + 1:
                        max_len = tail - head + 1
                    break
            tail += 1
    
    answer = max_len

    return answer


print(solution([1, 3, 2, 5, 7, 4, 2, 5, 1]))

리뷰

  • 배열 내의 각 위치로부터 시작하는 바이토닉 수열을 검사한다.

  • 가장 긴 값을 리턴한다.

풀이 2 (강사님 풀이)

def solution(nums):
    answer = 0
    max_len = 0
    
    peak = []
    for i in range(1, len(nums) - 1):
        if nums[i - 1] < nums[i] and nums[i + 1] < nums[i]:
            peak.append(i)

    length = []
    for idx in peak:
        left = idx
        right = idx
        cnt = 1
        while left >= 0 and nums[left-1] < nums[left]:
            cnt += 1
            left -= 1
        while right < len(nums)-1 and nums[right+1] < nums[right]:
            cnt += 1
            right += 1

        length.append(cnt)

    answer = max(length)

    return answer


print(solution([1, 3, 2, 5, 7, 4, 2, 5, 1]))

리뷰

  • 양 옆의 항목이 자신보다 낮은 항목들을 peak에 저장.

  • 각 peak에서 바이토닉 수열을 검사하여 길이를 length에 저장.

  • length의 최대값을 리턴.

10. 수열의 경우수

  • 바이토닉 수열이란 수열이 증가했다가 감소하는 수열을 의미합니다.

  • 길이가 n인 수열이 주어지면 이 수열의 연속부분수열 중 바이토익 수열이 몇 개 있는지 알아내는 프로그램을 작성하세요.

  • 만약 [1, 3, 2, 5, 7, 4, 2, 5, 1]수열이 주어지면 이 수열의 연속부분 수열 중 바이토닉 수열은 [1, 3, 2], [2, 5, 7, 4], [2, 5, 7, 4, 2], [5, 7, 4], [5, 7, 4, 2], [2, 5, 1]로 6개가 있습니다.

제한사항

  • nums의 길이 3<= n <=100
  • 배열 nums의 원소는 자연수입니다.

풀이

def solution(nums):
    answer = 0

    peak = []
    for i in range(1,len(nums)-1):
        if nums[i-1] < nums[i] and nums[i+1] < nums[i]:
            peak.append(i)

    for idx in peak:
        left = idx
        right = idx

        lc = 0
        rc = 0
        while left >= 0 and nums[left - 1] < nums[left]:
            lc += 1
            left -= 1
        while right < len(nums) - 1 and nums[right + 1] < nums[right]:
            rc += 1
            right += 1

        answer += lc * rc

    return answer


print(solution([1, 3, 2, 5, 7, 4, 2, 5, 1]))

리뷰

  • 양 옆의 항목이 자신보다 작은 경우, peak에 저장.

  • 각 peak에서 시작되는 바이토닉 수열의 최대 범위인 head와 tail을 구한다.

  • 최대 범위의 바이토닉 수열에서 나올수 있는 바이토닉 수열의 개수는 해당 수열의 head * tail로 표현되므로, answer에 누적하여 더해준다.

  • answer를 리턴.

11. 키보드

  • 당신은 키보드의 자판 중에서 스페이스바 외에 n개의 자판만 사용할 수 있습니다.

  • 만약 n=5이면, 당신은 “time to time”이라는 문장을 완성할 수 있습니다.

  • 만약 “Time to Time”라는 문장을 완성하려면 대문자 ‘T’를 써야 하는데 이 때는 shift키와 소문자 ‘t’키를 누르면 됩니다. 그래서 “Time to Time”문장을 완성하기 위해서는 n=6이어야 가능합니다.

  • 하나의 문장을 n개의 키로 완성할 수 있으면 그 문장에 쓰인 문자개수만큼 점수를 얻을 수 있습니다.

  • “Time to Time” 문장을 완성했을 경우 12점을 얻을 수 있습니다.
  • 매개변수에 하나의 문장을 담은 s문자열 변수와 사용 가능한 키의 개수인 n이 주어지면 여러분이 얻을 수 있는 점수를 리턴하는 함수를 작성하세요.

  • 만약 문장을 완성할 수 없다면 -1을 리턴합니다.

제한사항

  • 1 ≤ s의 길이 ≤ 1,000
  • 1 ≤ n ≤ 20

풀이

def solution(nums, m):
    used = [0] * 27

    for x in s:
        if x.islower():
            used[ord(x) - 97] = 1
        elif x.isupper():
            used[ord(x) - 65] = 1
            used[26] = 1

    if len([x for x in used if x > 0]) <= n:
        return len(s)
    else:
        return -1


print(solution("Big Life is Good", 10))

리뷰

  • 모 대기업의 코드 테스트 문제를 강사님께서 난이도를 낮추어 낸 문제이다.

  • 알파벳 26글자 + shift 한글자를 해싱할 27 크기의 리스트를 생성한다.

  • 소문자일경우 아스키코드값 97을 빼준 번호를 1로 설정하고, 대문자일 경우 shift에 해당하는 used[26]번 값과 아스키코드값 65를 빼준 값을 1로 설정한다.

  • used에서 1로 설정된 값들의 갯수를 리턴하고, used가 설정되지 않았다면 -1을 리턴한다.