빠른 정렬 - "데이터 구조 및 알고리즘"

안녕하세요 CSDN 여러분, 오늘은 다시 Xiaoyalan의 데이터 구조 및 알고리즘 열입니다. 빠른 정렬의 세계로 들어가 봅시다! ! !


빠른 정렬 

빠른 정렬은 1962년 Hoare가 제안한 이진 트리 구조 교환 정렬 방법입니다. 기본 아이디어는 정렬할 요소 시퀀스의 모든 요소를 ​​기준 값으로 취하고 정렬할 집합을 두 개의 하위 시퀀스로 나누는 것입니다. 정렬 코드에 따르면, 왼쪽 서브시퀀스의 모든 요소가 기준값보다 작고, 오른쪽 서브시퀀스의 모든 요소가 기준값보다 큰 경우, 가장 왼쪽 서브시퀀스의 모든 요소가 해당 위치에 정렬될 때까지 과정을 반복한다.

 hoare법

 단방향 애니메이션은 다음과 같습니다.

여기에 이미지 설명 삽입 

 

//hoare法
int PartSort1(int* a, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		//右边找小
		//要防止死循环和越界的问题
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}
		//左边找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
	return left;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort1(a, begin, end);
	//[begin,keyi-1] keyi [keyi+1,end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

 퀵 정렬 시도:

무효 TestQuickSort()
{     int a[] = { 2,1,4,3,6,5,7,9,8,10 };     PrintArray(a, sizeof(a) / sizeof(a[0]));     퀵소트(a, 0, sizeof(a) / sizeof(a[0]) - 1);     PrintArray(a, sizeof(a) / sizeof(a[0])); }




 

여기에 문제가 있습니다

왼쪽과 오른쪽이 만나는 위치가 keyi보다 작아야 하는지 어떻게 확인합니까?

왼쪽에 keyi를 만들고 오른쪽으로 먼저 이동하여 회의 위치 값이 keyi보다 작거나 keyi의 위치인지 확인합니다.

왼쪽과 오른쪽이 만나 두 가지 상황 외에는 왼쪽이 오른쪽과 오른쪽이 왼쪽과 만나

상황 1: 왼쪽이 오른쪽을 만나고 오른쪽이 정지하고 왼쪽이 걷는 것입니다. 오른쪽이 먼저 가고 오른쪽의 정지 위치는 keyi보다 작아야 하며, 만나는 위치는 오른쪽의 정지 위치로 keyi보다 작아야 합니다.

상황 2: 오른쪽이 왼쪽을 만나고 라운드에서 왼쪽이 움직이지 않고 오른쪽이 움직이고 왼쪽이 만나고 만나는 위치는 왼쪽의 위치이고 왼쪽의 위치는 키이의 위치이거나 몇 라운드 후에 교환, 왼쪽이 충족됩니다. 위치는 keyi보다 작아야 합니다.

오른쪽에서 keyi를 사용하고 왼쪽에서 먼저 이동하여 회의 위치 값이 keyi보다 큰지 확인합니다. 

 


 구덩이 파기

여기에 이미지 설명 삽입

 

//挖坑法
int PartSort2(int* a, int left, int right)
{
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		//右边找小
		//要防止死循环和越界的问题
		while (left < right && a[right] >= key)
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;
		//左边找大
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort2(a, begin, end);
	//[begin,keyi-1] keyi [keyi+1,end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

 

  


전방 및 후방 포인터 방식

 

 

 

 

//前后指针法
int PartSort3(int* a, int left, int right)
{
	int prev = left;
	int cur = left + 1;
	int keyi = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi])
		{
			++prev;
			Swap(&a[prev], &a[cur]);
		}
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	return keyi;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort3(a, begin, end);
	//[begin,keyi-1] keyi [keyi+1,end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}


 빠른 정렬 최적화 

  1. 세 개의 숫자는 중간 방법을 사용하여 키를 선택합니다.
  2. 작은 하위 범위로 재귀하는 경우 삽입 정렬 사용을 고려하십시오.

 

 

  • 퀵 정렬의 전반적인 종합적인 성능과 사용 시나리오는 상대적으로 좋기 때문에 감히 퀵 정렬이라고 부릅니다.
  • 시간 복잡도: O(N*logN)
  • 공간 복잡도: O(logN)
  • 안정성: 불안정

세 가지 숫자가 사용됩니다.

int GetMidIndex(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
}

완전히 최적화된 소스 코드:

int GetMidIndex(int* a, int left, int right)
{     int mid = (왼쪽 + 오른쪽) / 2;     if (a[left] < a[mid])     {         if (a[mid] < a[right])         {             return mid;         }         else if (a[왼쪽] < a[오른쪽])         {             오른쪽으로 돌아가기;         }         else         {             왼쪽으로 돌아가기;         } }     else     // a[왼쪽] > a[mid]     {         if (a[mid] > a[right])         {             return mid;         }         그렇지 않으면 (a[왼쪽] > a[오른쪽])























        {             오른쪽으로 돌아가기;         }         else         {             왼쪽으로 돌아가기;         } }     }








// 호어
// [left, right]
int PartSort1(int* a, int left, int right)
{     int midi = GetMidIndex(a, left, right);     Swap(&a[왼쪽], &a[midi]);

    int keyi = 왼쪽;
    while (left < right)
    {         // 右边找小         while (left < right && a[right] >= a[keyi])         {             --right;         }




        // 左边找大
        while (left < right && a[left] <= a[keyi])
        {             ++left;         }

        Swap(&a[왼쪽], &a[오른쪽]);
    }

    Swap(&a[keyi], &a[left]);

    왼쪽으로 돌아갑니다.
}


// 挖坑法
// [left, right]
int PartSort2(int* a, int left, int right)
{     int midi = GetMidIndex(a, left, right);     Swap(&a[왼쪽], &a[midi]);

    정수 키 = a[왼쪽];
    int 구멍 = 왼쪽;
    while (left < right)
    {         // 右边找小         while (left < right && a[right] >= key)         {             --right;         }




        a[구멍] = a[오른쪽];
        구멍 = 오른쪽;

        // 左边找大
        while (left < right && a[left] <= key)
        {             ++left;         }

        a[구멍] = a[왼쪽];
        구멍 = 왼쪽;
    }

    a[구멍] = 키;

    리턴홀;
}

// 前后指针法
// [left, right]
int PartSort3(int* a, int left, int right)
{     int midi = GetMidIndex(a, left, right);     Swap(&a[왼쪽], &a[midi]);

    int 이전 = 왼쪽;
    정수 현재 = 왼쪽 + 1;
    int keyi = 왼쪽;
    while (cur <= right)
    {         if (a[cur] < a[keyi] && ++prev != cur)         {             Swap(&a[prev], &a[cur]);         }



        ++커;
    }

    Swap(&a[prev], &a[keyi]);
    키이 = 이전;
    반환 키i;
}


무효 QuickSort(int* a, int 시작, int 끝)
{     if (시작 >= 끝)     {         반환;     }     int keyi = PartSort3(a, 시작, 종료);     //[begin,keyi-1] keyi [keyi+1,end]     QuickSort(a, begin, keyi - 1);     퀵소트(a, keyi + 1, end); }









 


빠른 정렬 비재귀

 

 

void QuickSortNonR(int* a, int begin, int end)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, end);
	StackPush(&st, begin);

	while (!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StackPop(&st);

		int right = StackTop(&st);
		StackPop(&st);

		int keyi = PartSort1(a, left, right);

		// [left, keyi-1] keyi [keyi+1, right]

		if (keyi + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, keyi + 1);
		}

		if (left < keyi - 1)
		{
			StackPush(&st, keyi - 1);
			StackPush(&st, left);
		}
	}

	StackDestroy(&st);
}

 비재귀적으로 퀵 정렬 테스트:

무효 TestQuickSortNonR()
{     int a[] = { 2,1,4,3,6,5,7,9,8,10 };     PrintArray(a, sizeof(a) / sizeof(a[0]));     QuickSortNonR(a, 0, sizeof(a) / sizeof(a[0]) - 1);     PrintArray(a, sizeof(a) / sizeof(a[0])); }




 

Stack.h 및 Stack.c의 내용:

 

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;//栈顶
	int capacity;//容量
}Stack;

// 初始化栈 
void StackInit(Stack* pst);

// 销毁栈 
void StackDestroy(Stack* pst);

// 入栈 
void StackPush(Stack* pst, STDataType x);

// 出栈 
void StackPop(Stack* pst);

// 获取栈顶元素 
STDataType StackTop(Stack* pst);

// 获取栈中有效元素个数 
int StackSize(Stack* pst);

// 检测栈是否为空 
bool StackEmpty(Stack* pst);




#include"Stack.h"
// 初始化栈 
void StackInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}
// 销毁栈 
void StackDestroy(Stack* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}
// 入栈 
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}
// 检测栈是否为空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
	//return pst->top==0;
}
// 出栈 
void StackPop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	pst->top--;
}
// 获取栈顶元素 
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	return pst->a[pst->top - 1];
}

// 获取栈中有效元素个数 
int StackSize(Stack* pst)
{
	assert(pst);
	return pst->top;
}


 자, Xiaoyalan의 퀵정렬은 여기까지 하고 데이터 구조와 알고리즘에 대해 계속 공부할 예정이니 병합정렬에 대한 Xiaoyalan의 다음 글도 기대해주세요! ! !

 

추천

출처blog.csdn.net/weixin_74957752/article/details/132031091