Prefácio
Perguntas relacionadas à classificação de registros encontradas ao escrever perguntas no LeetCode
Pergunta da entrevista 10.01. Mesclar matrizes classificadas
Mesclagem bidirecional, crie temporariamente uma matriz de comprimento m + n, em seguida, execute uma mesclagem bidirecional e classifique os elementos nas matrizes A e B em ordem crescente e armazene-os na matriz temporária e, finalmente, copie os elementos do array temporário para A. Um array temporário é necessário, e a complexidade do espaço é O (m + n), porque se for mesclado diretamente no array A, quando um elemento no array em B for maior que um elemento em A, os elementos em A serão substituídos. Observe que
A O tamanho inicial da matriz é m + n. Esta configuração definitivamente não é arbitrária. Para mesclar os elementos dos dois arrays e armazená-los diretamente no array A, você pode percorrer os arrays A e B na ordem inversa e mesclar os elementos do maior para o menor. Veja o código para detalhes.
public void merge(int[] A, int m, int[] B, int n) {
//从A数组的最后面往前归并元素
int index = m + n - 1;
int pa = m - 1;
int pb = n - 1;
while(pa >= 0 && pb >= 0){
if(A[pa] > B[pb]){
A[index--] = A[pa--];
}else{
A[index--] = B[pb--];
}
}
while(pa >= 0){
A[index--] = A[pa--];
}
while(pb >= 0){
A[index--] = B[pb--];
}
}
Fila rápida para resolver os K principais problemas
Quando encontrei o problema Top K antes, minha primeira reação foi usar a fila de prioridade . Quanto mais perguntas fiz depois, descobri que muitas das soluções usavam fila rápida para resolver esses problemas, e o consumo de tempo era muito menor do que a fila prioritária.
Pergunta da entrevista 17.14. Número mínimo de K
Ao ver o k mínimo, a primeira coisa que vem à mente é usar a fila/heap de prioridade para resolver o problema; além disso, você também pode classificar o array primeiro e depois pegar diretamente os k números principais. No entanto, ambos os métodos requerem ajuste de toda a matriz, mas precisamos apenas dos menores k números; e os menores k números retirados por esses dois métodos estão todos em ordem, mas a questão não requer ordem. Em suma, estas duas abordagens causarão operações redundantes
Você pode usar a ideia de classificação rápida: a operação de classificação rápida consiste em colocar um número (pivô) na posição onde ele deve ser armazenado em ordem, garantindo que todos os números à esquerda sejam menores ou iguais a ele, e todos os números à direita são maiores que iguais a ele e não garantem a ordem dos números nos lados esquerdo e direito. Portanto, supondo que, uma vez concluída a operação, o pivô seja colocado onde deveria estar e seja descoberto que é exatamente a posição do subscrito k, então os menores k números em toda a matriz são exatamente os k números elevados ao à esquerda dele (ou seja, abaixo dos números rotulados [0,k - 1])
Então, apenas com base na classificação rápida, cada vez que um pivô é organizado para determinar sua posição, se for a posição do índice k, retorne os k números para a esquerda; se for a posição do índice k - 1, retorne o outro O número k - 1 à esquerda mais ele mesmo; se for maior que k, execute uma classificação rápida na parte esquerda; se for menor que k - 1, execute uma classificação rápida na parte direita
class Solution {
int[] quickSort(int[] arr,int l,int r,int k){
if(l >= r) return null;
int ll = l;
int rr = r;
while(rr > ll){
//这里要用">=",因为有相等的数并不影响索引k的位置的左边是最小k个数
while(arr[rr] >= arr[l] && rr > ll) rr--;
while(arr[ll] <= arr[l] && rr > ll) ll++;
if(rr != ll){
//这里如果改成了rr > ll,用时就变 2ms 了
swap(arr,rr,ll);
}
}
swap(arr,rr,l);
if(rr == k) return Arrays.copyOfRange(arr,0,rr);
if(rr == k - 1) return Arrays.copyOfRange(arr,0,rr + 1);
if(rr > k) return quickSort(arr,l,rr - 1,k);
if(rr < k) return quickSort(arr,rr + 1,r,k);
return null;
}
public int[] smallestK(int[] arr, int k) {
if(k == 0) return new int[0];
return quickSort(arr,0,arr.length - 1,k);
}
void swap(int[] arr,int i,int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
215. O K-ésimo maior elemento em uma matriz
Copiei a maior parte do código desta questão do código de número K mínimo acima.
A ideia é muito simples. O K-ésimo maior elemento na matriz é na verdade o elemento cujo subscrito é nums.length - k após a classificação. Portanto, use a classificação rápida para processar a matriz e encontrar o elemento cujo subscrito é nums.length - após a classificação .k elementos podem ser
Deve-se notar que nesta questão, se o pivô é selecionado aleatoriamente tem um grande impacto no tempo de execução - se o número mais à esquerda na seleção for o pivô, o tempo de resultado é de cerca de 12ms; enquanto o pivô é aleatório Após a conversão, o resultado leva 1ms. Em suma, a randomização pivot é geralmente necessária
class Solution {
int quickSort(int[] arr,int l,int r,int k){
if(l == r) return arr[l]; //注意这里l == r,即剩下一个数时直接返回
//===========枢轴随机选取================//
int randomIndex = new Random().nextInt(r + 1 - l) + l;
swap(arr,l,randomIndex);
//=====================================//
int ll = l;
int rr = r;
while(rr > ll){
while(arr[rr] >= arr[l] && rr > ll) rr--;
while(arr[ll] <= arr[l] && rr > ll) ll++;
if(rr != ll){
swap(arr,rr,ll);
}
}
swap(arr,rr,l);
if(rr == k) return arr[rr];
if(rr > k) return quickSort(arr,l,rr - 1,k);
if(rr < k) return quickSort(arr,rr + 1,r,k);
return -1;
}
void swap(int[] arr,int i,int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public int findKthLargest(int[] nums, int k) {
return quickSort(nums,0,nums.length - 1,nums.length - k);
}
}
347. Principais K elementos de alta frequência
A ideia de classificação de baldes. Conte o número de ocorrências de cada número na matriz e, em seguida, pegue o número correspondente do maior para o menor de acordo com o número de ocorrências. Se você pegar k números suficientes, obterá a resposta.
public int[] topKFrequent(int[] nums, int k) {
List<Integer> res = new ArrayList();
//使用一个map记录所有元素的出现次数,键为某个元素,值为其出现次数
HashMap<Integer,Integer> map = new HashMap();
for(int num : nums){
map.put(num,map.getOrDefault(num,0) + 1);
}
//再以出现次数为数组下标,出现次数相同的元素构成的列表作为数组元素
List<Integer>[] list = new List[nums.length + 1];
for(int key : map.keySet()){
int i = map.get(key);
if(list[i] == null){
list[i] = new ArrayList();
}
list[i].add(key);
}
//这样,数组中下标从大到小的元素就是出现频率从高到低的元素,所以按照下标从大到小取够k个数就是出现频率最高的k个数
for(int i = list.length - 1;i >= 0 && res.size() < k;i--){
if(list[i] == null) continue;
res.addAll(list[i]);
}
//由于是使用ArrayList收集元素,要转化为数组
int size = res.size();
int[] ans = new int[size];
for(int i = 0;i < size;i++){
ans[i] = res.get(i);
}
return ans;
}
Espada refere-se à oferta 51. Pares invertidos em uma matriz
Sem olhar para a solução, eu não teria pensado que a classificação por mesclagem poderia ser usada para resolver esse problema. A classificação por mesclagem é dividida em três etapas: particionamento, classificação e mesclagem. Se atualmente quisermos mesclar os dois intervalos classificados [a j,…,bi+2,bi+1] e [bi,…,a2,a1 1 , p2 aponta para b i+1 e, em seguida, compara repetidamente o número apontado pelos dois ponteiros que é menor, coloca-o na matriz classificada e, em seguida, move o ponteiro para trás. Então, como integrar a operação de encontrar o reverso -order pares neste processo? Se julgarmos que o número apontado por p1 é maior que o número apontado por p2, uma vez que o intervalo a está organizado em ordem crescente, os números subsequentes de p1 também serão maiores que p2. Ou seja, p1 e os subsequentes números de p1 formam um par de ordem inversa com p2. Calcule quantos números totais são adicionados a res. Além disso, p2 aumentará em um neste momento e não será utilizado para cálculos subsequentes, garantindo que a contribuição de cada par de números para o logaritmo na ordem inversa será calculada apenas uma vez.
class Solution {
public int[] tmp;
public int res = 0;
public int reversePairs(int[] nums) {
tmp = new int[nums.length];
mergeSort(nums,0,nums.length - 1);
return res;
}
public void mergeSort(int[] nums,int l,int r){
if(l >= r) return;
int mid = (l + r) >> 1;
mergeSort(nums,l,mid);
mergeSort(nums,mid + 1,r);
merge(nums,l,mid,r);
}
public void merge(int[] nums,int l,int mid,int r){
System.arraycopy(nums,l,tmp,l,r - l + 1);
int index = l;
int p1 = l,p2 = mid + 1;
while(p1 <= mid && p2 <= r){
if(tmp[p1] > tmp[p2]){
res += mid - p1 + 1; //相比于单纯的归并排序,其实只加了这一行代码
nums[index++] = tmp[p2++];
}else{
nums[index++] = tmp[p1++];
}
}
while(p1 <= mid){
nums[index++] = tmp[p1++];
}
while(p2 <= r){
nums[index++] = tmp[p2++];
}
}
}