Diretório de artigos
-
-
- 8 algoritmo de divisão e conquista
-
- 8.1 [Recursão] Oferta do Ponteiro de Espada 07 - Reconstruir Árvore Binária
- 8.2 [Recursivo] [Quick Power] Jianzhi Oferta 16 - a potência inteira do valor
- 8.3 [Recursão] Espada refere-se à Oferta 33 - sequência de travessia pós-ordem da árvore de pesquisa binária
- 8.4 [Recursão] [Dividir e Conquistar] Oferta do Ponteiro de Espada 17 - Imprima os n dígitos de 1 ao maior
- 8.5 [Mesclar e Classificar] [Dividir e Conquistar] Oferta do Ponteiro de Espada 51 - Pares invertidos na matriz
- 9 tipo
- 10 Programação Dinâmica
-
- 10.1 [Programação Dinâmica] [Tabela Hash] [DFS] Oferta do Ponteiro de Espada 10- I - Sequência de Fibonacci
- 10.2 [Programação Dinâmica] [Tabela Hash] [DFS] Oferta do Ponteiro de Espada 10- II - Passos de salto de sapo
- 10.3 [Programação dinâmica] Espada refere-se à Oferta 63 - o lucro máximo das ações
- 10.4 [Programação Dinâmica] [Dividir e Conquistar] A espada refere-se à Oferta 42 - a soma máxima de submatrizes consecutivas
- 10.5 [Programação dinâmica] Espada refere-se à oferta 47 - o valor máximo do presente
-
8 algoritmo de divisão e conquista
8.1 [Recursão] Oferta do Ponteiro de Espada 07 - Reconstruir Árvore Binária
https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/
A travessia de pré-ordem é sobre a raiz, e a travessia em ordem é a raiz esquerda e direita, o que significa que o primeiro nó da travessia de pré-ordem é o nó raiz de toda a árvore. Siga este nó para encontrar seu posição na travessia em ordem. É in_root, então o lado esquerdo de in_root está na subárvore esquerda e o lado direito está na subárvore direita, para que você possa saber quantos nós existem na subárvore esquerda, e em seguida, vá para o percurso de pré-ordem para encontrar o ponto de divisão das subárvores esquerda e direita, dividido em Para as partes esquerda e direita, repita o processo acima respectivamente, encontre o primeiro nó raiz de cada parte e prossiga para baixo em sequência até o os limites das subárvores esquerda e direita se sobrepõem no final e a reconstrução da árvore binária é concluída.
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
private:
unordered_map<int,int> hash_table;
public:
TreeNode* subTree(vector<int>& preorder, vector<int>& inorder, int pre_l, int pre_r, int in_l, int in_r)
{
if(pre_l > pre_r) return nullptr;
//找到根节点,计算左子树的节点数
int pre_root = pre_l;
int in_root = hash_table[preorder[pre_l]];
int sub_l = in_root - in_l;
//开始生成root
TreeNode* root = new TreeNode(preorder[pre_l]);
//对于左子树而言,前序遍历的[左边界+1,左边界+左子树节点数]即为中序遍历的[左边界,根节点-1]
root->left = subTree(preorder, inorder, pre_l+1, pre_l+sub_l, in_l, in_root-1);
//对于右子树而言,前序遍历的[左边界+左子树节点数+1,右边界]即为中序遍历的[根节点+1,右边界]
root->right = subTree(preorder, inorder, pre_l+sub_l+1, pre_r, in_root+1, in_r);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
int n = inorder.size();
for(int i=0;i<n;i++)
{
hash_table[inorder[i]] = i;
}
return subTree(preorder, inorder, 0, n-1, 0, n-1);
}
};
8.2 [Recursivo] [Quick Power] Jianzhi Oferta 16 - a potência inteira do valor
https://leetcode.cn/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/description/
Este problema pode ser resolvido com potência rápida. A ideia específica é desmontar a potência de acordo com o binário e calculá-la separadamente. Aqui está um exemplo: Suponha que eu queira calcular x 10 x^ {10}x10 , porque a representação binária de 10 é 1010, entãox 10 = x 2 0 ∗ 0 ∗ x 2 1 ∗ 1 ∗ x 2 2 ∗ 0 ∗ x 2 3 ∗ 1 x^{10}=x^{2^0 * 0}*x^{2^1*1}*x^{2^2*0}*x^{2^3*1}x10=x20 ∗0∗x21 *1∗x22 *0∗x23 *1pode ser considerado como crescente em ordem de acordo com a potência de 2 de x. Basta verificar se o binário correspondente a esta potência é 1.
class Solution {
public:
double myPow(double x, int n) {
if(x==1 || n==0) return 1;
if(x==0) return 0;
double ans = 1;
long num = n;
if(n<0)
{
num = -num;
x = 1/x;
}
while(num)
{
if(num & 1) ans *= x;
x *= x;
num >>= 1;
}
return ans;
}
};
8.3 [Recursão] Espada refere-se à Oferta 33 - sequência de travessia pós-ordem da árvore de pesquisa binária
https://leetcode.cn/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof
A característica da travessia pós-ordem é visitar primeiro a subárvore esquerda, depois a subárvore direita e finalmente o nó raiz, então o último elemento deve ser o nó raiz, e a característica da árvore de pesquisa binária é que a subárvore esquerda <raiz nó <subárvore direita, então nós A matriz pode ser dividida em duas partes de acordo com o valor do nó raiz e, em seguida, julgar se está em conformidade com as características da árvore de pesquisa binária e repetir o processo acima até que todos os casos sejam julgados.
class Solution {
public:
bool dfs(vector<int>& postorder, int left, int right)
{
if(left >= right) return true;
int i = left;
while(postorder[i]<postorder[right])
{
i++;
}
int mid = i;
while(postorder[i]>postorder[right])
{
i++;
}
return i==right & dfs(postorder,left,mid-1) & dfs(postorder,mid,right-1);
}
bool verifyPostorder(vector<int>& postorder)
{
return dfs(postorder,0,postorder.size()-1);
}
};
8.4 [Recursão] [Dividir e Conquistar] Oferta do Ponteiro de Espada 17 - Imprima os n dígitos de 1 ao maior
https://leetcode.cn/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof
Como esta questão especifica que o array é do tipo int, não há necessidade de considerar o problema de números inteiros grandes, e ele pode ser resolvido por violência direta. Porém, se for um número inteiro grande, a ideia de dividir e conquistar a recursão precisa ser adotada, principalmente da seguinte forma:
(1) O primeiro nível de travessia é n, considerando que os números gerados são de 1 dígito, 2 dígitos, 3 dígitos...n dígitos;
(2) O segundo nível de travessia, percorrendo respectivamente o número de cada dígito, exceto o primeiro dígito é 1-9, o restante é 0-9.
class Solution {
public:
vector<int> printNumbers(int n) {
int cnt = pow(10,n);
vector<int> ans;
for(int i=1;i<cnt;i++)
{
ans.push_back(i);
}
return ans;
}
};
8.5 [Mesclar e Classificar] [Dividir e Conquistar] Oferta do Ponteiro de Espada 51 - Pares invertidos na matriz
https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof
Bem escrito por esse cara! ( https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solutions/622496/jian-zhi-offer-51-shu-zu-zhong-de-ni-xu -pvn2h )
class Solution {
public:
int merge_sort(int left, int right, vector<int>& nums, vector<int>& tmp)
{
if(left >= right) return 0;
int mid = (left + right) / 2;
int res = merge_sort(left, mid, nums, tmp) + merge_sort(mid+1, right, nums, tmp);
int i = left, j = mid + 1;
for(int k=left;k<=right;k++)
{
tmp[k] = nums[k];
}
for(int k=left;k<=right;k++)
{
if(i == mid+1) nums[k] = tmp[j++];
else if(j == right+1 || tmp[i] <= tmp[j]) nums[k] = tmp[i++];
else
{
nums[k] = tmp[j++];
res += mid - i + 1;
}
}
return res;
}
int reversePairs(vector<int>& nums)
{
vector<int> tmp(nums.size());
return merge_sort(0, nums.size()-1, nums, tmp);
}
};
9 tipo
9.1 [Bubble Sort] Jianzhi Oferta 45 - Organize a matriz no menor número
https://leetcode.cn/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof
Esta questão pode ser considerada uma classificação por bolha, mas o objeto é uma string, precisamos encontrar a maior string nele e colocá-la no final da string, como "32" e "3", porque " 323"<"332", então "32"<"3", então "3" deve ser colocado depois de "32" e use esta ideia de classificação para resolver o problema.
class Solution {
public:
string minNumber(vector<int>& nums)
{
for(int i=nums.size()-1;i>0;i--)
{
for(int j=0;j<i;j++)
{
if(string_sort(nums[j],nums[j+1]))
{
swap(nums[j],nums[j+1]);
}
}
}
string ans = "";
for(int i=0;i<nums.size();i++)
{
ans += to_string(nums[i]);
}
return ans;
}
bool string_sort(int num1, int num2)
{
string s1 = to_string(num1) + to_string(num2);
string s2 = to_string(num2) + to_string(num1);
if(s1>s2) return true;
else return false;
}
};
9.2 [Classificação] Jianzhi Oferta 61 - Straight in Poker
https://leetcode.cn/problems/bu-ke-pai-zhong-de-shun-zi-lcof
Esta questão não é difícil, basta pensar com clareza nas condições de julgamento de Shunzi, principalmente nos seguintes aspectos:
(1) Entre os cinco números, não pode haver números repetidos além de 0, caso contrário, definitivamente não é uma sequência;
(2) Encontre os valores máximo e mínimo entre os cinco números, veja quantos números estão faltando entre esses dois números, registre-os como lacuna e, em seguida, calcule o número de 0s entre os cinco números, registre-os como zero_num, se gap <= zero_num, significa que o número 0 pode preencher o número que falta, caso contrário, definitivamente não é uma sequência.
class Solution {
public:
bool isStraight(vector<int>& nums) {
int arr[14] = {
0};
int max_num = 0, min_num = 14, zero_num = 0;
for(int i=0;i<5;i++)
{
if(nums[i]==0) zero_num++;
else
{
if(arr[nums[i]]) return false;
else
{
arr[nums[i]] = 1;
min_num = min(min_num, nums[i]);
max_num = max(max_num, nums[i]);
}
}
}
int gap = 0;
for(int i=min_num;i<max_num;i++)
{
if(arr[i]!=1) gap++;
}
if(gap <= zero_num) return true;
else return false;
}
};
9.3 [Classificação de heap] Jianzhi Oferta 40 - o menor número k
https://leetcode.cn/problems/zui-xiao-de-kge-shu-lcof
Método 1: Use classificar para classificar diretamente e, em seguida, selecione os k números principais.
Método 2: Use classificação de heap, classificação de heap, usando o heap raiz grande, primeiro armazene os primeiros k números no heap raiz grande e compare os números subsequentes com o topo do heap raiz grande, se for menor que o topo, atualize o heap raiz grande e, finalmente, coloque o heap raiz grande. Os números no vetor são retornados como resposta.
//方法一:直接排序
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
sort(arr.begin(),arr.end());
vector<int> ans;
for(int i=0;i<k;i++)
{
ans.push_back(arr[i]);
}
return ans;
}
};
//方法二:堆排序
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> ans;
if(k == 0) return ans;
priority_queue<int, vector<int>, less<int>> max_stack;
//把前k个数字存入大根堆
for(int i=0;i<k;i++)
{
max_stack.push(arr[i]);
}
//依次和大根堆的top比较,如果比top小就更新大根堆
for(int j=k;j<arr.size();j++)
{
if(arr[j] < max_stack.top())
{
max_stack.pop();
max_stack.push(arr[j]);
}
}
//把大根堆中的数字存入vector中作为答案返回
while(k--)
{
ans.push_back(max_stack.top());
max_stack.pop();
}
return ans;
}
};
9.4 [Heap Sort] [Priority Queue] Apontando para a oferta 41 - Mediana no fluxo de dados
https://leetcode.cn/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof
Se esta questão classificar todos os números diretamente, ela será executada no final. Na verdade, desde que possamos encontrar dois números ou um número no meio de todo o fluxo de dados toda vez que a função findMedian for executada, podemos considerar usando classificação de heap. Mantenha uma pilha raiz grande e uma pilha raiz pequena ao mesmo tempo, divida o número no fluxo de dados em duas partes e também garanta que o topo da pilha raiz grande seja menor do que o topo da raiz pequena pilha, de modo que a mediana final seja pequena. O topo da pilha raiz, ou a média do topo da pilha raiz grande e da pilha raiz pequena, é construído da seguinte forma:
- Se o tamanho do heap raiz grande e do heap raiz pequeno forem iguais, então o número de add é adicionado ao heap raiz grande neste momento e, em seguida, o topo do heap raiz grande é inserido no heap raiz pequeno para certifique-se de que o tamanho do heap raiz grande<=o tamanho do heap raiz pequeno;
- Se o tamanho do heap raiz grande e do heap raiz pequeno forem diferentes, então o número de adição é adicionado ao heap raiz pequeno neste momento e, em seguida, o topo do heap raiz pequeno é inserido no heap raiz grande para garantir que o tamanho do heap raiz pequeno<=o tamanho do heap raiz grande;
- Ao executar a função findMedian, verifique se os tamanhos do heap raiz grande e do heap raiz pequeno são iguais. Se forem iguais, a mediana é a soma dos elementos superiores e calcula a média. Se não forem iguais, a mediana é o tamanho do pequeno heap raiz.
class MedianFinder {
public:
/** initialize your data structure here. */
priority_queue<int, vector<int>, greater<int>> min_stack;
priority_queue<int, vector<int>, less<int>> max_stack;
MedianFinder() {
}
void addNum(int num) {
//如果大根堆和小根堆的size一样,那么此时add的num就加入大根堆,然后把大根堆的top插入到小根堆中,保证大根堆的size<=小根堆的size
if(min_stack.size() == max_stack.size())
{
max_stack.push(num);
min_stack.push(max_stack.top());
max_stack.pop();
}
//如果大根堆和小根堆的size不一样,那么此时add的num就加入小根堆,然后把小根堆的top插入到大根堆中,保证小根堆的size<=大根堆的size
else
{
min_stack.push(num);
max_stack.push(min_stack.top());
min_stack.pop();
}
}
//执行findMedian函数时,看看此时大根堆和小根堆的size是否相等,如果相等的话,中位数就是各自取top元素相加取平均,如果不相等,那么中位数就是小根堆的top
double findMedian() {
if(min_stack.size() == max_stack.size())
{
return (max_stack.top() + min_stack.top()) / 2.0;
}
else return min_stack.top();
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
10 Programação Dinâmica
10.1 [Programação Dinâmica] [Tabela Hash] [DFS] Oferta do Ponteiro de Espada 10- I - Sequência de Fibonacci
https://leetcode.cn/problems/fei-bo-na-qi-shu-lie-lcof
Se você usar programação dinâmica diretamente para esta questão, o tempo de execução será principalmente porque alguns números serão calculados repetidamente durante o processo de cálculo, então usamos uma tabela hash para armazenar os números calculados aqui, para que quando eles forem calculados novamente mais tarde, basta usar diretamente.
class Solution {
public:
unordered_map<int,int> hash_table;
int dfs(int n)
{
if(n == 0) return 0;
else if(n == 1) return 1;
else
{
if(hash_table.count(n)) return hash_table[n];
else
{
int num1 = dfs(n-1) % 1000000007;
int num2 = dfs(n-2) % 1000000007;
hash_table[n] = (num1 + num2) % 1000000007;
return hash_table[n];
}
}
}
int fib(int n)
{
return dfs(n);
}
};
10.2 [Programação Dinâmica] [Tabela Hash] [DFS] Oferta do Ponteiro de Espada 10- II - Passos de salto de sapo
https://leetcode.cn/problems/qing-wa-tiao-tai-jie-wen-ti-lcof
Este tópico é a sequência de Fibonacci deformada. Aqui, uma tabela hash é usada para armazenar os números que foram calculados, para que possam ser usados diretamente quando forem calculados novamente mais tarde.
class Solution {
public:
unordered_map<int,int> hash_table;
int dfs(int n)
{
if(n == 0) return 1;
else if(n == 1) return 1;
else
{
if(hash_table.count(n)) return hash_table[n];
else
{
int num1 = dfs(n-1) % 1000000007;
int num2 = dfs(n-2) % 1000000007;
hash_table[n] = (num1 + num2) % 1000000007;
return hash_table[n];
}
}
}
int numWays(int n) {
return dfs(n);
}
};
10.3 [Programação dinâmica] Espada refere-se à Oferta 63 - o lucro máximo das ações
https://leetcode.cn/problems/gu-piao-de-zui-da-li-run-lcof
A solução para problemas de programação dinâmica é principalmente encontrar a equação de transição de estado. Para este problema, a transição de estado depende de haver ações em um determinado dia. Usamos isso como limite para definir a equação de transição de estado dp[i][ 2]:
(1) Não manteve ações nos dias anteriores
dp [i] [0] = max (dp [i − 1] [0], dp [i − 1] [1] + preços [i]) dp[i][0] = max(dp[i-1 ][0], dp[i-1][1] + preços[i])d p [ eu ] [ 0 ]=má x ( d p [ eu-1 ] [ 0 ] ,d p [ eu-1 ] [ 1 ]+preços [ eu ] ) _ _
(2) Manter ações no dia anterior
dp [ i ] [ 1 ] = max ( dp [ i − 1 ] [ 1 ] , 0 − preços [ i ] ) dp[i][1] = max(dp[i-1][1], 0 - preços [eu])d p [ eu ] [ 1 ]=má x ( d p [ eu-1 ] [ 1 ] ,0-preços [ eu ] ) _ _
Ao mesmo tempo, também é necessário prever a situação em que os preços estão vazios e retornar 0 neste momento, pois os dois elementos do dp são chamados repetidamente, então o código é substituído diretamente por duas variáveis.
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(!prices.size()) return 0;
int dp_0 = 0, dp_1 = -prices[0];
for(int i=1;i<prices.size();i++)
{
dp_0 = max(dp_0, dp_1 + prices[i]);
dp_1 = max(dp_1, 0 - prices[i]);
}
return dp_0;
}
};
10.4 [Programação Dinâmica] [Dividir e Conquistar] A espada refere-se à Oferta 42 - a soma máxima de submatrizes consecutivas
https://leetcode.cn/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof
Esta questão usa uma pequena ideia de dividir e conquistar, assumindo que o comprimento do array agora é nnn , a soma máxima de submatrizes consecutivas éf ( n ) f(n)f ( n ),那么f ( n ) = max ( f ( n − 1 ) + nums [ i ] , num [ i ] ) f (n) = max (f (n-1) + nums [i], num [eu])f ( n )=má x ( f ( n-1 )+n você m s [ eu ] ,n você m [ i ]) , o que significa que a maior soma é a primeiran − 1 n-1n-A maior soma de 1 número mais oiieu número, ou oiitheu numero a si mesmo, se for oiésimoPara o número i em si, é necessário encontrar a submatriz contínua a partir daqui e registrar o valor máximo no processo.
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre = 0, max_seqsum = nums[0];
for(int i=0;i<nums.size();i++)
{
pre = max(pre + nums[i], nums[i]);
max_seqsum = max(max_seqsum, pre);
}
return max_seqsum;
}
};
10.5 [Programação dinâmica] Espada refere-se à oferta 47 - o valor máximo do presente
https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof
Na verdade, o valor máximo de cada local está relacionado apenas a dois estados, assumindo que o requisito atual seja valor [i] [j] valor[i][j]v a l u e [ i ] [ j ]的最大值,那么valor [ i ] [ j ] = max ( valor [ i ] [ j − 1 ] , valor [ i − 1 ] [ j ] ) + grade [ i ] [ j ] valor[i][j] = max(valor[i][j-1],valor[i-1][j]) + grade[i][j]v a l você e [ eu ] [ j ]=má x ( v a l u e [ i ] [ j-1 ] ,v a l você e [ eu-1 ] [ j ])+g r i d [ i ] [ j ] , para conveniência da inicialização, o tamanho da matriz inicial é definido como( m + 1 ) ∗ ( n + 1 ) (m+1)*(n+1)( m+1 )∗( n+1 )。
class Solution {
public:
int maxValue(vector<vector<int>>& grid)
{
int m = grid.size(), n = grid[0].size();
int value[m+1][n+1];
memset(value,0,sizeof(value));
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
value[i+1][j+1] = max(value[i+1][j],value[i][j+1]) + grid[i][j];
}
}
return value[m][n];
}
};