4. 정렬

2021. 8. 8. 00:58문제풀기/알고리즘

1. 선택 정렬 (Selection Sort)

- 가장 작은 데이터를 선택해 맨 앞에 있는 데이터와 바꾸고, 그 다음 작은 데이터를 선택해 앞에서 두 번째 데이터와 바꾸고 ....

=> 정렬되지 않은 부분에서 가장 작은 것을 선택해서 정렬되지 않은 부분의 가장 앞의 원소와 바꾸는 것

- 시간 복잡도 : O(N^2)

def selectionSort(array):
    for start_index in range(len(array)):
        minimum_index = start_index
        for now_index in range(start_index+1, len(array)):
            if array[now_index] < array[minimum_index]:
                minimum_index = now_index

        array[start_index], array[minimum_index] = array[minimum_index], array[start_index]

    return array

 

2. 삽입 정렬 (Insertion Sort)

- 앞의 정렬되어 있는 부분 중 맞는 위치에 '삽입'하면서 정렬하는 것

- 시간 복잡도 : 평균적으로 O(N^2), 이미 정렬이 되어있는 최적의 경우는 O(N)

def insertionSort(array):
    for i in range(1,len(array)):
        for j in range(i,0,-1):
            if array[j]<array[j-1]: #한 칸씩 왼쪽으로 이동
                array[j],array[j-1] = array[j-1],array[j]
            else: #자기보다 작은 데이터를 만나면 멈추기
                break

 

3. 퀵 정렬

- 피벗(기준)을 잡고, 왼쪽에서부터 피벗보다 큰 데이터를 찾고, 오른쪽에서부터 피벗보다 작은 데이터를 찾아서 위치를 서로 교환해준다. 왼쪽에서 오른쪽으로 증가하는 인덱스와 오른쪽에서 왼쪽으로 감소하는 인덱스가 서로 만날 때 까지 이를 반복하자.
=> 두 인덱스가 서로 엇갈리게 되면, 작은 데이터와 피벗의 위치를 변경해준다.

=> 이 후, 피벗보다 작은 쪽과 피벗보다 큰 쪽에서 위의 과정을 반복해준다.

=> 피벗보다 작은 쪽, 피벗보다 큰 쪽으로 나눈 리스트의 길이가 1이 되면 종료한다.

- 시간 복잡도 : O(NlogN), 이미 데이터가 정렬되어 있는 최악의 경우 O(N^2)

def quickSort(array, start, end):
    if start>=end:
        return

    pivot = array[start]
    pivot_index = start

    left_index = start+1
    right_index = end
    
    while left_index<=right_index:
        while left_index<=end and array[left_index] <= pivot:
            left_index += 1

        while right_index>start and array[right_index]>=pivot:
            right_index -= 1

        if left_index<=right_index:
            array[left_index],array[right_index] = array[right_index], array[left_index]
        else:
            array[right_index],array[pivot_index] = array[pivot_index], array[right_index]

    quickSort(array,start,right_index-1)
    quickSort(array,right_index+1, end)

* 파이썬의 장점을 살린 버전 (시간 면에서는 조금 비효율적이다)

def quickSort(array):
    if len(array)<=1:
        return array

    pivot = array[0]
    tail = array[1:]

    left_side = [i for i in tail if i<=pivot]
    right_side = [i for i in tail if i>pivot]

    return quickSort(left_side) + [pivot] + quickSort(right_side)

 

4. 계수 정렬(Count Sort)

- 특정한 조건이 부합할 때만 사용할 수 있지만 매우 빠른 정렬 알고리즘이다.

=> 데이터의 크기 범위가 제한되어 정수 형태로 표현할수 있을 때만 사용할 수 있다. (실수형 데이터는 사용하기 어려움)

=> 일반적으로 가장 큰 데이터와 가장 작은 데이터의 차이가 1,000,000을 넘지 않을 때 효과적이다.

=> 왜? : 모든 범위를 담을 수 있는 크기의 리스트를 선언해야 하기 때문에

 

- 동일한 수가 여러번 나올 때 유리한 정렬이다.

- 시간 복잡도 : O(데이터의 개수 + 데이터 중 최댓값), 사용할 수 있다면 굉장히 빠르다

- 공간 복잡도 : O(데이터의 개수 + 데이터 중 최댓값)

- 가장 큰 데이터와 가장 작은 데이터의 범위가 모두 담길 수 있도록 하나의 리스트를 생성하고, 데이터의 값과 동일한 인덱스의 데이터를 1씩 증가시키면 계수 정렬이 완료된다.

def countSort(array):
    minimum = min(array)
    maximum = max(array)

    count = [0] * (maximum - minimum + 1)

    for i in array:
        count[i-minimum] += 1

    for i in range(len(count)):
        for j in range(count[i]):
            print(minimum+i,end = ' ')

 

5. 파이썬의 정렬 라이브러리

(1) sorted() 함수

- 일반적으로는 퀵 정렬보다 느리지만 최악의 경우에도 O(NlogN)을 보장한다.

- 리스트, 딕셔너리 자료형 등을 입력하면 정렬된 결과를 리스트 자료형으로 리턴한다.

 

(2) 리스트의 내장 함수인 sort()

- 정렬된 리스트가 반환되지 않고 내부 원소가 바로 정렬된다.

 

(3) sorted(), sort() 함수의 key 매개변수

- 정렬 기준인 key 매개변수를 줄 수 있다.

array = [('바나나',2), ('사과',5), ('당근',3)]

def setting(data):
    return data[1]

result = sorted(array, key=setting)

print(result) #[('바나나', 2), ('당근', 3), ('사과', 5)]

 

<문제>

1. 위에서 아래로 

하나의 수열에는 다양한 수가 존재하며, 이런 큰 수는 크기와 상관 없이 무작위로 주어진다. 이 수를 큰수 부터 작은 수까지 내림차순으로 정렬하면되는 문제다. 즉 수열을 내림차순으로 정렬하는 프로그램을 만들면된다.

 

<입력 조건>

  • 첫째 줄에 수열에 속해 있는 수의 개수 N이 주어진다. 이때 범위는 1 <= N <= 500
  • 둘째 줄부터 N + 1 번째 줄 까지 N개의 수가 입력된다. 수의 범위는 1 이상 100,000 이하 자연수

<출력 조건>

  • 입력으로 주어진 수열이 내림차순으로 정렬된 결과를 공백으로 구분해서 출력하면된다. 동일한 수는 순서상관없다.
입력 예시
3
15
27
12
출력 예시
27 25 12

(1) 직접 퀵정렬 구현해서 사용하기

import sys

def quickSort(array, start, end):
    if start>=end:
        return

    pivot = array[start]
    pivot_index = start

    left_index = start+1
    right_index = end
    
    while left_index<=right_index:
        while left_index<=end and array[left_index] <= pivot:
            left_index += 1

        while right_index>start and array[right_index]>=pivot:
            right_index -= 1

        if left_index<=right_index:
            array[left_index],array[right_index] = array[right_index], array[left_index]
        else:
            array[right_index],array[pivot_index] = array[pivot_index], array[right_index]

    quickSort(array,start,right_index-1)
    quickSort(array,right_index+1, end)

n = int(sys.stdin.readline().rstrip())

array = []
for _ in range(n):
    array.append(int(sys.stdin.readline().rstrip()))

quickSort(array, 0, len(array)-1)

for i in range(len(array)-1,-1,-1):
    print(array[i], end = ' ')

(2) 파이썬의 라이브러리 사용하기

import sys

n = int(sys.stdin.readline().rstrip())

array = []
for _ in range(n):
    array.append(int(sys.stdin.readline().rstrip()))

array.sort(reverse=True)

for i in array:
    print(i, end = ' ')

 

2. 성적이 낮은 순서대로 학생 출력하기

N명의 학생의 성적 정보가 주어진다. 형식은 이름 성적 으로 주어지는데 이때 이들의 성적이 낮은 순으로 학생 이름을 출력하는 문제다.

 

<입력 조건>

  • 첫 번째 줄에 학생의 수 N이 입력된다. (1 <= N <= 100,000)
  • 두 번째 줄 부터 N+1 번째 줄 까지 학생의 이름 그리고 성적이 공백으로 주어진다. 학생이름 길이는 100이하, 성적은 100이하 자연수로 주어진다.

<출력 조건>

  • 모든 학생의 이름을 성적이 낮은 순으로 출력하면된다. 동일한 성적은 자유롭게 출력하면된다.
입력 예시
2
홍길동 96
이순신 78
출력 예시
이순신 홍길동
import sys

n = int(sys.stdin.readline().rstrip())

array = []
for i in range(n):
    inputs = list(sys.stdin.readline().rstrip().split())
    array.append([inputs[0],int(inputs[1])])

'''
def setting(data):
    return data[1]

array.sort(key = setting)
'''
array.sort(key = lambda student : student[1])

for i in array:
    print(i[0], end = ' ')

 

3. 두 배열의 원소 교체

동빈이는 두 개의 배열 A와 B를 가지고 있다. 두 배열은 N개의 원소로 구성되어 있으며, 배열의 원소는 모두 자연수이다

동빈이는 최대 K 번의 바꿔치기 연산을 수행할 수 있는데, 바꿔치기 연산이란 배열 A에 있는 원소 하나와 배열 B에 있는 원소 하나를 골라서 두 원소를 서로 바꾸는 것을 말한다. 동빈이의 최종 목표는 배열 A의 모든 원소의 합이 최대가 되도록 하는 것이며, 여러분은 동빈이를 도와야한다.

N, K, 그리고 배열 A와 B의 정보가 주어졌을 때, 최대 K 번의 바꿔치기 연산을 수행하여 만들 수 있는 배열 A의 모든 원소의 합의 최댓값을 출력하는 프로그램을 작성하라.

예를 들어 N = 5, K = 3이고, 배열 A와 B가 다음과 같다고 해보자.

  • 배열 A = [1, 2, 5, 4, 3]
  • 배열 B = [5, 5, 6, 6, 5]

이 경우, 다음과 같이 세 번의 연산을 수행할 수 있다

  • 연산 1) 배열 A의 원소 '1'과 배열 B의 원소 '6'을 바꾸기
  • 연산 2) 배열 A의 원소 '2'와 배열 B의 원소 '6'을 바꾸기
  • 연산 3) 배열 A의 원소 '3'과 배열 B의 원소 '5'를 바꾸기

세 번의 연산 이후 배열 A와 배열 B의 상태는 다음과 같이 구성될 것이다

  • 배열 A = [6, 6, 5, 4, 5]
  • 배열 B = [3, 5, 1, 2, 5]

이때 배열 A의 모든 원소의 합은 26이 되며, 이보다 더 합을 크게 만들 수는 없다

 

<입력 조건>

  • 첫 번째 줄: N, K 가 공백으로 구분되어 입력 (1 <= N <= 100,000, 0 <= K <= N)
  • 두 번째 줄: 배열 A의 원소들이 공백으로 구분되어 입력 (원소 a < 10,000,000인 자연수)
  • 세 번째 줄: 배열 B의 원소들이 공백으로 구분되어 입력 (원소 b < 10,000,000인 자연수)

<출력 조건>

  • 최대 K번 바꿔치기 연산을 수행해서 가장 최대의 합을 갖는 A의 모든 원소 값의 합을 출력
입력 예시
5 3
1 2 5 4 3
5 5 6 6 5
출력 예시
26
import sys

n,k = map(int, sys.stdin.readline().rstrip().split())

array_a = list(map(int, sys.stdin.readline().rstrip().split()))
array_b = list(map(int, sys.stdin.readline().rstrip().split()))

array_a.sort()
array_b.sort(reverse=True)

for i in range(k):
    if array_a[i]<array_b[i]:
        array_a[i] = array_b[i]
    else:
        break

print(sum(array_a))

 

4. 국영수

https://www.acmicpc.net/problem/10825

 

10825번: 국영수

첫째 줄에 도현이네 반의 학생의 수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 한 줄에 하나씩 각 학생의 이름, 국어, 영어, 수학 점수가 공백으로 구분해 주어진다. 점수는 1보다 크거나 같고, 1

www.acmicpc.net

import sys

n = int(sys.stdin.readline().rstrip())

array = []
for _ in range(n):
    inputs = list(sys.stdin.readline().rstrip().split())
    array.append([inputs[0], int(inputs[1]), int(inputs[2]), int(inputs[3])])

array.sort(key = lambda x : (-x[1],x[2],-x[3],x[0]))

for i in array:
    print(i[0])

 

5. 안테나

https://www.acmicpc.net/problem/18310

 

18310번: 안테나

첫째 줄에 집의 수 N이 자연수로 주어진다. (1≤N≤200,000) 둘째 줄에 N채의 집에 위치가 공백을 기준으로 구분되어 1이상 100,000이하의 자연수로 주어진다.

www.acmicpc.net

import sys

n = int(sys.stdin.readline().rstrip())

array = list(map(int,sys.stdin.readline().rstrip().split()))

array.sort()
if len(array)%2 == 0:
    print(array[len(array)//2-1])
else:
    print(array[len(array)//2])

 

 

6. 실패율

https://programmers.co.kr/learn/courses/30/lessons/42889

 

코딩테스트 연습 - 실패율

실패율 슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스

programmers.co.kr

def solution(N, stages):
    stages.sort()
    total = [0]*(N+2)
    failure = []
    for i in stages:
        total[i] += 1
    
    for i in range(N,0,-1):
        total[i] = total[i] + total[i+1]

    stages = list(set(stages))
    
    for i in range(N,0,-1):
        notfinished = total[i] - total[i+1]
        if total[i] == 0:
            failure.append([i,0])
        else:
            failure.append([i,notfinished/total[i]])
        
    failure.sort(key = lambda x : x[1])

    answer = [i[0] for i in failure]
    answer.reverse()
    return answer

 

7. 카드 정렬하기

https://www.acmicpc.net/problem/1715

 

1715번: 카드 정렬하기

정렬된 두 묶음의 숫자 카드가 있다고 하자. 각 묶음의 카드의 수를 A, B라 하면 보통 두 묶음을 합쳐서 하나로 만드는 데에는 A+B 번의 비교를 해야 한다. 이를테면, 20장의 숫자 카드 묶음과 30장

www.acmicpc.net

import sys
import heapq

n = int(sys.stdin.readline().rstrip())

numbers = []

for _ in range(n):
    heapq.heappush(numbers, int(sys.stdin.readline().rstrip()))

total = 0
while len(numbers)>1:
    n1 = heapq.heappop(numbers)
    n2 = heapq.heappop(numbers)
    total += n1+n2
    heapq.heappush(numbers,n1+n2)

print(total)

'문제풀기 > 알고리즘' 카테고리의 다른 글

7. 최단 경로 (다익스트라, 플로이드 워셜 알고리즘)  (0) 2021.09.07
6. 다이나믹 프로그래밍  (0) 2021.09.01
3. DFS/BFS  (0) 2021.08.04
2. 구현  (0) 2021.07.28
1. 그리디 알고리즘  (0) 2021.07.22