안녕하세요 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); }
빠른 정렬 최적화
- 세 개의 숫자는 중간 방법을 사용하여 키를 선택합니다.
- 작은 하위 범위로 재귀하는 경우 삽입 정렬 사용을 고려하십시오.
- 퀵 정렬의 전반적인 종합적인 성능과 사용 시나리오는 상대적으로 좋기 때문에 감히 퀵 정렬이라고 부릅니다.
- 시간 복잡도: 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의 다음 글도 기대해주세요! ! !