2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Vorwort: Dieser Artikel gibt detaillierte Antworten auf einige Zweifel am frühen Lernen des Schnellsortierungsalgorithmus und stellt eine optimierte Version des grundlegenden Schnellsortierungsalgorithmus bereit.
Der Kern des Schnellsortierungsalgorithmus ist分治思想
Die Divide-and-Conquer-Strategie gliedert sich in die folgenden drei Schritte:
Auf den Schnellsortierungsalgorithmus angewendet:
Der Schlüsselcode für die schnelle Sortierung lautet如何根据基准元素划分数组区间(parttion)
Es gibt viele Zerlegungsmethoden, hier wird nur eine Methode bereitgestellt.挖坑法
Code:
class Solution {
public int[] sortArray(int[] nums) {
quick(nums, 0, nums.length - 1);
return nums;
}
private void quick(int[] arr, int start, int end) {
if(start >= end) return;// 递归结束条件
int pivot = parttion(arr, start, end);
// 递归解决子问题
quick(arr, start, pivot - 1);
quick(arr, pivot + 1, end);
}
// 挖坑法进行分解
private int parttion(int[] arr, int left, int right) {
int key = arr[left];
while(left < right) {
while(left < right && arr[right] >= key) right--;
arr[left] = arr[right];
while(left < right && arr[left] <= key) ++left;
arr[right] = arr[left];
}
arr[left] = key;
return left;
}
}
Ausführliche Antworten:
1.Warumstart>=end
Ist es die Endbedingung der Rekursion?
Zerlegen Sie das Unterproblem kontinuierlich. Die endgültige Größe des Unterproblems beträgt 1, das heißt, es gibt zu diesem Zeitpunkt keine Notwendigkeit, die Zerlegung fortzusetzen Zeit.
2. Warum sollte die Rechte zuerst gehen und nicht die Linke?
Es kommt darauf an, wer zuerst geht
基准元素的位置
,Im obigen Code ist das Basiselement (Schlüssel) das Element ganz links, wenn es zuerst verschoben wirdleft
, stößt zuerst auf ein Element, das größer als das Basiselement ist, und führt es dann ausarr[right] = arr[left]
,weil nicht gespeichert wurdearr[right]
, geht dieses Element verloren
Wenn Sie zuerst nach rechts gehen, trifft rechts zuerst auf ein Element, das kleiner als das Basiselement ist, und führt es dann ausarr[left]=arr[right]
Da sich die linke Seite zu diesem Zeitpunkt nicht bewegt hat, ist sie immer noch der Drehpunkt, aber der Drehpunkt wurde von uns mit dem Schlüssel gespeichert.
3.Warum ist arr[right]>=key?>Ist das nicht möglich?
Größer oder gleich dient hauptsächlich der Verarbeitung
重复元素问题
Es gibt zum Beispiel ein Array[6,6,6,6,6]
Wenn es > ist, bewegt sich der rechte Zeiger nicht und auch der linke Zeiger bewegt sich nicht und bleibt in einer Endlosschleife hängen.
4. Warum heißt es Grubengrabungsmethode?
Wenn der R-Zeiger auf den ersten trifft
arr[r] = arr[l]
Zu diesem Zeitpunkt ist die l-Position leer und bildet eine Grube.
Es gibt zwei Hauptoptimierungsrichtungen:
O(N*logN)
, also die beste Zeitkomplexität数组分三块
Wenn Sie gleichzeitig auf spezielle Testfälle (sequentielles Array oder umgekehrtes Array) stoßen, verringert sich die Zeitkomplexität aufO(N^2)
Zunächst basierend auf einer Frage (nach Farbe sortieren) verstehen, was ist数组分三块
analysieren
Code:
class Solution {
public void sortColors(int[] nums) {
// 分治 --
// 1.定义三指针
int i = 0;// 遍历整个数组
int l = -1, r = nums.length;
while(i < r) {
if(nums[i] == 0) swap(nums,++l,i++);
else if(nums[i] == 1) i++;
else swap(nums,--r,i);
}
return;
}
private void swap(int[] nums,int x,int y) {
int tmp = nums[x]; nums[x] = nums[y]; nums[y] = tmp;
}
}
l,r的起始位置
, das erste Element und das letzte Element gehören dazu未处理状态
, also kann `l, r nicht auf diese beiden Elemente zeigen und muss außerhalb des Intervalls liegen三个指针去分别维护四个区间
, eines der Intervalle ist未处理区间
Während sich der Cur-Zeiger weiter bewegt, werden alle Intervalle verarbeitet, und schließlich sind nur noch drei Intervalle vorhanden.Wenden Sie die oben genannten Ideen an快速排序的parttion中
, das Endergebnis wird in drei Intervalle unterteilt
Code:
class Solution {
// 快速排序优化版
// 分解--解决--合并
public int[] sortArray(int[] nums) {
qsort(nums, 0, nums.length - 1);
return nums;
}
private void qsort(int[] nums, int start, int end) {
if(start >= end) return;// 递归结束条件
// 分解
int pivot = nums[start];
int l = start - 1, r = end + 1, i = start;
while(i < r) {
int cur = nums[i];
if(cur < pivot) swap(nums, ++l, i++);
else if(cur == pivot) ++i;
else swap(nums, --r, i);
}
// [start, l] [l+1, r-1] [r, end]
// 递归解决
qsort(nums, start, l);
qsort(nums, r, end);
}
private void swap(int[] nums,int i, int j) {
int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;
}
}
2. Wählen Sie den Basiswert zufällig aus
Wählen Sie den Basiswert zufällig mithilfe von Zufallszahlen aus
int pivot = nums[start + new Random().nextInt(end - start + 1)];
// 起始位置 随机产生的偏移量
Komplett verbesserter Code:
class Solution {
// 快速排序优化版
// 分解--解决--合并
public int[] sortArray(int[] nums) {
qsort(nums, 0, nums.length - 1);
return nums;
}
private void qsort(int[] nums, int start, int end) {
if(start >= end) return;// 递归结束条件
// 分解
int pivot = nums[start + new Random().nextInt(end - start + 1)];
int l = start - 1, r = end + 1, i = start;
while(i < r) {
int cur = nums[i];
if(cur < pivot) swap(nums, ++l, i++);
else if(cur == pivot) ++i;
else swap(nums, --r, i);
}
// [start, l] [l+1, r-1] [r, end]
// 递归解决
qsort(nums, start, l);
qsort(nums, r, end);
}
private void swap(int[] nums,int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
Der Schnellauswahlalgorithmus ist基于快速排序优化版本
Eine zeitliche Komplexität vonO(N)
Der Auswahlalgorithmus ist das Nutzungsszenario第K大/前K大
Auswahlfragen wie
01. Das K-te größte Element im Array
Link: https://leetcode.cn/problems/kth-largest-element-in-an-array/
analysieren
sort
Sortieren Sie und kehren Sie dann zum K-ten Größten zurückO(N*logN)
O(logN)
Durch Rekursion generierte StapelaufrufeAls nächstes wird zur Implementierung ein Schnellauswahlalgorithmus verwendetO(N)
Die zeitliche Komplexität von
Code:
class Solution {
public int findKthLargest(int[] nums, int k) {
return qsort(nums, 0, nums.length - 1, k);
}
private int qsort(int[] nums, int start, int end, int k) {
if(start >= end) return nums[start];
int pivot = nums[start + new Random().nextInt(end - start + 1)];
// 数组分三块 <pivot ==pivot >pivot
int l = start - 1, r = end + 1, i = start;
while(i < r) {
if(nums[i] < pivot) swap(nums, ++l, i++);
else if(nums[i] == pivot) ++i;
else swap(nums, --r, i);
}
// [start, l] [l+1, r - 1] [r, end]
int c = end - r + 1, b = r - 1 - (l + 1) + 1, a = l - start + 1;
// 分情况讨论 进行选择
if(c >= k) return qsort(nums, r, end, k);
else if(b + c >= k) return pivot;
else return qsort(nums, start, l, k - b - c);// 找较小区间的第(k-b-c)大
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
}
}
O(N)