Java 프로그래머에게 필수 - 데이터 구조 및 알고리즘의 빠른 정렬 (1)

빠른 정렬의 기본 아이디어:

퀵 정렬 알고리즘의 문제에 관해서는 분할 정복 알고리즘을 구현하는 전형적인 예와도 동일할 수 있는데, 먼저 정렬되지 않은 시퀀스의 문자열을 참조 번호를 통해 왼쪽과 오른쪽 두 개의 시퀀스 S1, S2로 나누고, 여기서 S1의 요소는 참조 번호보다 큽니다. Small, S2의 요소는 모두 참조 번호보다 큽니다. 그런 다음 왼쪽 및 오른쪽 시퀀스를 다시 빠르게 정렬합니다. S1과 S2의 참조 번호를 마음대로 찾을 수 있습니다. 매번 가장 낮은 비트를 참조 번호로 설정할 필요는 없으며, 이렇게 서브 시퀀스(S1, S2의 서브 시퀀스 포함)에 퀵 정렬 알고리즘을 적용하면 최종 생성된 시퀀스는 자연스럽게 정렬된 시퀀스가 ​​됩니다.
분할 정복 알고리즘 아이디어의 단계를 사용하여 설명합니다.
1. 나누기 단계: 벤치마크 번호를 선택한 후, 벤치마크 번호보다 큰 부분과 벤치마크 번호보다 작은 부분이 되도록 시퀀스를 나눕니다.
2. 거버넌스 단계: 하위 시퀀스를 재귀적으로 정렬
3. 조합 단계: 이 알고리즘에는 이 단계가 없습니다.

추상적인 아이디어로는 퀵 정렬 알고리즘을 명확하게 이해할 수 없습니다. 퀵 정렬에 대한 명확한 이해를 돕기 위해 51CTO 포럼의 게시물에서 생생한 설명을 빌려 보겠습니다.

이제 10개의 숫자 "6 1 2 7 9 3 4 5 10 8"을 정렬한다고 가정합니다. 먼저, 이 시퀀스에서 기본 숫자로 사용할 숫자를 무작위로 찾습니다. 편의상 첫 번째 숫자 6을 기본 숫자로 둡니다. 다음으로, 다음 배열과 유사하게 기본 숫자보다 큰 모든 숫자를 6의 오른쪽에 배치하고 기본 숫자보다 작은 숫자를 6의 왼쪽에 배치해야 합니다.

3 1 2 5 4 6 9 7 10 8

초기 상태에서 숫자 6은 시퀀스의 위치 1에 있습니다. 우리의 목표는 6을 시퀀스의 중간 위치로 이동하는 것입니다. 이 위치가 k라고 가정해 보겠습니다. 이제 이 k를 구하고 k번째 위치를 분할점으로 삼아야 합니다. 왼쪽의 숫자는 모두 6보다 작거나 같고 오른쪽의 숫자는 모두 6보다 크거나 같습니다.
초기 시퀀스 "6 1 2 7 9 3 4 5 10 8"의 양쪽 끝에서 "프로빙"을 시작합니다. 먼저 오른쪽에서 왼쪽으로 6보다 작은 숫자를 찾은 다음 왼쪽에서 오른쪽으로 6보다 큰 숫자를 찾은 다음 서로 바꿉니다. 여기서는 두 개의 변수 i와 j를 사용하여 각각 시퀀스의 가장 왼쪽 부분과 가장 오른쪽 부분을 가리킬 수 있습니다. 우리는 이 두 변수에 "Sentinel i"와 "Sentinel j"라는 좋은 이름을 부여했습니다. 처음에는 센티넬 i가 시퀀스의 가장 왼쪽(예: i=1)을 가리키고 숫자 6을 가리키도록 합니다. 센티넬 j가 숫자를 가리키는 시퀀스의 가장 오른쪽 부분(즉 = 10)을 가리키도록 합니다.

여기에 사진 설명을 쓰세요

먼저 센티넬J(Sentinel J)가 파견되기 시작했다. 여기서 설정한 참조번호는 가장 왼쪽의 번호이기 때문에 센티넬j를 먼저 보내는 것이 매우 중요합니다(이유는 생각해주세요). Sentinel j는 6보다 작은 숫자를 찾아 멈출 때까지 왼쪽으로 한 단계씩(즉, j-) 이동합니다. 다음으로, Sentinel i는 6보다 큰 숫자를 찾아 멈출 때까지 오른쪽 단계(예: i++)로 이동합니다. 마침내 센트리 j는 5번 앞에 섰고, 센트리 i는 7번 앞에 멈췄습니다.

여기에 사진 설명을 쓰세요

이제 sentinel i와 sentinel j가 가리키는 요소의 값을 교환합니다. 교환 후 순서는 다음과 같습니다.

6 1 2 5 9 3 4 7 10 8
여기에 사진 설명을 쓰세요

이 시점에서 첫 번째 교환이 종료됩니다. 다음으로, 보초 j는 계속해서 왼쪽으로 이동합니다(친절한 알림, 보초 j는 매번 먼저 시작해야 합니다). 그는 4(기본수 6보다 작고 요구 사항을 충족함)를 찾은 후 중단했습니다. Sentinel i도 오른쪽으로 계속 이동하다가 9(기본수 6보다 크고 요구사항을 충족함)를 발견하고 멈췄습니다. 이때 다시 교환이 이루어지며, 교환 이후의 순서는 다음과 같다.

6 1 2 5 4 3 9 7 10 8

두 번째 교환이 끝났고 "탐색"이 계속됩니다. Sentinel j는 왼쪽으로 계속 이동하다가 3개(기본수 6보다 작고 요구사항을 충족함)를 찾은 후 멈췄습니다. 센티넬 난 계속 오른쪽으로 움직이고 있어, 아 안돼! 이때 센트리i와 센트리j가 만났고, 센트리i와 센트리j 둘 다 3으로 왔어요. 이 시점에서 "검색"이 종료됨을 나타냅니다. 기본 숫자 6과 3을 바꿉니다. 교환 후 순서는 다음과 같습니다.

3 1 2 5 4 6 9 7 10 8
여기에 사진 설명을 쓰세요

이 시점에서 첫 번째 "탐지" 단계는 사실상 끝났습니다. 이때, 나누는 기준수는 6을 사용하며, 6의 왼쪽에 있는 숫자는 모두 6보다 작거나 같고, 6의 오른쪽에 있는 숫자는 모두 6보다 크거나 같다. 지금까지의 과정을 되돌아보면 사실 센트리 j의 임무는 벤치마크 숫자보다 작은 숫자를 찾는 것이고, 센트리 i의 임무는 i와 j가 만날 때까지 벤치마크 숫자보다 큰 숫자를 찾는 것이다.
네, 설명이 끝났습니다. 이제 기본 번호 6이 반환되었으므로 시퀀스의 위치 6에 있습니다. 이 시점에서 우리는 원래 시퀀스를 분할점으로 6을 사용하여 두 개의 시퀀스로 분할했습니다. 왼쪽 시퀀스는 "3 1 2 5 4"이고 오른쪽 시퀀스는 "9 7 10 8"입니다. 다음으로 이 두 시퀀스를 별도로 처리해야 합니다. 왜냐하면 6의 왼쪽과 오른쪽의 순서가 여전히 매우 혼란스럽기 때문입니다. 하지만 문제가 되지 않습니다. 우리는 방법을 마스터했습니다. 다음으로, 6의 왼쪽과 오른쪽에 있는 시퀀스를 각각 처리하기 위해 지금 방법을 시뮬레이션하면 됩니다. 이제 먼저 6의 왼쪽 수열을 다루겠습니다.
왼쪽의 순서는 "3 1 2 5 4"입니다. 3을 기본 숫자로 하여 이 수열을 조정하여 3의 왼쪽에 있는 숫자는 모두 3보다 작거나 같고 3의 오른쪽에 있는 숫자는 모두 3보다 크거나 같도록 하세요. 좋아요, 글을 시작해 보겠습니다.
시뮬레이션이 올바른 경우 조정 후 시퀀스 순서는 다음과 같아야 합니다.

2 1 3 5 4

좋습니다. 이제 3이 다시 제자리로 돌아왔습니다. 다음으로, 3의 왼쪽에 있는 시퀀스 "2 1"과 오른쪽에 있는 시퀀스 "5 4"를 처리해야 합니다. 시퀀스 "2 1"은 2를 기준으로 조정됩니다. 처리 후 시퀀스는 "1 2"가 되고 2는 원래 위치로 돌아왔습니다. 시퀀스 "1"에는 숫자가 하나만 있으며 처리가 필요하지 않습니다. 이 시점에서 우리는 "2 1" 시퀀스를 완전히 처리하여 "1 2" 시퀀스를 얻었습니다. 시퀀스 "5 4"도 이러한 방식으로 처리되며 최종적으로 얻은 시퀀스는 다음과 같습니다.

1 2 3 4 5 6 9 7 10 8

시퀀스 "9 7 10 8"의 경우 새 하위 시퀀스를 분할할 수 없을 때까지 동일한 프로세스가 시뮬레이션됩니다. 결국 다음과 같은 시퀀스를 얻게 됩니다.

1 2 3 4 5 6 7 8 9 10

이 시점에서 정렬이 완전히 완료되었습니다. 주의 깊은 학생들은 빠른 정렬의 각 라운드가 실제로 이번 라운드의 벤치마크 숫자를 반환한다는 것을 발견했을 것입니다. 모든 숫자가 반환될 때까지 정렬은 끝났습니다. 다음의 압도적인 그림은 전체 알고리즘의 처리 과정을 설명합니다.

여기에 사진 설명을 쓰세요

추천

출처blog.csdn.net/NTSDB/article/details/52730753