LeetCode面试题 17.10. 主要元素/最优二叉树II(树形DP)/NC73数组中出现次数超过一半的数字/229. 求众数 II/NC134股票(无限次交易)/NC114旋转字符串
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode面试题 17.10. 主要元素/最优二叉树II(树形DP)/NC73数组中出现次数超过一半的数字/229. 求众数 II/NC134股票(无限次交易)/NC114旋转字符串相关的知识,希望对你有一定的参考价值。
面试题 17.10. 主要元素
2021.7.9每日一题
题目描述
数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。若没有,返回 -1 。
请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。
示例 1:
输入:[1,2,5,9,5,9,5,5,5]
输出:5
示例 2:
输入:[3,2]
输出:-1
示例 3:
输入:[2,2,1,1,1,2,2]
输出:2
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-majority-element-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
昨天刚又做了一次,摩尔投票法,这里没有说肯定有一个数目大于一半的数存在,所以需要选出候选人后再遍历一次判断是否满足条件
class Solution {
public int majorityElement(int[] nums) {
int candidate1 = nums[0];
int count = 1;
int n = nums.length;
for(int i = 1; i < n; i++){
if(count == 0)
candidate1 = nums[i];
if(candidate1 == nums[i])
count++;
else{
count--;
}
}
count = 0;
for(int num : nums){
if(candidate1 == num)
count++;
if(count > n / 2)
return candidate1;
}
return -1;
}
}
最优二叉树II
学习一下这个题,树形dp就做过一次
题目描述
链接:https://www.nowcoder.com/questionTerminal/0d939e874a004f449a370aca1346dd5c
来源:牛客网
小团有一个由N个节点组成的二叉树,每个节点有一个权值。定义二叉树每条边的开销为其两端节点权值的乘积,二叉树的总开销即每条边的开销之和。小团按照二叉树的中序遍历依次记录下每个节点的权值,即他记录下了N个数,第i个数表示位于中序遍历第i个位置的节点的权值。之后由于某种原因,小团遗忘了二叉树的具体结构。在所有可能的二叉树中,总开销最小的二叉树被称为最优二叉树。现在,小团请小美求出最优二叉树的总开销。
输入描述:
第一行输入一个整数N(1<=N<=300),表示二叉树的节点数。
第二行输入N个由空格隔开的整数,表示按中序遍历记录下的各个节点的权值,所有权值均为不超过1000的正整数。
输出描述:
输出一个整数,表示最优二叉树的总开销。
思路
树形dp
dp[i][j][k]表示以k为根节点,i ~ j为子树节点的最小开销
从节点left到right轮流当根节点,通过递归分治构建左右子树计算整体的开销。
记忆化递归,顺便学习一下这种输入数据的方法
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class Main {
static int[][][] mem;
static int[] weight;
static int n;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine().trim());
String[] strW = br.readLine().trim().split(" ");
weight = new int[n];
for(int i = 0; i < n; i++) weight[i] = Integer.parseInt(strW[i]);
// mem[l][r][k]表示以weight[l:r]为子节点,以weight[k]为根节点的树开销
mem = new int[n][n][n];
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++)
for(int k = 0; k < n; k++) mem[i][j][k] = -1;
}
//初始根节点没有确定,所以这里根节点写-1,确定根节点的逻辑在递归里完成
//一般情况下,是遍历数组,确定根节点,然后对每个根节点进行递归
System.out.println(recur(0, n - 1, -1));
}
private static int recur(int left, int right, int root) {
if(left > right) return 0;
if(root >= 0 && mem[left][right][root] != -1) return mem[left][right][root];
int cost = Integer.MAX_VALUE;
// [left,right]中的元素轮流做根节点构建二叉树
int leftCost = 0, rightCost = 0;
for(int i = left; i <= right; i++){
leftCost = recur(left, i - 1, i); // 左子树开销
rightCost = recur(i + 1, right, i); // 右子树开销
// root=-1时表示初始根节点还没有确定,不会有根节点连接左右子树的边
cost = Math.min(cost, leftCost + rightCost + weight[i]*(root != -1? weight[root]: 0));
}
if(root >= 0) mem[left][right][root] = cost;
return cost;
}
}
NC73数组中出现次数超过一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。你可以假设数组是非空的,并且给定的数组总是存在多数元素。1<=数组长度<=50000
示例1
输入:
[1,2,3,2,2,2,5,4,2]
返回值:
2
思路
就是相互抵消,如果不一样就相互抵消,一样就增加数量,因为有一个数出现次数超过一半,所以最后剩下的结果肯定是这个数
原来叫摩尔投票法,这里最后不需要再检查一遍,是因为已经规定了肯定共有数超过一半,一般需要最后在检查一遍找出来的数是否是最多的数
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
//这个叫个什么方法来着,好像是博弈论的东西,就是相互抵消
int l = array.length;
int temp = array[0];
int count = 1;
for(int i = 1; i < l; i++){
if(temp == array[i])
count++;
else{
count--;
if(count == 0)
temp = array[i + 1];
}
}
return temp;
}
}
再回顾一下选两个候选人的那道题:
229. 求众数 II
题目描述
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。
示例 1:
输入:[3,2,3]
输出:[3]
示例 2:
输入:nums = [1]
输出:[1]
示例 3:
输入:[1,1,1,3,3,2,2,2]
输出:[1,2]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/majority-element-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
还是摩尔投票法,这里因为找的是超过1/3的元素,所以这里定两个候选人,如果投票结果与这两个候选人其中一个相同,那么该候选人票数加1,如果都不相同,那么两个候选人的票数都减1,如果有一个等于0了,就更换候选人。因为这里没有说最后结果肯定有两个,所以最后需要验证,所得到的这两个候选人是否票数都超过了1/3
class Solution {
public List<Integer> majorityElement(int[] nums) {
//摩尔投票法(升级),我理解这个方法就是,选n个候选人,就需要有多于 1 / (n + 1)的票数,
//所以将剩下的小于1/n的人和这几个候选人每个人的票数相加,就相当于每个候选人的票数大于这一部分的1/2,
//所以就可以转换为169题,选大于1/2的那个题
//所以先确定两个候选人,如果第三个投票结果和他们中任何一个相同,使其中那个人的票数加1;
//如果第三个投票结果和他们任何一个都不同,那么就都抵消掉
//如果某一个计数等于0,那就更换候选人
//以这个过程遍历结束,剩余结果可能是符合要求的,也可能是不符合的
//所以需要计数阶段,就是遍历一次nums,统计个数
List<Integer> res = new ArrayList<>();
int cand1 = nums[0], count1 = 0;
//这里两个候选人先赋值为一样的也没关系,因为后面抵消阶段的条件是continue
int cand2 = nums[0], count2 = 0;
//抵消阶段
for(int n : nums){
if(n == cand1){
count1++;
continue;
}
if(n == cand2){
count2++;
continue;
}
//当count1等于0的时候,换候选人
if(count1 == 0){
cand1 = n;
count1++;
continue;
}
if(count2 == 0){
cand2 = n;
count2++;
continue;
}
//如果不相同,就减去
count1--;
count2--;
}
//计数阶段
count1 = 0;
count2 = 0;
for(int n : nums){
if(cand1 == n)
count1++;
//这里必须用else if,因为两个候选人可能相同
else if(cand2 == n){
count2++;
}
}
if(count1 > nums.length / 3)
res.add(cand1);
if(count2 > nums.length / 3)
res.add(cand2);
return res;
}
}
NC134股票(无限次交易)
题目描述
假定你知道某只股票每一天价格的变动。
你最多可以同时持有一只股票。但你可以无限次的交易(买进和卖出均无手续费)。
请设计一个函数,计算你所能获得的最大收益。
示例1
输入:
[5,4,3,2,1]
返回值:
0
说明:
由于每天股票都在跌,因此不进行任何交易最优。最大收益为0。
思路
无限次就是贪心
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算最大收益
* @param prices int整型一维数组 股票每一天的价格
* @return int整型
*/
public int maxProfit (int[] prices) {
// write code here
int l = prices.length;
int profit = 0;
for(int i = 1; i < l; i++){
if(prices[i] > prices[i - 1])
profit += prices[i] - prices[i - 1];
}
return profit;
}
}
NC114旋转字符串
import java.util.*;
public class Solution {
/**
* 旋转字符串
* @param A string字符串
* @param B string字符串
* @return bool布尔型
*/
public boolean solve (String A, String B) {
// write code here
//想了一下,旋转和切割然后拼接感觉差不多复杂度
int la = A.length();
int lb = B.length();
if(la != lb)
return false;
for(int i = 0; i < la; i++){
String sub1 = A.substring(0, i + 1);
String sub2 = A.substring(i + 1, la);
if(B.equals(sub2 + sub1))
return true;
}
return false;
}
}
将两个A拼接,然后找B,调用API的方法
import java.util.*;
public class Solution {
/**
* 旋转字符串
* @param A string字符串
* @param B string字符串
* @return bool布尔型
*/
public boolean solve (String A, String B) {
// write code here
//给的方法,因为是左右互换,所以将两个A拼接,中间的字符串就是拼接的结果
//然后在AA这个字符串中找B就行了
return A.length() != B.length() ? false : (A + A).contains(B);
}
}
写个KMP找B的方法,自己能直接写出来了,说明这个点也掌握了
import java.util.*;
public class Solution {
/**
* 旋转字符串
* @param A string字符串
* @param B string字符串
* @return bool布尔型
*/
public boolean solve (String A, String B) {
// write code here
//给的方法,因为是左右互换,所以将两个A拼接,中间的字符串就是拼接的结果
//然后在AA这个字符串中找B就行了
return A.length() != B.length() ? false : KMP(A + A, B);
}
public boolean KMP(String s, String sub){
int[] next = getNext(sub);
int i = 0;
int j = 0;
int l = s.length();
while(i < l){
while(j > 0 && s.charAt(i) != sub.charAt(j))
j = next[j - 1];
if(s.charAt(i) == sub.charAt(j))
j++;
if(j == sub.length())
return true;
i++;
}
return false;
}
public int[] getNext(String s){
int l = s.length();
char[] cc = s.toCharArray();
int[] next = new int[l];
int i = 1;
int j = 0;
while(i < l){
while(j > 0 && cc[j] != cc[i])
j = next[j - 1];
if(cc[j] == cc[i])
j++;
next[i] = j;
i++;
}
return next;
}
}
以上是关于LeetCode面试题 17.10. 主要元素/最优二叉树II(树形DP)/NC73数组中出现次数超过一半的数字/229. 求众数 II/NC134股票(无限次交易)/NC114旋转字符串的主要内容,如果未能解决你的问题,请参考以下文章