데이터 구조 | | 빠른 정렬, 양방향 빠른 정렬, 3 방향 빠른 정렬

빠른 정렬, 양방향 빠른 정렬, 3 방향 빠른 정렬


1. 빠른 정렬

1. 개념

빠른 정렬은 데이터를 정렬하기 위해 분할 및 정복이라는 아이디어를 사용합니다.

  1. 벤치 마크 선택

  2. 기준 값보다 작은 값은 기준 값의 왼쪽에 배치하고 나머지 (크거나 같음) 는 오른쪽에 배치합니다.

  3. 그런 다음 분할 된 간격의 길이가 1이 될 때까지 왼쪽과 오른쪽을 계속 나눕니다.

2. 시간 복잡성

빠른 정렬은 간격을 나눌 때 O (logN)이며 정렬하는 데 O (N)이 걸릴 때마다

따라서 빠른 정렬의 시간 복잡도는 O (NlogN) 이며, 이는 기준 값에 대해 간격을 대략 같은 부분으로 나눌 수있는 경우입니다.

데이터가 이미 정렬 된 경우 분할 정복 할 때마다 한쪽에는 데이터가없고 다른쪽에는 데이터가 있습니다. 이것은 버블 정렬과 동일하며 시간 복잡도는 O (N ^ 2)입니다.

요약하자면:

최고의 시간 복잡성 : O (NlogN)

최악의 시간 복잡성 : O (N ^ 2)

3. 적합한 시나리오

빠른 정렬은 데이터의 순서가 잘못된 상황에 적합합니다. 데이터가 정렬 된 경우 시간 복잡성이 O (N ^ 2) 일 가능성이 높으며 빠른 정렬의 장점을 반영하지 않습니다.

4. 최적화

  1. 작은 영역으로 나눌 경우 빠른 정렬 대신 삽입 정렬 사용

  2. 빠른 행의 재귀 구현을 위해 스택을 사용하여 비 재귀로 변경할 수 있습니다.

  3. 참조 값을 선택할 때마다 더 이상 첫 번째 또는 마지막 요소를 직접 선택하지 않습니다.

세 숫자의 중간 방법을 사용 하거나 벤치 마크 값을 무작위로 선택할 수 있습니다.

5. 실현

빠른 정렬의 분할 기능에 대한 세 가지 구현 방법이 있습니다.

  1. 좌우 포인터 방식

  2. 파기 방법

  3. 앞뒤 포인터 방식

여기에서 앞뒤 포인터의 방법을 사용하여

//前后指针法
int Partion(int array[], int left, int right)
{
    int cur = left;
    int prev = cur - 1;
    while (cur < right)
    {
        if (array[cur] <= array[right] && ++prev != cur)
        {
            std::swap(array[cur], array[prev]);
        }
        cur++;
    }
    std::swap(array[++prev], array[right]);
​
    return prev;
}
​
void QuickSort(int array[], int left, int right)
{
    if (array == nullptr)
    {
        return;
    }
​
    if (left < right)
    {
        int index = Partion(array, left, right);
        QuickSort(array, left, index - 1);
        QuickSort(array, index + 1, right);
    }
}

기타 구현 방법 및 최적화 된 코드 : https://github.com/YKitty/LinuxDir/blob/master/DS/Sort/Sort.c

2. 양방향 빠른 대기열

1. 이유

빠른 정렬을 위해 데이터 요소가 너무 많고 요소의 크기가 매우 가까우면 왼쪽과 오른쪽이 나누고 정복하면 한쪽에는 모든 데이터 가 발생 하고 다른쪽에는 데이터가 없어져 효율성과 시간 복잡성이 감소합니다. O (N ^ 2)가됩니다.

예 : 템플릿을 사용하여 병합 정렬 및 빠른 정렬 시간을 테스트하고, 1000000의 배열을 설정하고, 배열 요소가 0-10 사이에서 무작위로 선택된 다음 병합을 사용하는 데 0.290727 초, 빠른 정렬에 171.151 초가 걸립니다. 예, 없습니다. 잘못된. 퀵 정렬이 최적 일 때는 o (nlgn)이고, 이때 분명히 o (n ^ 2) 수준으로 저하됩니다. 왜 그런 겁니까?

위에서 작성한 빠른 정렬의 경우 벤치 마크 값보다 작거나 같은 모든 데이터가 왼쪽에 배치되고 벤치 마크보다 큰 데이터가 오른쪽에 배치되면 문제가 발생합니다. 조건이보다 크거나 같거나 같을 때와 상관없이 배열에 반복되는 요소가 너무 많거나 기준 값과 같은 요소가 너무 많으면 기준 값과 같은 부분이 항상 집중되어 있기 때문에 배열이 매우 불균형 한 두 부분으로 나뉩니다. 어레이 측면.

이때 양방향 고속 열 사용을 최적화하여 효율성 저하를 방지 할 수 있습니다.

양방향 빠른 행으로 해결 된 문제 :

참조 값과 동일한 모든 요소가 배열의 한쪽에 집중되지 않도록하십시오.

2. 생각

  1. 우리는 넣어 모두에게 요소 미만이다 어레이의 왼쪽 기준값 및 요소 댄 큰 배열의 우측 기준값

  2. 들어 왼쪽 A의 인덱스가 왼쪽 , 오른쪽에인덱스 권리를

  3. left가 기준 값보다 작 으면 기준 값 보다 크거나 같은 왼쪽을 만날 때까지 ++ 계속 뒤로 이동 하고, 오른쪽이 기준 값보다 크면 기준 값 보다 작거나 같은 오른쪽을 만날 때까지 계속 진행 합니다.

  4. 이때 왼쪽과 오른쪽 의 데이터 요소가 바뀐 다음 left ++, right--

  5. 왼쪽 == 오른쪽이 멈출 때까지 계속 2 번 실행

  6. 이 경우 교환 및 좌 위치 기준값 요소 는 기준값으로 반환 될 수 있습니다.

이러한 생각은 반복되는 데이터 요소가 많더라도 거의 똑같이 분리 될 수 있으며 한쪽에는 막대한 양의 데이터가 발생하지 않고 다른쪽에는 데이터가 없습니다.

3. 적합한 시나리오

너무 많은 반복되는 요소가 배열에있다 , 당신은, 정렬 빠른 사용 단지 빠른 정렬을 두 방법을 사용할 수 없습니다

4. 실현

구현할 때 변경 만하면되는 파티션 기능은

int Partion_Two(int array[], int left, int right)
{
    int l = left;
    int r = right - 1;
    while (1)
    {
        while (l <= r && array[l] < array[right])
        {
            l++;
        }
        while (l <= r && array[r] > array[right])
        {
            r--;
        }
        if (l > r)
        {
            break;
        }
        std::swap(array[r], array[l]);
    }
    std::swap(array[l], array[right]);
​
    return r;
}

참고 : 왼쪽보다 작지 않은 것을 찾고 오른쪽에서 크지 않은 것을 찾아 판단하고 교환하십시오.

3. 3 방향 빠른 대기열

1. 생각

배열을 기준 값보다 작고 기준 값보다 크고 기준 값과 같은 세 부분으로 나눕니다.

세 개의 첨자를 기록하십시오.

lt : 참조 값보다 작은 마지막 첨자

gt : 기준 값보다 큰 첫 번째 첨자

index : 순회되는 첨자

인덱스가 벤치 마크 값보다 작습니다 : 교환 인덱스 및 lt + 1, lt ++

지수가 기준 값보다 큽니다 : 교환 지수 및 gt-1, gt--

index는 벤치 마크 값과 같습니다. index ++

종료 조건 : index == gt

마지막 단계는 벤치 마크 값을 교환하는 것입니다.

스왑 (arr [lt], arr [오른쪽])

계속되는 간격 :

[왼쪽, lt-1]

[gt, 오른쪽]

 

2. 실현

void QuickSort_Three(int array[], int left, int right)
{
	if (array == nullptr)
	{
		return;
	}

	if (right <= left)
	{
		return;
	}

	int lt = left - 1;
	int gt = right;
	int index = left;
	int key = array[right];
	while (index < gt)
	{
		if (array[index] < key)
		{
			std::swap(array[index], array[lt + 1]);
			lt++;
			index++;
		}
		else if (array[index] > key)
		{
			std::swap(array[index], array[gt - 1]);
			gt--;
		}
		else
		{
			index++;
		}
	}
	std::swap(array[index], array[right]);
	QuickSort_Three(array, left, lt );
	QuickSort_Three(array, gt, right);
}

 

추천

출처blog.csdn.net/qq_40399012/article/details/89059038