You are looking for information, articles, knowledge about the topic nail salons open on sunday near me 퀵소트 자바 on Google, you do not find the information you need! Here are the best content compiled and compiled by the Chewathai27.com/to team, along with other related topics such as: 퀵소트 자바 퀵 정렬 피벗 가운데, 퀵정렬 피벗, 자바 퀵정렬 재귀, Quick_sort Python, 퀵 정렬 메모리, 퀵소트 병렬, 퀵 정렬 시뮬레이션, 퀵정렬 c언어
자바 [JAVA] – 퀵 정렬 (Quick Sort)
- Article author: st-lab.tistory.com
- Reviews from users: 11181
Ratings
- Top rated: 3.9
- Lowest rated: 1
- Summary of article content: Articles about 자바 [JAVA] – 퀵 정렬 (Quick Sort) 맞다. 퀵 정렬은 기본적으로 ‘분할 정복’ 알고리즘을 기반으로 정렬되는 방식이다. 다만, 병합정렬(Merge Sort)과 다른 점은 … …
- Most searched keywords: Whether you are looking for 자바 [JAVA] – 퀵 정렬 (Quick Sort) 맞다. 퀵 정렬은 기본적으로 ‘분할 정복’ 알고리즘을 기반으로 정렬되는 방식이다. 다만, 병합정렬(Merge Sort)과 다른 점은 … [정렬 알고리즘 모음] 더보기 1. 계수 정렬 (Counting Sort) 2. 선택 정렬 (Selection Sort) 3. 삽입 정렬 (Insertion Sort) 4. 거품 정렬 (Bubble Sort) 5. 셸 정렬 (Shell Sort) 6. 힙 정렬 (Heap Sort) 7. 합..
- Table of Contents:
댓글
이 글 공유하기
다른 글
자바 [JAVA] – 팀 정렬 (Tim Sort)
자바 [JAVA] – 이진 삽입 정렬 (Binary Insertion Sort)
자바 [JAVA] – 합병정렬 병합정렬 (Merge Sort)
자바 [JAVA] – 힙 정렬 (Heap Sort)
티스토리툴바
![자바 [JAVA] - 퀵 정렬 (Quick Sort)](https://img1.daumcdn.net/thumb/R800x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlJI9K%2Fbtq5bfzMFNG%2FxPb9tEXqLUpcYHb6h1ofik%2Fimg.png)
QuickSort(퀵소트) – JAVA – 오늘코딩
- Article author: gwang920.github.io
- Reviews from users: 42821
Ratings
- Top rated: 4.1
- Lowest rated: 1
- Summary of article content: Articles about QuickSort(퀵소트) – JAVA – 오늘코딩 QuickSort의 개념을 간단하게 알아보고, java 로 구현해보자. 퀵 정렬이란?Permalink. 불안정 정렬(Unstable Sort); 분할 정복 알고리즘 중 하나; 매우 … …
- Most searched keywords: Whether you are looking for QuickSort(퀵소트) – JAVA – 오늘코딩 QuickSort의 개념을 간단하게 알아보고, java 로 구현해보자. 퀵 정렬이란?Permalink. 불안정 정렬(Unstable Sort); 분할 정복 알고리즘 중 하나; 매우 … QuickSort의 개념을 간단하게 알아보고, java로 구현해보자.
- Table of Contents:
Skip links
퀵 정렬이란
퀵 정렬 과정
시뮬레이션
시간복잡도(Time Complexity)
구현
OS 기술 면접 정리
SelectionSort(선택정렬) – JAVA
InsetionSort(삽입정렬) – JAVA
BubbleSort(버블정렬) – JAVA
![QuickSort(퀵소트) - JAVA - 오늘코딩](https://i0.wp.com/wpsites.net/wp-content/uploads/2014/06/icon-after-more-link.png)
[알고리즘] 퀵 정렬 – Quick Sort (Python, Java) | Engineering Blog by Dale Seo
- Article author: www.daleseo.com
- Reviews from users: 27432
Ratings
- Top rated: 4.6
- Lowest rated: 1
- Summary of article content: Articles about [알고리즘] 퀵 정렬 – Quick Sort (Python, Java) | Engineering Blog by Dale Seo [알고리즘] 퀵 정렬 – Quick Sort (Python, Java). 이번 포스팅에서는 가장 유명한 정렬 알고리즘 중 하나인 퀵 정렬(Quick Sort)에 대해서 알아보겠습니다. …
- Most searched keywords: Whether you are looking for [알고리즘] 퀵 정렬 – Quick Sort (Python, Java) | Engineering Blog by Dale Seo [알고리즘] 퀵 정렬 – Quick Sort (Python, Java). 이번 포스팅에서는 가장 유명한 정렬 알고리즘 중 하나인 퀵 정렬(Quick Sort)에 대해서 알아보겠습니다. Engineering Blog by Dale Seo
- Table of Contents:
기본 컨셉
특징
복잡도
구현
최적화
![[알고리즘] 퀵 정렬 - Quick Sort (Python, Java) | Engineering Blog by Dale Seo](https://res.cloudinary.com/daleseo/image/upload/v1648323273/daleseo/og.png)
[JAVA] 정렬(Sort) – 퀵정렬(Quick Sort), 계수 정렬(Count Sort)
- Article author: hongs-coding.tistory.com
- Reviews from users: 49456
Ratings
- Top rated: 4.2
- Lowest rated: 1
- Summary of article content: Articles about [JAVA] 정렬(Sort) – 퀵정렬(Quick Sort), 계수 정렬(Count Sort) 오른쪽에도 피벗을 설정하고 정렬을 진행합니다. 퀵정렬 구현. import java.util.Scanner; public QuickSort { public static vo quickSort(int[] … …
- Most searched keywords: Whether you are looking for [JAVA] 정렬(Sort) – 퀵정렬(Quick Sort), 계수 정렬(Count Sort) 오른쪽에도 피벗을 설정하고 정렬을 진행합니다. 퀵정렬 구현. import java.util.Scanner; public QuickSort { public static vo quickSort(int[] … 퀵정렬(Quick Sort) 퀵정렬은 삽입정렬, 선택정렬, 버블정렬보다 빠른 알고리즘으로 가장 많이 사용되는 알고리즘 입니다. 퀵정렬의 핵심은 기준 데이터를 설정하고 그 기준보다 큰 데이터와 작은 데이터의 위치를..
- Table of Contents:
CATEGORIES
[JAVA] 정렬(Sort) – 퀵정렬(Quick Sort) 계수 정렬(Count Sort)category%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%A0%95%EB%A0%AC
퀵정렬(Quick Sort)
퀵 정렬의 원리
퀵정렬 구현
계수 정렬(COUNT SORT)
계수 정렬 원리
계수 정렬 구현
티스토리툴바
![[JAVA] 정렬(Sort) - 퀵정렬(Quick Sort), 계수 정렬(Count Sort)](https://img1.daumcdn.net/thumb/R800x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB55cH%2FbtrbkWNwJMF%2FEsiX5oPFjHSD6FcsxKd8Q1%2Fimg.png)
[알고리즘] JAVA 퀵 정렬 (Quick Sort) 알고리즘
- Article author: itprogramming119.tistory.com
- Reviews from users: 29794
Ratings
- Top rated: 3.2
- Lowest rated: 1
- Summary of article content: Articles about [알고리즘] JAVA 퀵 정렬 (Quick Sort) 알고리즘 [알고리즘] JAVA 퀵 정렬 (Quick Sort) 알고리즘 · – 퀵 정렬은 피봇값을 기준으로 분할정복하는 알고리즘이다. · – 퀵 정렬의 평균 시간 복잡도는 O(N*logN) … …
- Most searched keywords: Whether you are looking for [알고리즘] JAVA 퀵 정렬 (Quick Sort) 알고리즘 [알고리즘] JAVA 퀵 정렬 (Quick Sort) 알고리즘 · – 퀵 정렬은 피봇값을 기준으로 분할정복하는 알고리즘이다. · – 퀵 정렬의 평균 시간 복잡도는 O(N*logN) … – 퀵 정렬은 피봇값을 기준으로 분할정복하는 알고리즘이다. – 퀵 정렬의 평균 시간 복잡도는 O(N*logN)이다. – 퀵 정렬의 최악 시간 복잡도는 O(N^2)이다. 문제 – 다음의 숫자들을 오름차순으로 정렬하는 프로..
- Table of Contents:
네비게이션
[알고리즘] JAVA 퀵 정렬 (Quick Sort) 알고리즘사이드바
검색
티스토리툴바
![[알고리즘] JAVA 퀵 정렬 (Quick Sort) 알고리즘](https://img1.daumcdn.net/thumb/R800x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnvY4W%2FbtqB0fVe8JO%2F6VsAw26VVrlJ7ObV0VOltK%2Fimg.png)
[알고리즘] 5. Quick Sort : 퀵 정렬 (Java)
- Article author: velog.io
- Reviews from users: 15451
Ratings
- Top rated: 3.7
- Lowest rated: 1
- Summary of article content: Articles about [알고리즘] 5. Quick Sort : 퀵 정렬 (Java) 퀵 정렬이란? … package algorithms.advancedsort; import java.util.ArrayList; import java.util.List; public QuickSort { public … …
- Most searched keywords: Whether you are looking for [알고리즘] 5. Quick Sort : 퀵 정렬 (Java) 퀵 정렬이란? … package algorithms.advancedsort; import java.util.ArrayList; import java.util.List; public QuickSort { public … 퀵 정렬이란?
- Table of Contents:
알고리즘
생각한 로직
코드 구현 (Java)
고민한 부분
![[알고리즘] 5. Quick Sort : 퀵 정렬 (Java)](https://images.velog.io/velog.png)
[Java] 정렬 알고리즘 (4) – 퀵 정렬 (Quick Sort)
- Article author: shrimp-burger.tistory.com
- Reviews from users: 18567
Ratings
- Top rated: 3.3
- Lowest rated: 1
- Summary of article content: Articles about [Java] 정렬 알고리즘 (4) – 퀵 정렬 (Quick Sort) import java.util.Arrays; public QuickSort { int[] array = new int[]{10, 2, 5, 6, 3, 73}; public vo sort() { quickSort(array, 0, … …
- Most searched keywords: Whether you are looking for [Java] 정렬 알고리즘 (4) – 퀵 정렬 (Quick Sort) import java.util.Arrays; public QuickSort { int[] array = new int[]{10, 2, 5, 6, 3, 73}; public vo sort() { quickSort(array, 0, … 퀵 정렬 (Quick Sort) 1. Pivot 값을 기준으로 왼쪽에서부터 검사해서 큰 값을, 오른쪽에서부터 작은 값을 발견한다. 2. 두 값을 바꾸기 연산하고, 같은 과정을 수행한다. 3. 큰 값의 index 와 작은 값의 index 가..
- Table of Contents:
태그
관련글
댓글0
공지사항
최근글
인기글
최근댓글
태그
전체 방문자
티스토리툴바
![[Java] 정렬 알고리즘 (4) - 퀵 정렬 (Quick Sort)](https://t1.daumcdn.net/tistory_admin/static/images/openGraph/opengraph.png)
퀵 정렬(Quick Sort) — 넌 잘하고 있어
- Article author: hahahoho5915.tistory.com
- Reviews from users: 41591
Ratings
- Top rated: 4.2
- Lowest rated: 1
- Summary of article content: Articles about 퀵 정렬(Quick Sort) — 넌 잘하고 있어 분할된 두 개의 리스트에 대해 재귀함수를 통해 이 과정을 반복한다. – 시간복잡도 : 최악 O(n^2), 평균 O(nlogn). JAVA 소스코드 … …
- Most searched keywords: Whether you are looking for 퀵 정렬(Quick Sort) — 넌 잘하고 있어 분할된 두 개의 리스트에 대해 재귀함수를 통해 이 과정을 반복한다. – 시간복잡도 : 최악 O(n^2), 평균 O(nlogn). JAVA 소스코드 … – 리스트 가운데서 하나의 원소를 고름(pivot 선정) – pivot 앞에는 pivot보다 작은 값이 오고, pivot 뒤에는 pivot보다 큰 값들이 오도록 리스트를 둘로 분할한다. – 분할된 두 개의 리스트에 대해 재귀함수를..
- Table of Contents:
블로그 메뉴
공지사항
인기 글
태그
최근 댓글
최근 글
티스토리
티스토리툴바
[자료구조] Quick Sort 개념 및 Java로 구현하기
- Article author: mishuni.tistory.com
- Reviews from users: 29675
Ratings
- Top rated: 4.9
- Lowest rated: 1
- Summary of article content: Articles about [자료구조] Quick Sort 개념 및 Java로 구현하기 퀵 정렬 (Quick Sort) :분할 정복 (Dive and Conquer)방식을 이용한 알고리즘. pivot point 라는 기준점을 설정하여 왼쪽은 이 값보다 작은 값, … …
- Most searched keywords: Whether you are looking for [자료구조] Quick Sort 개념 및 Java로 구현하기 퀵 정렬 (Quick Sort) :분할 정복 (Dive and Conquer)방식을 이용한 알고리즘. pivot point 라는 기준점을 설정하여 왼쪽은 이 값보다 작은 값, … 퀵 정렬 (Quick Sort) :분할 정복 (Divide and Conquer)방식을 이용한 알고리즘. pivot point 라는 기준점을 설정하여 왼쪽은 이 값보다 작은 값, 큰 값은 오른쪽으로 옮기면서 정렬을 진행, 따라서, 분할과 동시..
- Table of Contents:
퀵 정렬 (Quick Sort)
태그
티스토리툴바
![[자료구조] Quick Sort 개념 및 Java로 구현하기](https://t1.daumcdn.net/tistory_admin/static/images/openGraph/opengraph.png)
자바로 구현하는 퀵정렬 (Quick Sort) 알고리즘
- Article author: creatordev.tistory.com
- Reviews from users: 38999
Ratings
- Top rated: 4.5
- Lowest rated: 1
- Summary of article content: Articles about 자바로 구현하는 퀵정렬 (Quick Sort) 알고리즘 자바로 구현하는 퀵정렬 (Quick Sort) 알고리즘. [Noa] 2014. 9. 1. 19:27. 퀵 정렬(QuickSort) 이란? 분할 작업을 순환적으로 반복하면서 피봇의 왼쪽 왼쪽 부분 … …
- Most searched keywords: Whether you are looking for 자바로 구현하는 퀵정렬 (Quick Sort) 알고리즘 자바로 구현하는 퀵정렬 (Quick Sort) 알고리즘. [Noa] 2014. 9. 1. 19:27. 퀵 정렬(QuickSort) 이란? 분할 작업을 순환적으로 반복하면서 피봇의 왼쪽 왼쪽 부분 … 퀵 정렬(QuickSort) 이란? 분할 작업을 순환적으로 반복하면서 피봇의 왼쪽 왼쪽 부분 집합과 오른쪽 부분집합을 정렬 하는 방법 1. 전체원소 가운데 하나의 원소를 중심(Pivot)으로 2개의 부분 집합으로 분할 한..
- Table of Contents:
태그
‘프로그래밍자료구조알고리즘’ Related Articles
공지사항
최근 포스트
태그
검색
전체 방문자
티스토리툴바
See more articles in the same category here: Chewathai27.com/to/blog.
자바 [JAVA] – 퀵 정렬 (Quick Sort)
글 작성자: ST_
728×90
[정렬 알고리즘 모음]Quick Sort [퀵 정렬]
이 번에 다뤄볼 정렬은 퀵 정렬이다. 이름에서도 보이듯이 빠른(Quick) 정렬이다.
아마 다른 블로거 혹은 책에서 본 적이 있다면 느끼실 수 있겠지만, 퀵 정렬의 경우 그 구현방법이 정말 다양하다. 그 이유는 조금 뒤에 얘기해보도록 하겠다.
퀵 정렬의 메커니즘은 크게 다음과 같다.
하나의 리스트를 피벗(pivot)을 기준으로 두 개의 부분리스트로 나누어 하나는 피벗보다 작은 값들의 부분리스트, 다른 하나는 피벗보다 큰 값들의 부분리스트로 정렬한 다음, 각 부분리스트에 대해 다시 위 처럼 재귀적으로 수행하여 정렬하는 방법이다.
이 부분에서 만약 알고리즘에 대해 잘 알고있다면 ‘분할 정복(Divide and Conquer)’을 떠올릴 것이다.
맞다. 퀵 정렬은 기본적으로 ‘분할 정복’ 알고리즘을 기반으로 정렬되는 방식이다. 다만, 병합정렬(Merge Sort)과 다른 점은 병합정렬의 경우 하나의 리스트를 ‘절반’으로 나누어 분할 정복을 하고, 퀵 정렬(Quick Sort)의 경우 피벗(pivot)의 값에 따라 피벗보다 작은 값을 갖는 부분리스트와 피벗보다 큰 값을 갖는 부분리스트의 크기가 다를 수 있기 때문에 하나의 리스트에 대해 비균등하게 나뉜다는 점이 있다.
실제로도 정렬 방법에서 병합 정렬과 퀵 정렬은 많이 비교되곤 한다. (다만 일반적으로 병합정렬보다 퀵정렬이 빠르다.)
위 말만 들으면 잘 감이 안오실 것 같다.
구체적으로 알아보기에 앞서 미리 언급하자면 퀵 정렬은 데이터를 ‘비교’하면서 찾기 때문에 ‘비교 정렬’이며 정렬의 대상이 되는 데이터 외에 추가적인 공간을 필요로 하지 않는다. 그러므로 ‘제자리 정렬(in-place sort)이다.’
퀵 정렬은 병합정렬과는 다르게 하나의 피벗을 두고 두 개의 부분리스트를 만들 때 서로 떨어진 원소끼리 교환이 일어나기 때문에 불안정정렬(Unstable Sort) 알고리즘이기도 하다.
위 말만 듣는다면 이해하기는 어려울 테니 정렬 방법에 대해 구체적으로 알아보도록 하자.
정렬 방법
퀵 정렬의 전체적인 과정은 이렇다.
1. 피벗을 하나 선택한다.
2. 피벗을 기준으로 양쪽에서 피벗보다 큰 값, 혹은 작은 값을 찾는다. 왼쪽에서부터는 피벗보다 큰 값을 찾고, 오른쪽에서부터는 피벗보다 작은 값을 찾는다.
3. 양 방향에서 찾은 두 원소를 교환한다.
4. 왼쪽에서 탐색하는 위치와 오른쪽에서 탐색하는 위치가 엇갈리지 않을 때 까지 2번으로 돌아가 위 과정을 반복한다.
5. 엇갈린 기점을 기준으로 두 개의 부분리스트로 나누어 1번으로 돌아가 해당 부분리스트의 길이가 1이 아닐 때 까지 1번 과정을 반복한다. (Divide : 분할)
6. 인접한 부분리스트끼리 합친다. (Conqure : 정복)
위 6가지 과정에 의해 정렬되는 방식이다.
2~3번 과정의 경우 좀 더 구체적으로 말하자면 대부분의 구현은 현재 리스트의 양 끝에서 시작하여 왼쪽에서는 피벗보다 큰 값을 찾고, 오른쪽에서는 피벗보다 작은 값을 찾아 두 원소를 교환하는 방식이다. 그래야 피벗보다 작은 값은 왼쪽 부분에, 피벗보다 큰 값들은 오른쪽 부분에 치우치게 만들 수 있기 때문이다. 이를 흔히 호어(Hoare)방식이라고 한다.
피벗을 선택하는 과정은 여러 방법이 있는데, 대표적으로 세 가지가 있다.
현재 부분배열의 가장 왼쪽 원소가 피벗이 되는 방법, 중간 원소가 피벗이 되는 방법, 마지막 원소가 피벗이 되는 방법이다.
각각의 방식은 다음과 같다.
(길이가 너무 길어지기 때문에 더 보기로 해두겠다.)
[왼쪽 피벗 선택 방식] [오른쪽 피벗 선택 방식] [중간 피벗 선택 방식]위 3 가지 방식 모두 특징은 ‘피벗’을 하나 설정하고 피벗보다 작은 값들은 왼쪽에, 큰 값들은 오른쪽에 치중하도록 하는 것이다. 이 과정을 흔히 파티셔닝(Partitioning)이라고 한다.
그렇게 파티셔닝을 했다면 파티셔닝을 통해 배치 된 피벗의 위치를 기준으로 좌우 부분리스트로 나누어 각각의 리스트에 대해 재귀호출을 해주면 된다.
위 원리를 이해해야 앞으로 구현 할 dual-pivot quick sort(이중 피벗 퀵 정렬) 또한 이해하기가 쉬울 것이다.
그리고 혹시 몰라 움직이는 이미지로 이해할 수 있도록 이미지를 첨부했다.
전체적인 흐름을 보자면 다음과 같은 형태로 정렬 한다.
https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif https://upload.wikimedia.org/wikipedia/en/f/f7/Quick_sort_animation.gif
그 외에도 많은 참고 영상들이 있는데, 영상으로 보는 것이 더 쉬울 것 같아 같이 첨부하도록 하겠다.
(위 영상은 중간 요소를 피벗으로 설정하여 정렬하는 과정이다)
Quick Sort 구현하기
그러면 일단 구현을 해야 할 것이다.
퀵 정렬의 경우 결국 피벗을 어느 곳을 기준으로 잡을 것인지가 관건일 것이다. 이러한 부분 때문에, 여러 블로거 혹은 책들마다 구현 방식이 매우 상이하다는 것이다.
일단 가장 기본적인 퀵 정렬을 구현해보도록 하겠다. 이해하기 가장 쉬우면서 기본적인 방법은 왼쪽 피벗 선택 방식이다. 위에서 설명한 방식을 그대로 구현하면 된다. (자세한 건 주석과 함께 달아놓겠다.)
[왼쪽 피벗 선택 방식]public class QuickSort { public static void sort(int[] a) { l_pivot_sort(a, 0, a.length – 1); } /** * 왼쪽 피벗 선택 방식 * @param a 정렬할 배열 * @param lo 현재 부분배열의 왼쪽 * @param hi 현재 부분배열의 오른쪽 */ private static void l_pivot_sort(int[] a, int lo, int hi) { /* * lo가 hi보다 크거나 같다면 정렬 할 원소가 * 1개 이하이므로 정렬하지 않고 return한다. */ if(lo >= hi) { return; } /* * 피벗을 기준으로 요소들이 왼쪽과 오른쪽으로 약하게 정렬 된 상태로 * 만들어 준 뒤, 최종적으로 pivot의 위치를 얻는다. * * 그리고나서 해당 피벗을 기준으로 왼쪽 부분리스트와 오른쪽 부분리스트로 나누어 * 분할 정복을 해준다. * * [과정] * * Partitioning: * * a[left] left part right part * +———————————————————+ * | pivot | element <= pivot | element > pivot | * +———————————————————+ * * * result After Partitioning: * * left part a[lo] right part * +———————————————————+ * | element <= pivot | pivot | element > pivot | * +———————————————————+ * * * result : pivot = lo * * * Recursion: * * l_pivot_sort(a, lo, pivot – 1) l_pivot_sort(a, pivot + 1, hi) * * left part right part * +———————–+ +———————–+ * | element <= pivot | pivot | element > pivot | * +———————–+ +———————–+ * lo pivot – 1 pivot + 1 hi * */ int pivot = partition(a, lo, hi); l_pivot_sort(a, lo, pivot – 1); l_pivot_sort(a, pivot + 1, hi); } /** * pivot을 기준으로 파티션을 나누기 위한 약한 정렬 메소드 * * @param a 정렬 할 배열 * @param left 현재 배열의 가장 왼쪽 부분 * @param right 현재 배열의 가장 오른쪽 부분 * @return 최종적으로 위치한 피벗의 위치(lo)를 반환 */ private static int partition(int[] a, int left, int right) { int lo = left; int hi = right; int pivot = a[left]; // 부분리스트의 왼쪽 요소를 피벗으로 설정 // lo가 hi보다 작을 때 까지만 반복한다. while(lo < hi) { /* * hi가 lo보다 크면서, hi의 요소가 pivot보다 작거나 같은 원소를 * 찾을 떄 까지 hi를 감소시킨다. */ while(a[hi] > pivot && lo < hi) { hi--; } /* * hi가 lo보다 크면서, lo의 요소가 pivot보다 큰 원소를 * 찾을 떄 까지 lo를 증가시킨다. */ while(a[lo] <= pivot && lo < hi) { lo++; } // 교환 될 두 요소를 찾았으면 두 요소를 바꾼다. swap(a, lo, hi); } /* * 마지막으로 맨 처음 pivot으로 설정했던 위치(a[left])의 원소와 * lo가 가리키는 원소를 바꾼다. */ swap(a, left, lo); // 두 요소가 교환되었다면 피벗이었던 요소는 lo에 위치하므로 lo를 반환한다. return lo; } private static void swap(int[] a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } } 위와 같이 왼쪽 피벗을 선택해서 피벗보다 작은 요소들은 왼쪽에, 피벗보다 큰 요소들은 오른쪽에 치중시킨 뒤, 피벗의 위치를 최종적으로 들어갈 위치와 교환해준 뒤 그 위치를 반환하는 메소드가 partition메소드고, 반환 된 피벗의 위치를 기준으로 왼쪽과 오른쪽 부분리스트로 나누어 재귀적으로 분할정복을 해주는 것이다. 오른쪽 피벗 선택 방식 또한 위 방식에서 역순으로만 해주면 된다. (자세한 건 주석과 함께 달아놓겠다.) [오른쪽 피벗 선택 방식] public class QuickSort { public static void sort(int[] a) { r_pivot_sort(a, 0, a.length - 1); } /** * 오른쪽 피벗 선택 방식 * @param a 정렬할 배열 * @param lo 현재 부분배열의 왼쪽 * @param hi 현재 부분배열의 오른쪽 */ private static void r_pivot_sort(int[] a, int lo, int hi) { /* * lo가 hi보다 크거나 같다면 정렬 할 원소가 * 1개 이하이므로 정렬하지 않고 return한다. */ if(lo >= hi) { return; } /* * 피벗을 기준으로 요소들이 왼쪽과 오른쪽으로 약하게 정렬 된 상태로 * 만들어 준 뒤, 최종적으로 pivot의 위치를 얻는다. * * 그리고나서 해당 피벗을 기준으로 왼쪽 부분리스트와 오른쪽 부분리스트로 나누어 * 분할 정복을 해준다. * * [과정] * * Partitioning: * * left part right part a[right] * +———————————————————+ * | element < pivot | element >= pivot | pivot | * +———————————————————+ * * * result After Partitioning: * * left part a[hi] right part * +———————————————————+ * | element < pivot | pivot | element >= pivot | * +———————————————————+ * * * result : pivot = hi * * * Recursion: * * r_pivot_sort(a, lo, pivot – 1) r_pivot_sort(a, pivot + 1, hi) * * left part right part * +———————–+ +———————–+ * | element <= pivot | pivot | element > pivot | * +———————–+ +———————–+ * lo pivot – 1 pivot + 1 hi * */ int pivot = partition(a, lo, hi); r_pivot_sort(a, lo, pivot – 1); r_pivot_sort(a, pivot + 1, hi); } /** * pivot을 기준으로 파티션을 나누기 위한 약한 정렬 메소드 * * @param a 정렬 할 배열 * @param left 현재 배열의 가장 왼쪽 부분 * @param right 현재 배열의 가장 오른쪽 부분 * @return 최종적으로 위치한 피벗의 위치(lo)를 반환 */ private static int partition(int[] a, int left, int right) { int lo = left; int hi = right; int pivot = a[right]; // 부분리스트의 오른쪽 요소를 피벗으로 설정 // lo가 hi보다 작을 때 까지만 반복한다. while(lo < hi) { /* * hi가 lo보다 크면서, lo의 요소가 pivot보다 큰 원소를 * 찾을 떄 까지 lo를 증가시킨다. */ while(a[lo] < pivot && lo < hi) { lo++; } /* * hi가 lo보다 크면서, hi의 요소가 pivot보다 작거나 같은 원소를 * 찾을 떄 까지 hi를 감소시킨다. */ while(a[hi] >= pivot && lo < hi) { hi--; } // 교환 될 두 요소를 찾았으면 두 요소를 바꾼다. swap(a, lo, hi); } /* * 마지막으로 맨 처음 pivot으로 설정했던 위치(a[right])의 원소와 * hi가 가리키는 원소를 바꾼다. */ swap(a, right, hi); // 두 요소가 교환되었다면 피벗이었던 요소는 hi에 위치하므로 hi를 반환한다. return hi; } private static void swap(int[] a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } } 오른쪽 피벗 선택 방식도 그렇게 어려운 것은 없을 것이다. 왼쪽 피벗 선택 방식에서 순서와 부등호만 약간 수정해주면 된다. 마지막으로 중간 피벗 선택 방식인데, 이 부분은 약간 고려해야 할 것들이 있다. 일단 코드를 보면서 확인해보자. [중간 피벗 선택 방식] public class QuickSort { public static void sort(int[] a) { m_pivot_sort(a, 0, a.length - 1); } /** * 중간 피벗 선택 방식 * @param a 정렬할 배열 * @param lo 현재 부분배열의 왼쪽 * @param hi 현재 부분배열의 오른쪽 */ private static void m_pivot_sort(int[] a, int lo, int hi) { /* * lo가 hi보다 크거나 같다면 정렬 할 원소가 * 1개 이하이므로 정렬하지 않고 return한다. */ if(lo >= hi) { return; } /* * 피벗을 기준으로 요소들이 왼쪽과 오른쪽으로 약하게 정렬 된 상태로 * 만들어 준 뒤, 최종적으로 pivot의 위치를 얻는다. * * 그리고나서 해당 피벗을 기준으로 왼쪽 부분리스트와 오른쪽 부분리스트로 나누어 * 분할 정복을 해준다. * * [과정] * * Partitioning: * * left part a[(right + left)/2] right part * +———————————————————+ * | element < pivot | pivot | element >= pivot | * +———————————————————+ * * * result After Partitioning: * * left part a[hi] right part * +———————————————————+ * | element < pivot | pivot | element >= pivot | * +———————————————————+ * * * result : pivot = hi * * * Recursion: * * m_pivot_sort(a, lo, pivot) m_pivot_sort(a, pivot + 1, hi) * * left part right part * +———————–+ +———————–+ * | element <= pivot | | element > pivot | * +———————–+ +———————–+ * lo pivot pivot + 1 hi * */ int pivot = partition(a, lo, hi); m_pivot_sort(a, lo, pivot); m_pivot_sort(a, pivot + 1, hi); } /** * pivot을 기준으로 파티션을 나누기 위한 약한 정렬 메소드 * * @param a 정렬 할 배열 * @param left 현재 배열의 가장 왼쪽 부분 * @param right 현재 배열의 가장 오른쪽 부분 * @return 최종적으로 위치한 피벗의 위치(hi)를 반환 */ private static int partition(int[] a, int left, int right) { // lo와 hi는 각각 배열의 끝에서 1 벗어난 위치부터 시작한다. int lo = left – 1; int hi = right + 1; int pivot = a[(left + right) / 2]; // 부분리스트의 중간 요소를 피벗으로 설정 while(true) { /* * 1 증가시키고 난 뒤의 lo 위치의 요소가 pivot보다 큰 요소를 * 찾을 떄 까지 반복한다. */ do { lo++; } while(a[lo] < pivot); /* * 1 감소시키고 난 뒤의 hi 위치가 lo보다 크거나 같은 위치이면서 * hi위치의 요소가 pivot보다 작은 요소를 찾을 떄 까지 반복한다. */ do { hi--; } while(a[hi] > pivot && lo <= hi); /* * 만약 hi가 lo보다 크지 않다면(엇갈린다면) swap하지 않고 hi를 리턴한다. */ if(lo >= hi) { return hi; } // 교환 될 두 요소를 찾았으면 두 요소를 바꾼다. swap(a, lo, hi); } } private static void swap(int[] a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } }
중간 선택 방식은 조금은 복잡하다. 일단 중간 위치를 피벗으로 설정하게 되면 필자가 이미지로 첨부한 것에서도 볼 수 있듯이 hi가 가리키는 위치가 pivot의 위치보다 높으면서 hi가 가리키는 원소가 pivot보다 작은 경우가 생긴다.
그렇기 때문에 분할정복으로 재귀를 호출하더라도 파티셔닝을 통해 얻은 피벗까지 포함하여 부분리스트로 나누어야 한다.
실제로 이 방식은 큰 장점이 있어 위와 같은 방식의 피벗 선택을 많이 추천하곤 한다. 이 부분은 바로 다음 파트에서 설명하도록 하겠다.
퀵 정렬의 장점 및 단점
[장점]1. 특정 상태가 아닌 이상 평균 시간 복잡도는 NlogN이며, 다른 NlogN 알고리즘에 비해 대체적으로 속도가 매우 빠르다. 유사하게 NlogN 정렬 알고리즘 중 분할정복 방식인 merge sort에 비해 2~3배정도 빠르다. (정리 부분의 표 참고)
2. 추가적인 별도의 메모리를 필요로하지 않으며 재귀 호출 스택프레임에 의한 공간복잡도는 logN으로 메모리를 적게 소비한다.
[단점]1. 특정 조건하에 성능이 급격하게 떨어진다.
2. 재귀를 사용하기 때문에 재귀를 사용하지 못하는 환경일 경우 그 구현이 매우 복잡해진다.
위에서 시간 복잡도에 대해 O(NlogN) 으로 유지가 된다고 했다. 이는 합병(병합)정렬과 같은 이유이기 떄문에 표를 그대로 갖고오겠다.
왜 그러한 시간복잡도가 나오는지 한 번 같이 알아보도록 하자.
이상적으로 피벗이 중앙에 위치되어 리스트를 절반으로 나눈다고 할 때, N개의 데이터가 있는 리스트를 1개까지 쪼개어 트리로 나타내게 되면 이진트리(Binary Tree) 형태로 나온다는 것은 우리가 확인 할 수 있다.
N개 노드에 대한 이진트리의 높이(h)는 logN 인 것은 힙정렬에서 다뤘으므로 해당 링크를 참고하시길 바란다.
그러면 비교 및 정렬 과정을 생각해보아야 한다.
우리가 피벗을 잡고 피벗보다 큰 요소와 작은 요소를 각 끝단에서 시작하여 탐색하며 만족하지 못하는 경우 swap을 한다. 이는 현재 리스트의 요소들을 탐색하기 때문에 O(N)이다.
좀 더 자세하게 말하자면 i번 째 레벨에서 노드의 개수가 2i 개이고, 노드의 크기. 한 노드에 들어있는 원소 개수는 N/2i 개다.
이를 곱하면 한 레벨에서 비교작업에 대한 시간 복잡도는 O(2i × N/2i) 이고 이는 곧 O(N)이다.
그리고 O(N)의 비교작업을 트리의 높이인 logN -1 번 수행하므로 다음과 같이 표기할 수 있다.
O(N) × O(logN)
최종적으로 위를 계산하면 O(NlogN) 의 시간복잡도를 갖는 것을 알 수 있다.
위와 달리 반대로 최악의 경우도 있다. (퀵 정렬의 최대의 단점이다.)
바로 정렬 된 상태의 배열을 정렬하고자 할 때다.
간단한 예시로 왼쪽을 기준으로 피벗을 잡을 때를 생각해보자.
우리가 N개가 있는 리스트에 대해 가장 왼쪽 요소를 pivot을 잡고, pivot보다 작은 요소는 왼쪽에, 큰 요소는 오른쪽에 위치시켜 두 부분리스트로 나누게 된다. 이때 만약 pivot이 가장 작은 요소였다면 부분리스트는 어떻게 나눠질까?
왼쪽의 부분리스트는 없고, N-1개의 요소를 갖는 오른쪽 부분리스트만 생성될 것이다.
이를 반복적으로 재귀호출을 해본다고 해보자.
결과적으로 각 재귀 레벨에서 N개의 비교작업을 N번 재귀호출을 하기 때문에 O(N2) 의 최악의 성능을 내기도 한다.
마찬가지로 정렬할 리스트가 이미 내림차순으로 정렬되어있다면 위 트리에서 왼쪽 서브 트리로 치중되어버릴 것이다. 마찬가지로 O(N2) 의 시간복잡도를 갖게 된다.
이는 왼쪽 피벗을 선택하건, 오른쪽 피벗을 선택하건 위와같이 트리가 치중되는 건 마찬가지다. 이 때문에 만약 위 처럼 치중 될 경우 공간 복잡도 또한 O(N)으로 되어버린다.
그래서 중간 피벗이 선호되는 이유가 바로 이러한 이유 때문이다. 그러면 거의 정렬 된 배열이더라도 거의 중간지점에 가까운 위치에서 왼쪽 리스트와 오른쪽 리스트가 균형에 가까운 트리를 얻어낼 수 있기 때문이다. (물론 아예 불가능한 건 아니다.)
그러면 위와 같이 거의 정렬 된 상태의 배열에서 효과적으로 해결할 수 있다.
그럼 왜 다른 O(NlogN) 정렬 방법들에 비해 빠른가에 대해 궁금할 것이다. 만약 Shell Sort 가 다른 정렬 알고리즘에 비해 빠른편에 속하는 이유를 알고 있다면 좀 더 쉽게 이해 할 것이다.
기본적으로 Shell Sort나, Quick Sort는 정렬 방식이 ‘멀리 떨어진 요소와 교환’되는 정렬 방식이다. Shell Sort는 일정 간격을 두고 두 원소의 값을 비교하며 정렬하고, Quick Sort 또한 양 끝에서 피벗을 기준으로 피벗보다 작은 값을 갖는 위치에 있어야 할 원소가 피벗보다 크거나, 그 반대인 경우 서로 원소를 교환하는 방식이다.
이러한 방식은 해당 원소가 최종적으로 있어야 할 위치에 거의 근접하게 위치하도록 하기 때문에 효율이 좋은 것이다.
예로들어 9번째 원소가 3번째에 위치해야 한다고 할 때, 보통의 경우 9 → 8 → 7 → 6 → 5 → 4 → 3 이런식으로 자기가 현재 있는 위치와 바로 직전의 원소와 교환되면서 총 6번에 걸쳐 이동을 했었어야 한다면, Shell Sort나 Quick Sort의 경우 9 → 1 -> 4 -> 3 서로 떨어져있는 비연속적 원소가 교환되면서 원래 있어야 할 위치에 거의 근접하게 위치하도록 하기 때문에 빠른 정렬이 가능한 것이다.
대신에 위와 같이 정렬하는 경우 별도의 배열이나 플래그(flag)를 부여하지 않는한 대부분의 정렬의 경우 안정정렬은 안된다고 보면 된다.
정리
퀵 정렬의 경우 대부분의 정렬들에 비해 빠른 속도를 자랑하기 때문에 많이 다뤄지는 정렬이다. 또한 자바에서는 primitive 타입 배열들의 경우 앞으로 구현 할 dual-pivot quick sort 을 기본 정렬로 사용하고 있기 때문에 이 정렬의 기본 베이스인 quick sort를 이해하고 넘어가야 dual-pivot quick sort의 내용 또한 어렵지 않게 이해할 수 있을 것이다.
또한 여러분들이 시간이 된다면 위와 같이 거의 정렬 된 상태에서 성능이 매우 떨어진다는 점을 고려 할 때, 임계값을 설정하여 일정 크기 이하로 떨어질 경우 정렬 된 배열에서 좋은 성능을 발휘하는 Insertion sort(삽입 정렬)을 하도록 바꾸면 거의 정렬 된 배열에서도 어느정도 성능 하락을 방지할 수 있으니 이렇게 혼합하여 구현해보는 것도 추천 드린다.
각 정렬 알고리즘 별 시간 복잡도와 평균 정렬 속도는 다음과 같다.
보면 quick sort의 경우 매우 빠른 편인 것을 볼 수 있다. (참고로 중간 피벗 선택 방식이다.)
이번 퀵 정렬의 경우 분할정복 알고리즘을 알고있어야 하기 때문에 만약 이에 대한 기본 지식이 없을 경우 어려울 수 있는 정렬방식이다. 또한 피벗을 어떻게 선택하냐의 문제와 잘못된 참조가 있는지를 고려해야 하기 때문에 이전까지 구현했던 정렬들과 다르게 조금 복잡한 정렬 방법이기도 하다.
혹여 부족한 부분이 있거나 궁금한 점, 모르거나 이해가 가지 않는 부분이 있다면 언제든 댓글 달아주시면 감사하겠다.
그 외에 필자가 여러 타입들에 대해 구현 한 정렬 알고리즘 소스 코드들을 자세히 보고싶다면 아래 깃허브 repo에 업로드되어있으니 이를 참고하시길 바란다.
https://github.com/kdgyun/Sorting_Algorithm.git
QuickSort(퀵소트) – JAVA
public class qucikSort { private static void quickSort ( int [] arr , int start , int end ) { int part = partition ( arr , start , end ); if ( start < part - 1 ) quickSort ( arr , start , part - 1 ); if ( end > part ) quickSort ( arr , part , end ); } private static int partition ( int [] arr , int start , int end ) { int pivot = arr [( start + end )/ 2 ]; while ( start <= end ) { while ( arr [ start ]< pivot ) start ++; while ( arr [ end ]> pivot ) end –; if ( start <= end ) { swap ( arr , start , end ); start ++; end --; } } return start ; } private static void swap ( int [] arr , int start , int end ) { int tmp = arr [ start ]; arr [ start ]= arr [ end ]; arr [ end ]= tmp ; return ; } public static void main ( String [] args ) { int [] arr = { 7 , 4 , 2 , 8 , 3 , 5 , 1 , 6 , 10 , 9 }; quickSort ( arr , 0 , arr . length - 1 ); for ( int i = 0 ; i < arr . length ; i ++) { System . out . print ( arr [ i ]+ "," ); } } }
[알고리즘] 퀵 정렬 – Quick Sort (Python, Java)
이번 포스팅에서는 가장 유명한 정렬 알고리즘 중 하나인 퀵 정렬(Quick Sort)에 대해서 알아보겠습니다.
달레 코딩에서 코딩 테스트/인터뷰 준비에 좀 더 특화된 컨텐츠를 만나보세요! 📝
기본 컨셉
병합 정렬과 마찬가지로 퀵 정렬도 분할 정복 (Devide and Conquer) 기법과 재귀 알고리즘을 이용한 정렬 알고리즘입니다.
쉬운 이해를 위해서 다음과 같이 1 부터 7 까지 총 7개의 숫자가 들어있는 배열을 기준으로 설명하겠습니다.
[ 6 , 5 , 1 , 4 , 7 , 2 , 3 ]항상 정 가운데를 기준으로 분할을 하는 병합 정렬과 달리, 퀵 정렬은 흔히 피봇(pivot)이라고 불리는 임의의 기준값을 사용합니다. pivot 값을 선택하는데는 여러가지 방법이 있지만 여기서는 간단한 설명을 위해 정 중앙에 위치한 4 을 pivot으로 정하겠습니다. 그리고 다음과 같이 이 pivot 값을 기준으로 pivot보다 작은 값의 그룹과 pivot보다 큰 값의 그룹으로 나눕니다.
p [ 3 , 2 , 1 ] < 4 < [ 7 , 5 , 6 ] 위와 같이 pivot 값보다 작은 값들은 모두 왼편으로 몰고, 큰 값들은 모두 오른편으로 몰면 기준값은 정확히 정렬된 위치에 놓이게 됩니다. 또한 이런 방식으로 분할을 해놓으면 앞으로 더 이상 왼편에 있는 값들과 오른편에 있는 값들 간에는 비교를 할 필요가 없습니다. 따라서 반대편은 전혀 신경쓰지 않고 왼편이든 오른편이든 같은편 내의 값들 끼리만 비교 후 정렬을 할 수 있게 됩니다. 먼저 왼편을 동일한 방식으로 정렬해보도록 하겠습니다. 왼편의 정 가운데에 위치한 pivot 값인 2 보다 작은 값인 1 인 왼쪽에 큰 값인 3 은 오른쪽에 위치시켰습니다. 이제 양쪽 모두 값이 하나씩 밖에 없기 때문에 이로써 왼편의 정렬 작업은 완료되었습니다. p [ 1 ] < 2 < [ 3 ] 오른편도 동일한 방식으로 정렬해보겠습니다. 오른편의 pivot 값인 5 보다 작은 값은 없으므로 7 과 6 을 모두 오른편에 위치시켰습니다. p [ ] < 5 < [ 7 , 6 ] 오른편의 오른편(?)에는 값이 2개가 있기 때문에 추가 정렬이 필요합니다. 왼편에는 값이 없지만 오른편에는 여전히 두 개의 값이 있기 때문에, 동일한 방식의 정렬을 적용하겠습니다. p [ 6 ] < 7 < [ ] 마지막으로 지금까지 좌우로 분할했던 값들을 모두 합치보면 다음과 같이 정렬된 배열을 얻을 수 있습니다. [ 1 , 2 , 3 , 4 , 5 , 6 , 7 ] 지금까지 살펴본 것과 같이 퀵 정렬은 배열을 pivot 값 기준으로 더 작은 값과 큰 값으로 반복적으로 분할하여 정렬해나가는 방식을 취하고 있습니다. 특징 파이썬의 list.sort() 함수나 자바의 Arrays.sort() 처럼 프로그래밍 언어 차원에서 기본적으로 지원되는 내장 정렬 함수는 대부분은 퀵 정렬을 기본으로 합니다. 함수나 자바의 처럼 프로그래밍 언어 차원에서 기본적으로 지원되는 내장 정렬 함수는 대부분은 퀵 정렬을 기본으로 합니다. 일반적으로 원소의 개수가 적어질수록 나쁜 중간값이 선택될 확률이 높아지기 때문에, 원소의 개수에 따라 퀵 정렬에 다른 정렬을 혼합해서 쓰는 경우가 많습니다. 병합 정렬과 퀵 정렬은 분할 정복과 재귀 알고리즘을 사용한다는 측면에서는 유사해보이지만, 내부적으로 정렬을 하는 방식에서는 큰 차이가 있습니다. 병합 정렬은 항상 정 중앙을 기준으로 단순 분할 후 병합 시점에서 값의 비교 연산이 발생하는 반면, 퀵 정렬은 분할 시점부터 비교 연산이 일어나기 때문에 그 이후 병합에 들어가는 비용이 매우 적거나 구현 방법에 따라서 아예 병합을 하지 않을 수도 있습니다. 복잡도 쿽 정렬의 성능은 어떻게 pivot 값을 선택 선택하느냐에 크게 달라질 수 있습니다. 이상적인 경우에는 pivot 값을 기준으로 동일한 개수의 작은 값들과 큰 값들이 분할되어 병합 정렬과 마찬가지로 O(NlogN) 의 시간 복잡도를 가지게 됩니다. 의 시간 복잡도를 가지게 됩니다. 하지만 pivot 값을 기준으로 분할했을 때 값들이 한 편으로 크게 치우치게 되면, 퀵 정렬은 성능은 저하되게 되며, 최악의 경우 한 편으로만 모든 값이 몰리게 되어 O(N^2) 의 시간 복잡도를 보이게 됩니다. 의 시간 복잡도를 보이게 됩니다. 따라서 상용 코드에서는 중앙값(median)에 가까운 pivot 값을 선택할 수 있는 섬세한 전략이 요구되며, 배열의 첫값과 중앙값 그리고 마지막값 중에 크기가 중간인 값을 사용하는 방법이 많이 사용됩니다. 퀵 정렬은 공간 복잡도는 구현 방법에 따라 달라질 수 있는데, 입력 배열이 차지하는 메모리만을 사용하는 in-place sorting 방식으로 구현을 사용할 경우, O(1) 의 공간 복잡도를 가진 코드의 구현이 가능합니다. 구현 위에 설명드린 기본 컨셉을 그대로를 코드로 구현할 수 있습니다. 먼저 리스트의 정 가운데 있는 값을 pivot 값으로 선택하고, pivot 값보다 작은 값, 동일한 값 그리고 큰 값을 담아둘 3개의 리스트를 생성합니다. 그리고 반복문을 통해 각 값을 pivot과 비교 후에 해당하는 리스트에 추가시킵니다. 그 다음 작은 값과 큰 값을 담고 있는 배열을 대상으로 퀵 정렬 함수를 재귀적으로 호출합니다. 마지막으로 재귀 호출의 결과를 다시 크기 순으로 합치면 정렬된 리스트를 얻을 수 있습니다. Python 코드 def quick_sort ( arr ) : if len ( arr ) <= 1 : return arr pivot = arr [ len ( arr ) // 2 ] lesser_arr , equal_arr , greater_arr = [ ] , [ ] , [ ] for num in arr : if num < pivot : lesser_arr . append ( num ) elif num > pivot : greater_arr . append ( num ) else : equal_arr . append ( num ) return quick_sort ( lesser_arr ) + equal_arr + quick_sort ( greater_arr )
Java 코드
public class QuickSorter { public static List < Integer > quickSort ( List < Integer > list ) { if ( list . size ( ) <= 1 ) return list ; int pivot = list . get ( list . size ( ) / 2 ) ; List < Integer > lesserArr = new LinkedList < > ( ) ; List < Integer > equalArr = new LinkedList < > ( ) ; List < Integer > greaterArr = new LinkedList < > ( ) ; for ( int num : list ) { if ( num < pivot ) lesserArr . add ( num ) ; else if ( num > pivot ) greaterArr . add ( num ) ; else equalArr . add ( num ) ; } return Stream . of ( quickSort ( lesserArr ) , equalArr , quickSort ( greaterArr ) ) . flatMap ( Collection :: stream ) . collect ( Collectors . toList ( ) ) ; } }
최적화
위의 구현은 간결하고 이해하기 쉽지만 매번 재귀 호출될 때 마다 새로운 리스트를 생성하여 리턴하기 때문에 메모리 사용 측면에서 비효율적입니다. 큰 사이즈의 입력 데이터를 다뤄야하는 상용 코드에서는 이러한 단점은 치명적으로 작용할 수 있기 때문에 추가 메모리 사용이 적은 in-place 정렬이 선호됩니다.
처음부터 스스로 in-place 정렬을 구현하는 코드를 작성하기는 생각했던 것보다 쉽지 않을 수도 있습니다. 기존과 동일하게 값의 대소 비교를 위해서는 pivot 값을 사용하지만, 분할은 기준점은 pivot 값이 아닐 수도 있기 때문입니다. 왜냐하면, pivot 값을 기준으로 대소 비교를 했을 때 좌측과 우측에 여유 공간이 딱 맞는 경우가 드물기 때문입니다.
알고리즘
코드가 이해가 어려울 수도 있기 때문에 간단히 알고리즘을 설명하겠습니다. 메인 함수인 quick_sort() 는 크게 sort() 와 partition() 2개의 내부 함수로 나눠집니다. sort() 함수는 재귀 함수이며 정렬 범위를 시작 인덱스와 끝 인덱스로 인자로 받습니다. (둘다 inclusive) partition() 함수는 정렬 범위를 인자로 받으며 다음 로직을 따라서 좌우측의 값들을 정렬하고 분할 기준점의 인덱스를 리턴합니다. 이 분할 기준점(mid)는 sort() 를 재귀적으로 호출할 때 우측 리스트의 시작 인덱스로 사용됩니다.
리스트의 정 가운데 있는 값을 pivot 값을 선택합니다.
시작 인덱스(low)는 계속 증가 시키고, 끝 인덱스(high)는 계속 감소 시키기위한 while 루프를 두 인덱스가 서로 교차해서 지나칠 때까지 반복시킵니다. 시작 인덱스(low)가 가리키는 값과 pivot 값을 비교해서 더 작은 경우 반복해서 시작 인덱스 값을 증가시킵니다. (pivot 값보다 큰데 좌측에 있는 값을 찾기 위해) 끝 인덱스(high)가 가리키는 값과 pivot 값을 비교해서 더 작은 경우 반복해서 끝 인덱스 값을 감소시킵니다. (pivot 값보다 작은데 우측에 있는 값을 찾기 위해) 두 인덱스가 아직 서로 교차해서 지나치치 않았다면 시작 인덱스(low)가 가리키는 값과 끝 인덱스(high)가 가리키는 값을 상호 교대(swap) 시킵니다. (잘못된 위치에 있는 두 값의 위치를 바꾸기 위해) 상호 교대 후, 다음 값을 가리키기 위해 두 인덱스를 각자 진행 방향으로 한 칸씩 이동 시킵니다.
두 인덱스가 서로 교차해서 지나치게 되어 while 루프를 빠져나왔다면 다음 재귀 호출의 분할 기준점이될 시작 인덱스를 리턴합니다.
Python 코드
def quick_sort ( arr ) : def sort ( low , high ) : if high <= low : return mid = partition ( low , high ) sort ( low , mid - 1 ) sort ( mid , high ) def partition ( low , high ) : pivot = arr [ ( low + high ) // 2 ] while low <= high : while arr [ low ] < pivot : low += 1 while arr [ high ] > pivot : high -= 1 if low <= high : arr [ low ] , arr [ high ] = arr [ high ] , arr [ low ] low , high = low + 1 , high - 1 return low return sort ( 0 , len ( arr ) - 1 ) Java 코드
So you have finished reading the 퀵소트 자바 topic article, if you find this article useful, please share it. Thank you very much. See more: 퀵 정렬 피벗 가운데, 퀵정렬 피벗, 자바 퀵정렬 재귀, Quick_sort Python, 퀵 정렬 메모리, 퀵소트 병렬, 퀵 정렬 시뮬레이션, 퀵정렬 c언어