トピック:
整数配列arrを指定します。配列内の各要素を、並べ替えられた番号に置き換えてください。
シリアル番号は、要素の大きさを表します。シリアル番号の規則は次のとおりです。
- シリアル番号は1から始まります。
要素が大きいほど、シーケンス番号は大きくなります。2つの要素が等しい場合、それらのシーケンス番号は同じです。
各番号のシリアル番号はできるだけ小さくする必要があります。 - 例1:
入力:arr = [40,10,20,30]
出力:[4,1,2,3]
説明:40は最大の要素です。10は最小の要素です。20は2番目に小さい数です。30は3番目に小さい数です。 - 例2:
入力:arr = [100,100,100]
出力:[1,1,1]
説明:すべての要素のシーケンス番号は同じです。 - 例3:
入力:arr = [37,12,28,9,100,56,80,5,12]
出力:[5,3,4,2,8,6,7,1,3]
問題解決のアイデア1:最初にタイトルを読んでください。これは非常に単純な並べ替えの問題だと思います。元の配列内の重複する要素を除外し、残りの要素を並べ替えてから、それぞれの元の配列をトラバースするだけです。ソートの要素リスト内の位置をシリアル番号の割り当てと比較して正しい答えを得ることができるので、私はこの考えに従ってアルゴリズムを書き始めました。
遭遇した問題1:このアルゴリズムは問題を解決するという明確な考えを持っていますが、アルゴリズムが非効率的で計算時間が長すぎるため、最後のテストケースでスタックし、最終的に失敗しました。
package 力扣;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class 数组序号转换 {
public static void main(String[] args) {
int[] arr = {
2,-11,24,15,26,-31,-48,-49,22,37,-36};
arrayRankTransform(arr);
}
public static int[] arrayRankTransform(int[] arr) {
ArrayList list = new ArrayList();
int s = arr.length;
//1.用于过滤重复数组元素
for (int i = 0; i < s; i++) {
if (!list.contains(arr[i])){
list.add(arr[i]);
}
}
int temp = list.size();
//2.转为数组之后进行排序
Object[] brr = list.toArray();
Arrays.sort(brr);
//3.对于原数组遍历寻找在排序位置的值然后标记
for (int i = 0; i < s; i++) {
for (int j = 0; j < temp; j++) {
if (arr[i] == (int)brr[j]){
arr[i] = j+1;
break;
}
}
}
//4.输出相应的值(提交代码时请删掉)
for (int i = 0; i < s; i++) {
System.out.print(arr[i]+",");
}
return arr;
}
}
問題解決のアイデア2:最初のアルゴリズムの経験を要約した後、問題を解決するために考え方を変える必要があると思うので、2次元配列を使用しての値とシリアル番号を記録できるかどうかを考えました。元の配列を個別に、最初に元の配列要素を使用して値のサイズが並べ替えられ、次に要素の値が現在の順序に置き換えられ、最後に元の配列値の元の順序が並べ替えられます。並べ替え列の要素を順番に取得し、最初の要素を最適化します。1つのアルゴリズムは、並べ替え後のトラバーサル検索の効率の低さを比較します。
遭遇した問題2:まず、このアルゴリズムでは、[]の空の配列のテスト値でスタックし、配列が範囲外であるためにエラーが報告されたため、長さを判断するために2つのケースを追加しました。配列ですが、最終的には、アルゴリズムがタイムアウトし、非効率的で失敗に終わったためです。
package 力扣;
import java.util.Arrays;
public class 数组序号转换优化 {
public static void main(String[] args) {
int[] arr = {
19,22,-47,24,-37};
arr = arrayRankTransform(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
public static int[] arrayRankTransform(int[] arr) {
int s = arr.length;
if (s != 0){
int[][] brr = new int[2][s];
int add = 1;
int i = 0,j = 0;
//1.用二维数组来存储一维数组,第二层表示序号
for (i = 0; i < s; i++) {
brr[0][i] = arr[i];
brr[1][i] = add++;
}
//比较数字
int temp = 0;
//下标记录
int flag = 0;
//2.先以原数组进行排序
for (i = 0; i < s-1; i++) {
temp = brr[0][i];
flag = i;
for (j = i; j < s; j++) {
if(brr[0][j] < temp){
temp = brr[0][j];
flag = j;
}
}
int temp1 = brr[0][i];
int temp2 = brr[1][i];
brr[0][i] = brr[0][flag];
brr[1][i] = brr[1][flag];
brr[0][flag] = temp1;
brr[1][flag] = temp2;
}
//3.进行位序替换
add = 1;
temp = brr[0][0];
brr[0][0] = add;
for (int k = 1; k < s; k++) {
if(brr[0][k] != temp){
temp = brr[0][k];
brr[0][k] = ++add;
}else{
brr[0][k] = add;
}
}
//4.再以序号原数组进行排序
for (i = 0; i < s-1; i++) {
temp = brr[1][i];
flag = i;
for (j = i; j < s; j++) {
if(brr[1][j] < temp){
temp = brr[1][j];
flag = j;
}
}
if(flag != i){
int temp1 = brr[0][i];
int temp2 = brr[1][i];
brr[0][i] = brr[0][flag];
brr[1][i] = brr[1][flag];
brr[0][flag] = temp1;
brr[1][flag] = temp2;
}
}
return brr[0];
}
else{
return arr;
}
}
}
問題解決のアイデア3:最後に、問題解決を閲覧するコメント領域で、超最適化された問題解決のアイデアを見つけました。元の著者は、次のように彼のアイデアを説明しました:詳細なコードの説明
public static int[] arrayRankTransform(int[] arr) {
//分别设置最大无穷和最小无穷
int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
//分别找出数组中最小和最大值存入min和max当中
for (int num : arr) {
if (num < min) min = num;
if (num > max) max = num;
}
//创建数组,容量为最大值减去最小值+1
int[] count = new int[max - min + 1];
//遍历数组,在原数组每项值减去最小值的位置处赋值1
for (int num : arr)
count[num - min] = 1;
//创建数组,容量为最大值减去最小值+2
int[] preSum = new int[count.length + 1];
//遍历preSum数组,将每个位置赋值是preSum[i - 1] + count[i - 1]
for (int i = 1; i < preSum.length; i++)
preSum[i] = preSum[i - 1] + count[i - 1];
//创建数组,容量为原数组长度
int[] ans = new int[arr.length];
int index = 0;
for (int num : arr)
ans[index++] = preSum[num - min] + 1;
return ans;
}
コードの理解:著者のコードは確かに非常に簡潔で簡潔ですが、理解するのは少し難しいようです。著者のコードにコメントを追加し、それを文ごとに分析します。私自身の理解があります。まず、著者は次のことを望んでいます。バケットソートアルゴリズムを使用して処理する巨大な配列は、効率を向上させるためにソートされます。また、このソートアルゴリズムを慎重にチェックしました。全体的な考え方は、順序付けされていない配列の場合、バケットは範囲で分割され、バケットでソートされてからスプライスされるというものです。
- 最初の鍵は、バケット範囲のサイズをカスタマイズする方法、つまり配列の最大値と最小値を決定する方法を最初に知る必要があるということです。
- 次に、バケットを作成します。各バケットに要素が1つしかないことを確認します。これは、最終的な終了まで非常に再帰的なアルゴリズムです。作成者は、配列の各値から最小数を引いた値をバケット記号として使用することを考えました。 create彼にカウント配列の値1を割り当てます。
- 数値が大きいほど、最小値を引いた値が大きくなるため、上記の操作は配列の並べ替えと同等であり、preSum配列の機能は、バケット内の断片化された数値をスプライスすることです。すべて割り当てられた1、各番号が実際にどの番号にランク付けされているかはわかりません。現時点では、各番号を合計するだけでよいため、値が小さいほど大きくなり、1を追加するたびに各要素の順序が変更されていることがはっきりとわかります。
- 上記の操作を行った後、元の配列と同じ長さの配列を作成し、値から最小値を引いた方法を使用して、対応する位置で対応する順序値を見つけ、保存するだけです。
学習効果:
- 配列の重複を排除したい場合は、ArrayListを使用できます
ArrayList list = new ArrayList();
int s = arr.length;
//1.用于过滤重复数组元素
for (int i = 0; i < s; i++) {
if (!list.contains(arr[i])){
list.add(arr[i]);
}
}
- 無限の最小値と無限の最大値を取得したい
//分别设置最大无穷和最小无穷
int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;