シンプルなソートアルゴリズム、二等分、対数
選択ソート
まず、ソート対象のデータ要素から最小 (または最大) の要素が選択され、シーケンスの先頭に格納されます。次に、ソートされていない残りの要素から最小 (最大) の要素が検索されて配置されます。ソートされたシーケンスの最後。ソートされるすべてのデータ要素の数がゼロになるまで、同様に続きます。
コード:
/**
* 选择排序
*/
public static void selectionSort(int[] arr){
// 如果数组为空或只有一个元素故不需要进行排序,直接返回即可
if(arr == null || arr.length < 2 ){
return;
}
for(int i = 0; i < arr.length -1;i++ ){ // 从i~N-1中找出最小的元素并放到i位置上
int minIndex = i;
for(int j = i+1;j < arr.length;j++){ //从i~N-1上找最小值的下标
minIndex = arr[j] < arr[minIndex]? j : minIndex;
}
swap(arr,i,minIndex);
}
}
// 交换arr的i和j位置上的值
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
バブルソート
並べ替える要素の列を繰り返し訪問し、隣接する 2 つの要素を順番に比較し、順序 (大から小、頭文字 Z から A など) が間違っている場合はそれらを交換します。要素を訪問する作業は、隣接する要素を交換する必要がなくなるまで、つまり要素列がソートされるまで繰り返されます。
/**
* 冒泡排序
*/
public static void bubbleSort(int[] arr){
// 数组为空或数组元素为1则不需要进行排序,直接推出即可
if(arr == null || arr.length<2){
return;
}
for(int i = 0; i < arr.length-1; i++){ // 控制比较轮次,一共 n-1 趟
for (int j = 0; j < arr.length-1-i; j++){ // 控制两个挨着的元素进行比较
if(arr[i] > arr[j]){
swap(arr,arr[i],arr[j]);
}
}
}
}
// 交换arr的i和j位置上的值 ^ 表示异或运算,不同为1相同为0
// 数组中这样交换两个数的前提是 i j 位置上的数一定不相同 否则i j 位置上的数均会变成0
// 不提倡这么写!!!
private static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
補足: 2 つの数値の値を交換するために追加のスペースを申請しないでください。
ここでの前提は、a と b がメモリ内の 2 つの別個の領域であるということです。
補足: XOR 面接の質問
arr 配列には n 種類の数値があります
1. ある種類の数字は奇数回出現し、他の数字は偶数回出現します。奇数回出現する数字を調べるにはどうすればよいですか?
2. 2 つの数字は奇数回出現し、他の数字は偶数回出現することがあります。奇数回出現する数字を見つけるにはどうすればよいですか?
ほどく:
- 変数を ero に設定します。 ero=配列内のすべての要素が XOR されます。 ero=奇数回出現する数値です。
XOR 演算は交換法則と結合法則を満たすため、同じは 0、偶数の数値の XOR 結果は 0、任意の数値と 0 の XOR はそれ自体に等しくなります。(Xiaoxiaoleに似ています)
- 変数を ero に設定します。a と b を奇数回出現する数字とします。
a^b != 0 は、8 番目のビットが異なると仮定して、a と b が少なくとも 1 ビット異なることを意味します。すべての数字を8桁目が1と0の2つに分け(偶数回出現する数字も同様に分類します)、aとbは片側のみを占めます。eor1 8桁目が1の数字のXORをとり、偶数回出た数字はそのまま0に消します。 このとき、eor1はaかbとなり、このときaとbのうちもう一方の数字は となります。 ero^ero1で入手!
/**
* 异或运算面试题 第二问
*/
public static void printOddTimesNum2(int[] arr){
int eor = 0;
for(int i : arr){
eor ^= i;
}
// eor = a ^ b
// eor != 0
// eor必然有一个位置上是1 选择最右侧的数值为1
// 将一个不为0的数最右侧的1提取出来 ~eor 表示 eor取反
int rightOne = eor & (~eor + 1); // 也就是说 一个数 & 自身取反+1 则把自己最右侧的数给弄出来了
int onlyOne = 0; // eor1
for(int cur : arr){
if((cur & rightOne) == 1){ //那个位置 等于 1 我才进行异或 相当于两边只要了一侧
onlyOne ^= cur;
}
}
System.out.println("两个出现了奇数次的数为: "+ onlyOne + " " + (eor ^ onlyOne));
}
挿入ソート
ソート対象の要素を参照します。前の n-1 (n>=2) の数値がすでにソートされていると仮定し、n 番目の数値を以前にソートしたシーケンスに挿入し、位置に適した数値を見つけます。 n 番目の数値に挿入されたシーケンスもソートされます。この方法では、シーケンス全体がソートされるまですべての要素が挿入されます。これを挿入ソートと呼びます。挿入ソートは、最初の要素が順序どおりであること、最初の 2 要素が順序どおりであること、最初の 3 要素が順序どおりであること、および最初の 4 要素が順序どおりであることを確認することと同じです。
/**
* 插入排序(优于选择排序和冒泡排序)
*/
public static void insertSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
for (int i = 1; i < arr.length; i++) { // 0到i做到有序
for(int j = i-1 ; j >= 0 && arr[j] > arr[j + 1]; j--){
swap(arr,i,j+1);
}
}
}
二分法
まず、テーブル内の要素が昇順に並んでいると仮定し、テーブルの中央に記録されているキーと検索キーを比較し、両者が等しい場合は検索成功、そうでない場合は中央のレコードを使用します。中央の位置に記録されているキーが検索キーより大きい場合は、前のサブテーブルをさらに検索し、そうでない場合は、後のサブテーブルをさらに検索します。条件を満たすレコードが見つかって検索が成功するまで、または子テーブルが存在しない場合は検索が失敗するまで、上記のプロセスを繰り返します。
- 順序付けられた配列に特定の数値が存在するかどうかを確認します
時間計算量 O(LogN)
- 順序付けされた配列で、>= 特定の数値の左端の位置を見つけます
例: 1 2 2 2 2 2 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5 >=3 の左端の数字を見つけますか?
これと最初の点の違いは、2 つの点の位置に数値が存在するかどうかを見つけるには、それを見つけるだけで済みますが、一番左の数を見つけるには、常に 2 つの点を実行する必要があることです。
- 極小値問題
例: 配列 arr 内の要素は順序付けされておらず、隣接する 2 つの数値が等しくありません。極小値を見つけてください。時間計算量は O(n) 未満です。
極小値: i-1 i i+1 配列内の位置 i の数値が i-1 の数値より小さく、かつ i+1 の数値よりも小さい場合、i は極小値と呼ばれます。
0 1 2 … M-1 M M+1 …N-2 N-1
最初の二分法で M を見つけ、M が M-1 の数より小さく、M-2 の数より小さい場合は M を返します。それ以外の場合は、片側から探索を続ければ、必ずローカル ミニマムの位置が見つかります。
対数の概念と使用法
- テストしたいメソッドがあります
- 実装の複雑さはそれほど高くありませんが、方法 b は実装が簡単です
- ランダムサンプルジェネレーターを実装する
- 同じランダムサンプルに対してメソッド a とメソッド b を実行して、結果が同じかどうかを確認します。
- 比較結果に矛盾をもたらすランダムなサンプルがある場合は、手動介入用にサンプルを印刷し、方法 a または方法 b に変更します。
- サンプル数が多い場合でも比較検定は正しく、方法 a が正しいと判断できます。
対数はその名のとおりデータを比較するためのツールであり、一般にデータ比較テストはオンラインプラットフォームに依存せずに独自のデータのテストを実現できるOJフリー環境で実行されます。たとえば、方法 a (テストしたい) と方法 b (非常に単純だが効率的ではない) の 2 つの方法があるとします。ランダム サンプル ジェネレーターを使用して一連のデータを生成し、方法 a で生成されたデータと生成されたデータを比較します。方法 b. では、データが矛盾している場合は、どちらかの方法に誤りがあることを意味し、データが一致している場合は、その方法が正しいことを意味します。これが対数の原理です。
/**
* 生成随机数组
*/
public static int[] generateRandomArray(int maxSize,int maxValue){
// Math.random() -> [0,1) 所有的小数,等概率返回一个
// Math.random() * N -> [0,N) 所有小数等概率返回一个
// (int)(Math.random() * N) -> [0,N-1] 所有的整数,等概率返回一个
int[] arr = new int[(int) ((maxSize+1)*Math.random())]; // 数组长度随机
for(int i = 0; i < arr.length; i++){
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) ((maxValue + 1) * Math.random());
}
return arr;
}
public static void main(String[] args) {
// 对数器代码实现举例
int testTime = 500000; // 表示测试的次数
int maxSize = 100; // 确保了每次生成的数的大小范围在0~100之间
int maxValue = 100; // 确保了每一次生成的数值是在0~100之间
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize,maxValue);
int[] arr2 = copyArray(arr1); // 将数组arr1拷贝一份
insertionSort(arr1);
comparator(arr2);
if(!isEqual(arr1,arr2)){
succeed = false;
break;
}
}
system.out.println(succeed);
}