리스트(list)
관련 내용
리스트(list)
지금까지 우리는 변수 하나에 숫자 하나, 변수 하나에 문자열 하나 이렇게 값들을 저장했습니다. 그런데 만약 우리가 변수 100,000개를 설정해야 풀 수 있는 문제가 있으면 어떻게 해야 할까요? 이런 값들을 변수 하나에 저장해서 풀 수는 없을까요? 그래서 이번에 소개할 내용은 파이썬에서 제일 중요한 자료구조인 리스트입니다.
C/C++을 배우신 분들은 배열이나 c++의 std::vector라는 말이 더 익숙하실 것 같네요. 리스트는 vector쪽에 더 가깝습니다.
리스트는 대괄호('['. ']')로 감싸져 있는 자료구조입니다. 혹은 list()로 선언해서 안에 입력값을 넣어도 됩니다.
안에 내용물은 요소 또는 원소라고 부르고 있습니다. 둘 다 혼용해서 사용하긴 하는데 여기에서는 헷갈릴 수도 있으니 요소라고 말하겠습니다. 요소에는 어떤 데이터 타입이 들어와도 상관없습니다. 각 요소끼리 데이터 타입이 달라도 괜찮습니다.
문자열을 입력받아서 list로 바꾼다면 한 글자씩 떼어져서 리스트에 저장을 할 수 있습니다.
문자나 숫자 여러 개의 값을 리스트로 입력받을 때는 평소에 우리가 썼던 입력받는 함수에 list()를 씌워주면 됩니다.
참고로 아래와 같이 list안에 list가 있을 경우
이렇게 생긴 리스트를 2차원 리스트라고 부르고 있습니다. 이외에도 리스트가 몇 개 겹쳐지냐에 따라 3차원 리스트, 4차원 리스트, ... 라고 부르고 있습니다.
이것을 이용한 문제는 좀 어렵기 때문에 나중에 따로 문제들을 모아서 해결할 예정입니다. 그때 가서 다시 설명할 예정이니 이런 것이 있다는 것만 알아두시면 됩니다.
이제 리스트가 가진 다양한 기능들에서 배워보겠습니다. 인덱싱, 슬라이싱, 리스트의 연산은 이전에 배운 문자열에서 인덱싱, 슬라이싱, 문자의 연산과 사용 방법이 같습니다. 혹시 문자열에서 제대로 이해하지 못한 분들을 위해 다시 똑같이 설명해두었습니다.
인덱싱(indexing)
indexing이라는 단어 의미는 가르키다라는 뜻을 가지고 있습니다. 리스트에서는 요소의 값을 가리킨다고 할 수 있습니다.
이 말은 아래 코드 같이 a = [1, 2, 3]일 때 a[0] = 1, a[1] = 2, a[2] = 3으로 요소 하나를 콕 집어서 가르킬 수 있다는 소리입니다. index 값은 여기서 0, 1, 2입니다. for문처럼 0부터 시작합니다.
이것을 이용해서 바로 예제 문제를 풀어봅시다.
"(숫자) + (숫자) = (숫자)" 꼴로 입력이 주어지는데 주어진 덧셈식이 맞는 수식인지 출력하는 문제입니다.
우선 입력 값이 숫자와 문자가 섞여 있으므로 문자로 입력을 받은 다음, 숫자들은 int형으로 고쳐주면 됩니다.
숫자가 있는 index는 0, 2, 4이니까 D[0] = int(D[0]) 같이 int형으로 값을 바꾸어봅시다.
이제 D[0] + D[2] = D[4]가 맞는지 확인하고 출력하면 되겠네요. 이것은 if문으로 만들 수 있습니다.
또 다른 문제를 풀어봅시다.
영식 요금제, 민식 요금제 2개로 나누어지고, N개의 통화 시간이 주어질 때 어느 요금제가 더 싼지 알아보는 문제입니다.
일단 N이 주어지고 N개의 통화 시간을 입력 받는 것으로 코드를 구현해보면 아래와 같습니다.
이제 통화 시간에 따라 요금제를 구해야 하는데, 요금제 하나씩 비교하는 것은 for문으로 비교할 수 있습니다.
민 요금제는 30초마다 10원이 청구된다고 합니다. 그리고 문제를 읽어보니 30초를 다 쓰지 않아도 10원이 청구되는 것 같습니다. 1초만 통화해도 10원이 청구되는 것이네요.
영식 요금제는 60초마다 15원이 청구된다고 합니다. 이것도 민식 요금제와 마찬가지로 1초만 통화해도 15원이 청구되는 방식입니다.
각각 30초, 60초마다 금액이 증가하므로 이것을 몫과 나머지로 계산할 수 있습니다.
민식 요금제는 통화 시간을 30으로 나눈 몫 * 10에다가 10을 더해줍니다.
마찬가지로 영식 요금제도 통화 시간을 60으로 나눈 몫 * 15에다가 15를 더해줍니다.
이것을 for문 안에 코드로 적으면 됩니다.
풀어볼 문제
참고로 index 값이 음수여도 가능합니다. 음수면 맨 뒷자리부터 index가 시작합니다. -1이 제일 마지막 숫자입니다.
요소 추가(append) / 삭제(pop)
이번에는 리스트의 메소드인 append, pop을 이용하여 리스트 안에 요소를 추가하거나, 빼는 방법을 알아봅시다.
append
append는 리스트 오른쪽 끝에 요소를 추가하는 메소드입니다.
위와 같이 append(4)를 해주면 arr의 리스트 오른쪽 끝에 요소 4가 추가되는 것을 볼 수 있습니다.
append()에는 다양한 자료형을 넣어줘도 상관없습니다.
pop
pop은 리스트 오른쪽 끝에 있는 요소를 빼는 메소드입니다.
pop()은 괄호 안에 숫자를 넣어서 원하는 위치의 값을 삭제할 수도 있습니다. 이 기능을 사용하면 삭제된 index 기준으로 오른쪽에 있는 값들이 한 칸씩 왼쪽으로 당겨저서 index 값이 바뀌므로 주의해주세요.
참고로 arr.pop(x)은 빠지는 값을 저장하고 있습니다. 그래서 print()를 이용하여 출력할 수도 있습니다.
이제 문제를 풀어봅시다.
숫자를 7개 받는데 그중에서 홀수들의 합과 제일 작은 홀수의 값을 출력하거나, 홀수가 없으면 -1을 출력하라고 합니다.
숫자를 7번 받아야하니 이 부분은 for문으로 할 수 있고, 숫자들은 append로 값을 추가하여 리스트에 담아봅시다.
이제 값들을 for문으로 확인하여 홀수 일 때 숫자를 모두 더해주는 변수와 if문을 이용하여 제일 작은 홀수 값을 찾는 변수를 하나 만들어봅시다.
홀수와 짝수를 구별하는 방법은 숫자를 2로 나눠서 나머지가 0인지 1인지 확인해보면 됩니다.
이제 출력을 해봅시다. 출력은 홀수가 있냐 / 없냐로 sum odd와 min odd / -1로 나눠서 출력하면 될 것 같습니다. 홀수가 있냐 없냐는 min odd의 값이 101이냐 아니냐로 구하면 되겠네요. 101이 아니면 홀수가 있어서 값이 바뀌었다는 것이니까요.
또 다른 문제를 풀어봅시다.
제대로 입력된 괄호 문자열인지 확인하는 문제입니다.
모양이 바르게 구성된 괄호 문자열은 특징이 2가지 있습니다.
'(' 의 개수와 ')'의 개수가 똑같다.
입력받을 때 ')'의 개수가 '('의 개수를 넘어갈 수 없다.
1번의 경우에는 쉽게 이해를 하실 겁니다. (() 나 ())는 '('와 ')'의 개수가 다르니까 올바르지 않죠.
2번의 경우에는 예제 1번 첫 번째 값을 봅시다.
이런 값을 를 입력받을 때, ( → ( → ) → ) → ( → ) → ) 순으로 입력받을 수 있습니다.
두 번째 입력값까지 받으면 ( → (로 '('가 2개,가 나옵니다. 하지만 상관없습니다. 나중에 ')'가 2개 나오면 되니까요.
하지만 ( → ( 로 입력값이 끝나면 1번 규칙에 위배되어 제대로 입력되지 않은 괄호 문자열이 됩니다.
계속 이어서 봅시다. 마지막 입력을 받기 직전까지는 '('가 3개, ')'가 3개였지만 다음 입력받는 것이 ')' 입니다. 그러면 ')'가 4개가 되고, '('가 3개가 됩니다.
')'가 4개면 '('도 4개여야 쌍이 맞는데, 지금까지 '(' 입력받은 것은 3개라서 제대로 입력되지 않은 괄호 문자열이 됩니다.
그래서 올바른 괄호 문자열은 두 가지 특징을 이용하여 문제를 풀 수 있습니다.
일단 코드는 문자열을 입력받고 for문을 이용하여 규칙 1인 '('와 ')'의 개수를 구해봅시다.
여기서
위와 같이 규칙1에 해당하는 코드를 짤 수 있습니다. 이제 규칙2인 코드를 구해야 하는데 여기서 append와 pop을 이용합니다.
'('를 입력받는다면 일단 append로 넣어주고, ')'를 입력받는다면 리스트에 '('가 있을 경우 pop으로 빼야합니다.
만약 리스트 안에 '('가 없을 때는 규칙1 때문에 괄호의 개수를 계속 세야 하니까 break문 대신 규칙2가 적용되는지 확인하는 flag 라는 변수를 하나 새로 만들어줍니다. flag = True로 선언한 후에 for문안에서 ')'가 입력으로 들어왔는데 리스트가 '('가 없는 경우, 즉 리스트의 길이가 0일 때 flag = False로 변경합니다.
이제 규칙 1은 l = r인지 확인하면 되고, 규칙 2는 flag = True인지 확인해보고 출력하면 됩니다. 이것은 if / else문으로 만들 수 있습니다.
풀어볼 문제
일단 여기까지 읽고 다양한 출력 방법 - join으로 넘어가세요. 리스트를 출력하는 대표적인 방법이 join을 이용하는 방법이기 때문에 꼭 읽고 오셔야 나머지 리스트 문제를 풀 수 있습니다.
슬라이싱(slicing)
slicing은 자른다는 의미를 가지고 있습니다. 리스트에서의 슬라이싱은 리스트에서 원하는 범위만큼의 요소를 잘라서 사용하는 것이라고 생각하시면 편합니다.
위 코드와 같이 리스트 옆에 [start:end:step]를 작성하여 사용합니다. 이것은 for문의 range에 나오는 [start:end:step]과 똑같습니다. [start:end]로 값을 입력하면 start, start+1, start+2, ... , end -1 까지의 요소를 가져오는 것이죠. 여기서 step는 자동으로 1이라는 값을 가지고 있습니다.
위 arr 값에서 두 번째 값부터 네 번째 값까지 추출하여 x라는 변수에 저장한다고 해보면 코드는 아래와 같습니다.
이번엔 arr의 값에 홀수 번째 값만 추출해본다고 해봅시다. 그러면 1, 3, 5가 slice로 빼 올 수 있겠네요. 여기서는 2칸씩 건너 뛰어서 같은 홀수끼리만 가져와야 하니 step = 2를 사용하면 됩니다.
start와 end에 값이 들어가 있지 않으면 start = 0, end는 마지막 값의 index + 1이라고 자동으로 인식합니다.
for문처럼 step에도 음수 값이 들어갈 수 있습니다.
step에 음수 값이 들어가면 맨 마지막 값부터 시작하여 왼쪽으로 step 값만큼 건너뛴다고 생각하시면 됩니다.
그리고 end의 값이 리스트의 길이를 초과하여도 리스트의 길이까지만 뽑아옵니다.
문제를 읽고 예제를 살펴보면 맨 앞에 공백이 있다는 것을 알 수 있습니다. 그러므로 이 문제는 앞 내용이 'Simon says'라고 나오면 나머지 문장을 출력하면 됩니다.
S의 인덱스는 0이니 start = 0이고, 맨 뒷글자 s의 인덱스는 9이니 end = 10로 적으면 됩니다. 앞에 Simon says가 나올 때만 출력을 하는 것이니 if문만 사용하면 되겠습니다.
이 문제는 ***를 입력받으면 종료되는 것이므로 while문을 사용해야하고, 문자열은 거꾸로 뒤집어서 출력해야 합니다. 거꾸로 출력하는 방법은 위에서 배운 대로 [::-1]을 사용하고, 문자열로 출력해야 하므로 join을 사용하여 문제를 출력하면 됩니다.
풀어볼 문제
리스트의 연산( + , * )
리스트는 더하기 문자인 + 로 리스트끼리 합칠 수 있고, *를 이용하여 똑같은 리스트를 몇 개 더 붙여서 이어줄 수 있습니다.
이 내용은 문자열에서 쉽게 배운 내용이니까 바로 예시를 통해 살펴보겠습니다.
이와 같이 리스트끼리 합쳐서 새로운 리스트를 만들 수 있습니다. 이 기능은 보통 index기능과 slice기능을 이용하여 새로운 리스트를 만들어 내는데 이용하고 있습니다.
두 번째로 리스트는 곱하기 문자인 *로 하나의 리스트를 곱하는 숫자만큼 복사하여 합치는 기능을 해줍니다. 이것도 바로 예시 코드를 봅시다.
이렇게 [1, 2, 3] 리스트를 3개 만들어서 전부 합치는 기능을 합니다.
오타 난 위치와 문자열을 입력받아서 오타 난 위치를 빼고 출력하라고 합니다.
일단 입력 값을 받고 오타 낸 위치와 문자열을 변수 2개로 나눠줍니다. '숫자 문자열'로 입력이 주어지므로 input().split()으로 입력값을 받은 후에 숫자는 int()함수를 이용하여 숫자로 만들어주면 됩니다.
이제 slice 기능을 이용하여 오타 난 부분을 제외하고, + 기능을 이용하여 리스트를 합치면 됩니다.
문제 예제 값을 살펴보니 첫 번째 글자는 1로 시작하는데 우리는 0부터 시작해야 하므로 오타 난 위치의 왼쪽 값은 idx - 1로 D[:idx-1]을 해줘야 오타의 왼쪽 부분 리스트를 추출할 수 있고, 오타 난 위치의 오른쪽 값은 idx로 D[idx:]를 해줘야 오타의 오른쪽 부분 리스트를 추출할 수 있습니다.
풀어볼 문제
그 외 메소드
pop과 append를 제외한 나머지 메소드가 어떤 것이 있는지 알아봅시다.
문제는 풀지 않을 것이고, 나중에 쓸 일이 있으면 그때 다시 언급하겠습니다.
extend
단어 뜻대로 확장하는 기능을 가지고 있습니다. 하지만 우리는 이미 리스트의 연산에서 +를 배웠습니다. 이것과 같은 역할을 하는 메소드입니다.
reverse
리스트를 뒤집어주는 메소드입니다. 하지만 우리는 이미 [::-1]로 뒤집는 방법을 배웠습니다.
index
index는 요소의 위치를 찾아주는 메소드입니다. 찾고자 하는 값이 리스트에 여러 개가 있다면 index의 값이 가장 작은 위치를 반환합니다. 찾고자 하는 값이 리스트에 없으면 오류가 나옵니다.
count
count는 찾고자 하는 요소가 리스트 안에 몇 개가 있는지 확인하는 메소드입니다. 여기서는 리스트 안에 찾고자 하는 값이 없으면 0을 출력합니다.
insert
insert는 원하는위치에 값을 삽입하는 기능입니다.
insert의 위치값(인덱싱)에 음수를 넣거나 리스트의 길이보다 긴 값을 넣을 수도 있습니다. 이때 음수는 리스트의 인덱싱과 같으며, 리스트의 길이보다 긴 값은 맨 오른쪽에 값을 추가합니다.
이 기능은 현재 값을 추가하고 싶은 자리에 있는 요소를 오른쪽으로 밀어버리고 값을 추가한다고 생각하시면 편합니다.
Last updated