Я просматривал некоторые из моих старых книг по алгоритмам и узнал о разных видах. Похоже, что все быстрые алгоритмы сортировки работают примерно в O (nLogn), и мне стало интересно, почему это лучшее, что мы можем сделать? Я написал еще один алгоритм, который, как представляется, работает лучше в определенных ситуациях (если только я не пропустил что-то), но действительно плохо в других ситуациях. Это уже алгоритм, который используется, и я просто изобретаю колесо здесь?
public class Main {
public static void main(String[] args) {
// array sort looks like it performs best in this example.
// this is because N is pretty close in value to (max - min) in the array
int[] arr = { 5, 26, 3, 32, 27, 9, 24, 29, 6, 37, 16, 10, 12, 28, 31, 22, 8, 20, 18, 2, 35, 14, 36, 7, 4, 15, 21};
arraySort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
// array sort does very poorly here.
// this is because N is 4 which is very far from the value (max - min = 999) in the array
int[] arr2 = {1, 1000, 100, 10};
arraySort(arr2);
for (int i = 0; i < arr2.length; i++) {
System.out.print(arr2[i] + " ");
}
// I think an algorithm could first check if N and maxDifference are close, then it could
// make sure that maxDifference is not so big that we start to care about size constraints.
// If it meets those criteria, we can use arraySort, and if not we can use quicksort.
}
/**
* Sorts in O(N) + O(maxDifference), where maxDifference is the difference between
* the maximum and minimum values in the array. Spatial complexity is an array of
* size maxDifference.
*/
private static void arraySort(int[] arr) {
if (arr==null || arr.length ==1){//no need to sort
return;
}
int loopCount = 0; // used for computing the algorithm complexity
int min = arr[0];
int max = arr[0];
// get the max and min values
for (int i = 0; i < arr.length; i++) {
loopCount++;
int element = arr[i];
if (element < min) {
min = element;
} else if (element > max) {
max = element;
}
}
int maxDifference = max - min;
// create a boolean array of size maxDifference.
// spatial complexity can get pretty bad when
// there is a huge maxDifference
boolean[] positions = new boolean[maxDifference + 1];
for (int i = 0; i < arr.length; i++) {
loopCount++;
int element = arr[i];
// flag this position as true for later traversal
positions[element - min] = true;
}
int count = 0;
// traverse the array
for (int i = 0; i < positions.length; i++) {
loopCount++;
boolean element = positions[i];
if (element) {
// insert the number into the sorted array
arr[count++] = i + min;
}
}
int qsortComplexity = (int) (arr.length * Math.log(arr.length)/Math.log(2));
double isortComplexity = Math.pow(arr.length, 2);
System.out.println("N = " + arr.length);
System.out.println("spatial complexity = " + maxDifference);
System.out.println("complexity = " + loopCount);
System.out.println("qsortComplexity~= " + qsortComplexity + " isortComplexity~= " + isortComplexity);
}
}
Редактировать Если кто-то заинтересован, я пошел дальше и изменил это, чтобы принять дубликаты, чтобы он больше напоминал сортировку.
public class Main {
public static void main(String[] args) {
// array sort looks like it performs best in this example.
// this is because N is pretty close in value to (max - min) in the array
int[] arr = { 5, 26, 3, 32, 27, 9, 24, 29, 6, 37, 16, 10, 12, 28, 31, 22, 8, 20, 18, 2, 35, 14, 36, 7, 4, 15, 21};
countingSort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
// array sort does very poorly here.
// this is because N is 4 which is very far from the value (max - min = 999) in the array
int[] arr2 = {1, 1000, 100, 10};
countingSort(arr2);
for (int i = 0; i < arr2.length; i++) {
System.out.print(arr2[i] + " ");
}
// testing duplicates
int[] arr3 = {10, 10, 9, 5, 6, 6, 4, 3, 7, 4, 10, 5, 3, 8, 2, 9};
countingSort(arr3);
for (int i = 0; i < arr3.length; i++) {
System.out.print(arr3[i] + " ");
}
}
/**
* Sorts in O(N) + O(maxDifference), where maxDifference is the difference between
* the maximum and minimum values in the array. Spatial complexity is an array of
* size maxDifference.
*/
private static void countingSort(int[] arr) {
if (arr==null || arr.length ==1){//no need to sort
return;
}
int loopCount = 0; // used for computing the algorithm complexity
int min = arr[0];
int max = arr[0];
// get the max and min values
for (int i = 0; i < arr.length; i++) {
loopCount++;
int element = arr[i];
if (element < min) {
min = element;
} else if (element > max) {
max = element;
}
}
int maxDifference = max - min;
int[] positionCounts = new int[maxDifference + 1];
for (int i = 0; i < arr.length; i++) {
loopCount++;
int element = arr[i];
// add to the count at that position
positionCounts[element - min] +=1;
}
int count = 0;
// traverse the array
for (int i = 0; i < positionCounts.length; i++) {
int element = positionCounts[i];
if (element == 0){
loopCount++;
}
for (int j=0; j<element; j++){
// insert the number into the sorted array
arr[count++] = i + min;
loopCount++;
}
}
int qsortComplexity = (int) (arr.length * Math.log(arr.length)/Math.log(2));
double isortComplexity = Math.pow(arr.length, 2);
System.out.println("N = " + arr.length);
System.out.println("spatial complexity = " + maxDifference);
System.out.println("complexity = " + loopCount);
System.out.println("qsortComplexity~= " + qsortComplexity + " isortComplexity~= " + isortComplexity);
}
}
Вы заново изобрели вариант [*] сортировки.
Это не алгоритм сортировки сравнения, поэтому нижняя граница Ω(n log n)
для наихудшего числа сравнений не применяется: этот алгоритм действительно может работать в меньшем количестве операций при условии выполнения определенных условий:
Сортировка сортировки и другие связанные с ней алгоритмы, такие как сортировка ковша, сортировка по методу radix и т.д., Являются полезными инструментами для вашего инструментария. Они не так широко применимы, как подобные quicksort, но могут быть именно правильным инструментом в правильных обстоятельствах. См. Википедию для сравнения сортировки ковша с другими алгоритмами.
[*] Как видно из названия, классический подсчет сортирует значения вместо использования логических флагов, поэтому является более общим. Ваш алгоритм не может правильно обрабатывать повторяющиеся элементы: он потеряет все, кроме одного из них.
Это похоже на сортировку, которая, как вы заметили, является хорошим решением для коллекций элементов с целыми ключами, чей спред не значительно больше числа элементов.
Сортировка счета - это вариант сортировки ведра с использованием ведра размера 1. Это увеличивает требования к памяти для сортировки, но снижает временную сложность.