Пытаюсь преобразовать мою функцию быстрой сортировки таким образом, чтобы она могла обрабатывать массив объектов и сортировать по значению внутри объекта

#javascript #algorithm #quicksort

#javascript #алгоритм #быстрая сортировка

Вопрос:

В настоящее время я пытаюсь доработать свою функцию быстрой сортировки, чтобы я мог проверить реальную проблему, когда есть массив объектов, который имеет ту же цель сортировки, но другой идентификатор.

Ниже приведены мои два разных кода, в которых механизмы сводного выбора отличаются друг от друга.

 //first Code


function quickSort(array) {
  if (array.length === 1) return array; // base case
  let pivot = array[array.length - 1]; 
  let leftArr = [];
  let rightArr = [];

  for(let el of array) {
      if (el.num < pivot.num) {
          leftArr.push(el); 
      } else {
          rightArr.push(el); 
      }
  }
  console.log(rightArr);
  console.log(leftArr);

  if (leftArr.length > 0 amp;amp; rightArr.length > 0) {
    return [...quickSort(leftArr), pivot, ...quickSort(rightArr)] 
  } else if (leftArr.length > 0) {
    return [...quickSort(leftArr), pivot]
  } else {
    return [pivot, ...quickSort(rightArr)] 
  }
}
  
 // second Code 

function quickSort2(arr) {
  if (!arr.length) {
    return arr;
  }
  let pivotIdx = Math.floor(Math.random() * (arr.length));
  let pivot = arr[pivotIdx];
  let head = [];
  let tail = [];
  let exceptPivot = arr.slice(0, pivotIdx).concat(arr.slice(pivotIdx   1));
  for (let el of exceptPivot) {
    if (el.num <= pivot.num) {
      head.push(el)
    } else {
      tail.push(el)
    }
  }
  // return quickSort1(head).concat([pivot].concat(quickSort1(tail)));
  return [...quickSort1(head), pivot, ...quickSort1(tail)];
};

  

И ниже приведен тест:

 let test = [
  {id: 4, num: 21},
  {id: 7, num: 22},
  {id: 12, num: 24},
  {id: 5, num: 100},
  {id: 6, num: 100},
  {id: 32, num: 14},
  {id: 19, num: 15},
  {id: 23, num: 16},
  {id: 1, num: 15},
  {id: 42, num: 100},
  {id: 56, num: 100},
  {id: 74, num: 100}
]
  

Для первого кода я получаю много стеков в качестве результатов. И в итоге вообще не сортирует.
Для второго кода я получаю только результаты массива.

Не могли бы вы указать на логику, которую я не учел, и сбой в моих кодах? Заранее благодарю вас.

Ответ №1:

Для первого кода ваш код создает бесконечный цикл, потому что вы не учли, что произойдет, когда el.num === pivot.num. Например,

 array = [
  {id: 4, num: 21},
  {id: 7, num: 22},
  {id: 12, num: 24},
  {id: 19, num: 15},
  {id: 23, num: 16},
  {id: 1, num: 15}
]
  

переход к этой другой логике навсегда.

 else {
    return [pivot, ...quickSort(rightArr)] 
}
  

Теперь решение простое, проверьте, когда числовой ключ из 2 элементов равен. Например:

 function quickSort(array) {
  if (array.length <= 1) return array; // note that array.length === 0 is also base case
  
  let pivot = array[array.length - 1]; 
  let leftArr = [];
  let rightArr = [];
  let sameArr = []; // to save elements with same num value
  for(let el of array) {
      if (el.num < pivot.num) {
          leftArr.push(el); 
      } else if(el.num > pivot.num) {
          rightArr.push(el); 
      } else {
          sameArr.push(el);
      }
  }

  return [...quickSort(leftArr), ...sameArr, ...quickSort(rightArr)]
}
  

Но что произойдет, когда 2 элемента имеют одинаковое значение num, но разное значение id?
Это зависит от вашего варианта использования, который имеет более высокий приоритет? число или идентификатор?

Если вы хотите сначала выполнить сортировку на основе значения id, затем значения num:

 function quickSort(array) {
  if (array.length <= 1) return array; // base case
  
  let pivot = array[array.length - 1]; 
  let leftArr = [];
  let rightArr = [];
  let sameArr = [];
  for(let el of array) {
      if (el.id < pivot.id) {
          leftArr.push(el); 
      } else if(el.id > pivot.id) {
          rightArr.push(el); 
      } else {
        if (el.num < pivot.num) {
          leftArr.push(el);
        } else if (el.num > pivot.num) {
          rightArr.push(el);
        } else {
          sameArr.push(el);
        }
      }
  }

  return [...quickSort(leftArr), ...sameArr, ...quickSort(rightArr)]
}
  

Если вы хотите сначала выполнить сортировку на основе значения num, затем значения id:

 function quickSort(array) {
  if (array.length <= 1) return array; // base case
  
  let pivot = array[array.length - 1]; 
  let leftArr = [];
  let rightArr = [];
  let sameArr = [];
  for(let el of array) {
      if (el.num < pivot.num) {
          leftArr.push(el); 
      } else if(el.num > pivot.num) {
          rightArr.push(el); 
      } else {
        if (el.id < pivot.id) {
          leftArr.push(el);
        } else if (el.id > pivot.id) {
          rightArr.push(el);
        } else {
          sameArr.push(el);
        }
      }
  }

  return [...quickSort(leftArr), ...sameArr, ...quickSort(rightArr)]
}
  

Когда есть много ключей объекта для сравнения, вы можете еще больше упростить это, создав дополнительный массив приоритетов и сравнив элементы в порядке уровня приоритета этого ключа.