계수 정렬 알고리즘의 단계 안내
24767 단어 cppalgorithmspythoncomputerscience
계수 정렬은 바로 이러한 알고리즘 중의 하나로, 이 글은 그것의 실현에 차근차근 지침을 제공했다.
이 게시물은 최초로 나의 개인 홈페이지에 게재되었다website.너는 여기서 알고리즘을 직관적으로 해석한 슬라이드를 찾을 수 있다.
계수 정렬은 선결 조건을 적용해야 한다.n개의 입력 요소 중 하나는 유한한 범위 내의 정수 또는 이 범위 내의 정수 키가 있어야 한다.
현재 우리는 범위가 [0,k]라고 가정한다.
생각
그 기본 사상은 모든 키가 입력 수조에 나타나는 횟수(예를 들어 그 주파수)는 원소가 정렬 출력 수조에 있는 위치를 확정하는 데 사용할 수 있다는 것이다.
이게 어떻게 가능하지?
입력 요소의 키가 c, c<=k와 같다고 가정하십시오. 입력 그룹이 m개의 키
계산 주파수
모든 키의 주파수는 크기가 k+1과 같은 부기수 그룹을 사용하여 계산하고 0으로 초기화할 수 있습니다.입력 수조의 키가 [0,k] 범위 내에 있기 때문에 우리는 이를 교체하여 부기 수조의 인덱스로 사용할 수 있다.
교체 과정에서, 우리는 키 인덱스로 된 부기 요소를 추가했다.마지막으로 부기수 그룹의 모든 요소는 키의 주파수와 해당하는 인덱스를 저장합니다.
구성 정렬 출력
만약 추가 값이 없는 정수 키만 있다면, 정렬 출력을 구축하는 것은 매우 간단할 것이다.우리는 bookkeping 그룹을 교체할 수 있으며, 키의 출현 횟수마다 정렬된 출력을 채울 수 있다.
void CountingSort(vector<int>& keys) {
int max_key = *max_element(keys.begin(), keys.end());
vector<int> bookkeeping(max_key + 1);
for (int key : keys) bookkeeping[key]++;
for (int i = 0, k = 0; i <= max_key; ++i) {
for (int j = 0; j < bookkeeping[i]; ++j) {
keys[k++] = i;
}
}
}
입력 요소는 키 값이 맞는 일반적인 상황보다 더 복잡합니다.정렬 출력에서 각 입력 요소의 위치를 미리 계산할 필요가 있습니다.가장 간단한 방법은 다른 진열을 세우는 것이다.우리는 부기수 그룹을 사용해서 하나의 그룹을 만들 수 있으며, 이 그룹은 다음에 나타날 키가 정렬 출력에 있는 위치를 알려줄 것이다.
우리는 이 그룹을 nxt index라고 할 수 있다. 왜냐하면 i번째 요소는 다음 입력 요소가 정렬된 출력의 위치를 표시하기 때문이다.다음 색인 배열 작업을 구성하려면 다음 두 가지를 고려할 수 있습니다.
따라서 다음 인덱스 그룹의 i번째 요소는 부기 그룹에서 인덱스 i-1까지의 누적과 대응한다.부기와 다음 색인 그룹의 크기가 어떻게 같은지 주의하십시오.
마지막 단계는 다음 색인 그룹을 사용하여 정렬된 출력을 구축하는 것입니다.
next 인덱스로 정렬 출력 만들기
현재, 우리는 입력 그룹을 두루 훑어보고, 모든 키를 다음 인덱스 그룹의 인덱스로 사용해서 출력 그룹의 위치를 얻을 수 있습니다.
현재 요소가 출력 위치에 배치되면 키 인덱스의 다음 인덱스 요소가 증가합니다.이것은 항상 변하지 않습니다. 즉, nxt 인덱스의 i번째 요소는 다음 입력 요소가 정렬 출력에서 키 i의 위치를 나타냅니다.
일단 이 교체가 완성되면, 우리는 정렬된 출력 그룹을 입력 그룹으로 복사할 수 있다.
vector<pair<int, string>> CountingSort(vector<pair<int, string>>& items) {
int max_key = max_element(items.begin(), items.end(),
[](auto const& x, auto const& y) {
return x.first < y.first;
})->first;
vector<int> bookkeeping(max_key+1, 0);
//counter[i] corresponds to the number of entries with key equal to i
for (const auto& item : items) {
bookkeeping[item.first]++;
}
//nextIndex[i] corresponds to the number of entries with key less than i
vector<int> next_index(max_key+1, 0);
for (int i = 1; i < next_index.size(); ++i) {
next_index[i] = next_index[i-1] + bookkeeping[i-1];
}
vector<pair<int, string>> output(items.size());
for (const auto& item : items) output[next_index[item.first]++] = item;
return output;
}
공간 최적화
우리는 단독 넥스트 인덱스 그룹을 만드는 것이 아니라 메모리 공간을 절약하기 위해 부기 그룹을 직접 사용할 수 있다.이 점을 이해하기 위해 생각해 보자.
2️. 인덱스 [0, i] 범위 내의 부기 서브 그룹은 키 <=i 요소의 주파수를 포함합니다
따라서 인덱스 i에 대한 부기수 그룹의 누적과 정렬 출력의 마지막 입력 요소에 대응하는 위치 (1 추가).부기수 그룹의 누적과
다음에, 우리는 출력 그룹의 위치를 얻기 위해, 모든 키를 부기 그룹의 인덱스로 뒤로 교체해서 입력할 수 있다.
현재 요소를 출력 위치에 두기 전에 인덱스 키를 누르면 부기가 감소합니다.이렇게 하면 다음 인덱스의 i번째 요소는 정렬된 출력에서 마지막 입력 요소의 위치를 나타냅니다(1).
vector<pair<int, string>> CountingSort(vector<pair<int, string>>& items) {
int max_key = max_element(items.begin(), items.end(),
[](auto const& x, auto const& y) {
return x.first < y.first;
})->first;
vector<int> bookkeeping(max_key + 1, 0);
//count keys frequency
for (const auto& item : items) {
bookkeeping[item.first]++;
}
//at the end each element is the index of the last element with that key
std::partial_sum(bookkeeping.begin(), bookkeeping.end(), bookkeeping.begin());
vector<pair<int, string>> output(items.size());
//build sorted output iterating backward
for (auto it = items.crbegin(); it != items.crend(); ++it) {
output[--bookkeeping[it->first]] = *it;
}
return output;
}
범용 범위까지 확장
우리는 항상 모든 키가 정수이고 0에서 어떤 최대치 k 사이에 포함된다고 가정한다.
사실 이것은 전제가 아니다.우리는 계수 정렬을 임의의 정수 범위의 키에 적용할 수 있다.비결은 최소 원소를 찾아 최소 원소의 주파수를 0 인덱스에 저장하는 것이다.
등록 정보
계수 정렬의 중요한 특징은 안정적인 것이다. 같은 키를 가진 요소는 출력 그룹에서 입력 그룹과 같은 순서로 나타난다.
점근 분석에 대해 우리는 다음과 같이 생각한다.
파이썬 구현
def countingSort(input):
maxKey= max(input, key=lambda item:item[0])[0]
bookeepingLength = maxKey+1
bookeeping = [0] * bookeepingLength
# Count keys frequency
for item in input:
bookeeping[item[0]] += 1
# at the end each element is the index
# of the last element with that key
for i in range(1, bookeepingLength):
bookeeping[i] += bookeeping[i-1]
output = [0] * len(input)
# build sorted output iterating backward
i = len(input) - 1
while i >= 0:
item = input[i]
bookeeping[item[0]] -= 1
position = bookeeping[item[0]]
output[position] = item
i -= 1
return output
C# 구현
class CountingSort {
private static int[] PartialSum(int[] input)
{
for (int i = 1; i < input.Count(); i++)
{
input[i] = input[i] + input[i - 1];
}
return input;
}
public static (int, string)[] CountingSort((int, string)[] items)
{
int max_key = items.Max(t => t.Item1);
var bookkeeping = new int[max_key + 1];
//count keys frequency
foreach (var item in items) {
bookkeeping[item.Item1]++;
}
//at the end each element is the index of the last element with that key
bookkeeping = PartialSum(bookkeeping);
var output = new (int, string)[items.Length];
//build sorted output iterating backward
for (int i = items.Length - 1; i >= 0; i--) {
output[--bookkeeping[items[i].Item1]] = items[i];
}
return output;
}
}
결론
계수 정렬은 기능이 강하고 매우 효율적인 알고리즘이다.그것의 기본 사상은 매우 간단하지만, 실현하기에는 매우 까다로울 수 있으니 주의해야 한다.
이 글은 계수 정렬 알고리즘을 실현하는 단계별 지침을 제공했다.
이 알고리즘은 다양한 프로그래밍 언어(C++, C#, Python)에서 내 GitHub 저장소에서 얻을 수 있다.
만약 당신이 이 글을 좋아한다면, 나를 따라 매일 더 많은 관련 내용을 얻으세요!
Reference
이 문제에 관하여(계수 정렬 알고리즘의 단계 안내), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/francofernando/a-step-by-step-guide-to-the-counting-sort-algorithm-4pef텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)