1.再帰的なクイックソートの最適化
- 最初の最適化のアイデア:
1.まず、クイックソートは測定単位として1万などの大量のデータを対象とし、次にクイックソートの時間計算量と空間計算量が他のソートと比較して最適であり、次に少量のデータを対象とします。 10、100など、クイックソートは明らかに最善ではありません
。2。次に、ベンチマークを探した後、毎回クイックソートを実行します。これにより、データの順序がゆっくりとなります。今回は、低と高の間のデータがa未満の場合です。与えられた値たとえば、100の場合、挿入ソートとソートの間を呼び出すことができます。
public static void quick(int[] arr,int low,int high){
if(low > high){
return;
}
//第一个优化:当low,high之间的数据个数少于某一个范围,可以调用直接插入排序
if(high-low+1 < 100){
insertsort2(arr,low,high);
return;
}
}
//用来优化的的直接插入排序
public static void insertsort2(int[] arr,int low,int high){
for(int i = low+1;i <= high;i++) {
int tmp = arr[i];
int j = i-1;
for (;j >= low;j--){
if (arr[j] > tmp) {
arr[j+1] = arr[j];
}else{
break;
}
}
arr[j+1] = tmp;
}
}
- 2番目の最適化:
1.たとえば、データのグループ{1、2、3、4、5}の場合、左側に添え字0が付いたデータが参照として使用されるたびに、参照位置が決定されるたびに、極端な状況であり、データはすべて参照にありますベンチマークの位置を決定するために、誰もがそれが決定されるたびにベンチマークの左右にデータを持っているのは良いと思います、または極端なケースは整理されます、次に、両側が次の場合に良いことは明らかです
。2。したがって、各ベンチマークの両側にデータがあることを確認するために、毎回左端の低値と右端の高値を取得し、中央の添え字mid = (high + low)/2。これらの3つの添え字は、データの中央値に対応します。つまり、(arr [mid] <arr [low] <arr [high])であり、最後の低い添え字は中央値です。どちらに関係なくポジションはこれらの3つのポジションで見つかります。ローポジションのExchangeと比較するだけで、ローは常にこの中央値になります。
public static void quick(int[] arr,int low,int high){
if(low > high){
return;
}
}
//第二次优化,取三个数中位数,这样的基准就不会出现极端情况
mid(arr,low,high);
//par是基准
int par = parttion(arr,low,high);
quick(arr,low,par-1);
quick(arr,par+1,high);
}
//优化的取三个数字的中位数
public static void mid(int[] arr,int low,int high){
/*int mid = (high+low)/2;
if (arr[low] < arr[mid] && arr[mid] < arr[high]){
int tmp = arr[low];
arr[low] = arr[mid];
arr[mid] =tmp;
System.out.println(low);
}else if(arr[high] > arr[low] && arr[high] < arr[mid]) {
int tmp = arr[high];
arr[high] = arr[low];
arr[low] =tmp;
System.out.println(low);
}else{
System.out.println(low);
return;
}*/
int mid = (low+high)/2;
//array[mid] < array[low] < array[high]
if(arr[low] >= arr[high]) {
swap(arr,low,high);
}
if(arr[low] <= arr[mid]) {
swap(arr,low,mid);
}
if(arr[mid] >= arr[high]) {
swap(arr,low,mid);
}
}
- 両方の最適化のすべてのコード
public static void quicksort(int[] arr){
quick(arr,0,arr.length-1);
}
//
public static void quick(int[] arr,int low,int high){
if(low > high){
return;
}
//第一个优化:当low,high之间的数据个数少于某一个范围,可以调用直接插入排序
if(high-low+1 < 100){
insertsort2(arr,low,high);
return;
}
//第二次优化,取三个数中位数,这样的基准就不会出现极端情况
mid(arr,low,high);
//par是基准
int par = parttion(arr,low,high);
quick(arr,low,par-1);
quick(arr,par+1,high);
}
//用来优化的的直接插入排序
public static void insertsort2(int[] arr,int low,int high){
for(int i = low+1;i <= high;i++) {
int tmp = arr[i];
int j = i-1;
for (;j >= low;j--){
if (arr[j] > tmp) {
arr[j+1] = arr[j];
}else{
break;
}
}
arr[j+1] = tmp;
}
}
//优化的取三个数字的中位数
public static void mid(int[] arr,int low,int high){
/*int mid = (high+low)/2;
if (arr[low] < arr[mid] && arr[mid] < arr[high]){
int tmp = arr[low];
arr[low] = arr[mid];
arr[mid] =tmp;
System.out.println(low);
}else if(arr[high] > arr[low] && arr[high] < arr[mid]) {
int tmp = arr[high];
arr[high] = arr[low];
arr[low] =tmp;
System.out.println(low);
}else{
System.out.println(low);
return;
}*/
int mid = (low+high)/2;
//array[mid] < array[low] < array[high]
if(arr[low] >= arr[high]) {
swap(arr,low,high);
}
if(arr[low] <= arr[mid]) {
swap(arr,low,mid);
}
if(arr[mid] >= arr[high]) {
swap(arr,low,mid);
}
}
public static void swap(int[] arr,int low,int high){
int tmp = arr[low];
arr[low] = arr[high];
arr[high] = tmp;
}
//划分函数
public static int parttion(int[] arr,int start,int end){
//申请一个tmp空间用来放基准的值(基准一般选择的是边上的值)
int tmp = arr[start];//这里取的是最左边的值,那么一会就先从最右边开始找[end]位置
while(start < end){
while(start < end && arr[end] >= tmp){
end--;
}
//到了这里,从while出来有俩种情况,不满足start < end或者不满足arr[end] >= tmp
if (start >= end){
//arr[start] = tmp;
break;//直接退出,因为已经遍历完毕
}else{
//else就是arr[end] < tmp
arr[start] = arr[end];
}
while(start < end && arr[start] <= tmp){
start++;
}
//到了这里,同样从while出来有俩种情况,不满足start < end或者不满足arr[start] <= tmp
if (start > end){
//arr[end] = tmp;
break;
}else{
arr[end] = arr[start];
}
}
arr[start] = tmp;
return start;//返回基准移动到位置,这样基准的左边全小于它,右边全都大于它
}
2.非再帰的クイックソート
- アイデア:
1.スタックを使用して非再帰的な高速ソートを実現します
。2。次のデータセットでは、参照位置が初めて見つかった後、赤い四角(添え字番号5)があり
ます。3。再帰用。 、左側を残しておく必要がありますか?右側の基準位置と同じ基準位置の決定を実行します。次に、非再帰の場合、左側のデータの下位(No. 0)添え字を最初にスタックに入れ、ハイ(No. 4)をスタックに入れ、新しいロー(6)をスタックに押し込み、新しいハイ(9)をスタックに押し込みます
。4。次に、最初にスタックが空かどうかを判断します。空でない場合は、空の場合、スタックの一番上を高に、2番目を低に割り当て、位置を見つけます。次に、スタックに押し込み、スタックから飛び出し、スタックが空になるまで配置します。
//非递归实现快速排序(需要一个栈)
public static void quicksort1(int[] arr){
Norquick(arr,0,arr.length-1);
}
//非递归实现快速排序
public static void Norquick(int[] arr,int low,int high){
Stack<Integer> stack = new Stack<>();
int par = parttion(arr,low,high);
if (par > low+1){
stack.push(low);
stack.push(par-1);
}
if (par < high-1){
stack.push(par+1);
stack.push(high);
}
while(!stack.empty()){
int end = stack.pop();
int start = stack.pop();
par = parttion(arr,start,end);
if (par > start+1){
stack.push(start);
stack.push(par-1);
}
if (par < end-1){
stack.push(par+1);
stack.push(end);
}
}
}
3.マージソート
- 次の図に示すように、最初に分割してからマージします。
//归并排序
public static void mergesort(int[] arr){
mergeSortInternal(arr,0,arr.length-1);
}
//分割
public static void mergeSortInternal(int[] arr,int low,int high){
if(low >= high){
return;
}
int mid = (low + high)/2;
mergeSortInternal(arr,low,mid);
mergeSortInternal(arr,mid+1,high);
//分割完成,就开始合并
merge(arr,low,high,mid);
}
//合并方法
public static void merge(int[] arr,int low,int high,int mid){
int s1 = low;
int s2 = mid+1;
int[] tmp = new int[high-low+1];//临时存放合并后的数据
//开始合并
int k = 0;//tmp开始下标
while(s1 <= mid && s2 <= high){
//俩个段都有数据
if (arr[s1] <= arr[s2]){
tmp[k++] = arr[s1++];
}else{
tmp[k++] = arr[s2++];
}
}
//第一个归并段还有很多数据
while(s1 <= mid){
//第一个还有若干个数据
tmp[k++] = arr[s1++];
}
while(s2 <= high){
//第二个还有若干个数据
tmp[k++] = arr[s2++];
}
//此时就有序了
for (int i = 0;i < tmp.length;i++){
arr[low+i] = tmp[i];//将临时存放好的数据给人家放回去;
}
}