337 打家劫舍 III
动态规划,之前做过 ,后序遍历,递归时计算当前节点的最大偷窃金额,偷当前节点则不能偷左右孩子,偷左右孩子则不能偷当前节点
class Solution {
public int rob ( TreeNode root) {
int [ ] res = dfs ( root) ;
return Math . max ( res[ 0 ] , res[ 1 ] ) ;
}
private int [ ] dfs ( TreeNode root) {
int [ ] res = new int [ 2 ] ;
if ( root == null ) return res;
int [ ] left = dfs ( root. left) ;
int [ ] right = dfs ( root. right) ;
res[ 0 ] = Math . max ( left[ 0 ] , left[ 1 ] ) + Math . max ( right[ 0 ] , right[ 1 ] ) ;
res[ 1 ] = root. val + left[ 0 ] + right[ 0 ] ;
return res;
}
}
338 比特位计数
class Solution {
public int [ ] countBits ( int n) {
int [ ] res = new int [ n + 1 ] ;
for ( int i = 0 ; i <= n; i++ ) {
res[ i] = count ( i) ;
}
return res;
}
private int count ( int n) {
int res = 0 ;
while ( n != 0 ) {
res++ ;
n &= ( n - 1 ) ;
}
return res;
}
}
动态规划,x通过右移一位去掉最低位得到y,若x为偶数,则最低位为0,1比特位和y相同;若x为奇数,则最低位为1,1比特位比y多1。递推:bits[x]=bits[x>>1]+(x & 1)
class Solution {
public int [ ] countBits ( int n) {
int [ ] res = new int [ n + 1 ] ;
for ( int i = 0 ; i <= n; i++ ) {
res[ i] = res[ i >> 1 ] + ( i & 1 ) ;
}
return res;
}
}
347 前 K 个高频元素
优先级队列(前k大用小顶堆,前k小用大顶堆),之前做过 ,先统计数组中的数出现频率,然后使用小顶堆,复习:手动实现优先级队列
class Solution {
public int [ ] topKFrequent ( int [ ] nums, int k) {
Map < Integer , Integer > map = new HashMap < > ( ) ;
int [ ] res = new int [ k] ;
for ( int num : nums) {
if ( ! map. containsKey ( num) ) {
map. put ( num, 0 ) ;
}
map. put ( num, map. get ( num) + 1 ) ;
}
PriorityQueue < Map. Entry < Integer , Integer > > priorityQueue = new PriorityQueue < > ( ( o1, o2) -> o1. getValue ( ) - o2. getValue ( ) ) ;
for ( Map. Entry < Integer , Integer > entry : map. entrySet ( ) ) {
if ( priorityQueue. size ( ) < k) {
priorityQueue. offer ( entry) ;
} else {
if ( priorityQueue. peek ( ) . getValue ( ) < entry. getValue ( ) ) {
priorityQueue. poll ( ) ;
priorityQueue. offer ( entry) ;
}
}
}
for ( int i = 0 ; i < k; i++ ) {
res[ i] = priorityQueue. poll ( ) . getKey ( ) ;
}
return res;
}
}
394 字符串解码
考虑到嵌套问题使用栈,类似150 逆波兰表达式求值 ,定义两个栈,一个保存数字,一个保存字符串,遍历字符串,当为数字时计算其值(可能有两位数),当为字母时拼接字符串之后,当为左括号时将当前字符串和数字入栈,当为右括号时,数字和字符串出栈,当前字符串重复后拼接到字符串(上一个字符串)之后。 注意:出栈时的字符串为上一字符串,当前未入栈的字符串和出栈的数字对应。
class Solution {
public String decodeString ( String s) {
Deque < String > stackS = new LinkedList < > ( ) ;
Deque < Integer > stackN = new LinkedList < > ( ) ;
int num = 0 ;
StringBuilder sb = new StringBuilder ( ) ;
for ( char c : s. toCharArray ( ) ) {
if ( c >= '0' && c <= '9' ) {
num = 10 * num + ( c - '0' ) ;
} else if ( c == '[' ) {
stackS. push ( sb. toString ( ) ) ;
stackN. push ( num) ;
sb = new StringBuilder ( ) ;
num = 0 ;
} else if ( c == ']' ) {
int cycleNum = stackN. pop ( ) ;
String preString = stackS. pop ( ) ;
StringBuilder temp = new StringBuilder ( ) ;
for ( int i = 0 ; i < cycleNum; i++ ) {
temp. append ( sb) ;
}
sb = new StringBuilder ( preString + temp) ;
} else {
sb. append ( c) ;
}
}
return sb. toString ( ) ;
}
}
399 除法求值
图论问题,广度优先搜索,遍历输入数组,使用哈希表将点映射为正整数标号(key:点对应字符串,value:正整数递增标号),定义边数组存储每个点到其连接的点的标号及权重(下标位置为起点标号)。然后遍历查询数组,从哈希表中查询点对应标号,若不存在标号,则为-1;若存在标号,则再根据标号到边数组中查询,一一对比得到查询两点的比值。
时间复杂度O((|输入方程长度|+|查询方程长度|)*|方查询次数| + 输入方程长度 * 查询方程长度
),空间复杂度O(|方程中不同字符的个数|
)
class Solution {
public double [ ] calcEquation ( List < List < String > > equations, double [ ] values, List < List < String > > queries) {
Map < String , Integer > nodeMap = new HashMap < > ( ) ;
int count = 0 ;
for ( List < String > equ : equations) {
if ( ! nodeMap. containsKey ( equ. get ( 0 ) ) ) {
nodeMap. put ( equ. get ( 0 ) , count++ ) ;
}
if ( ! nodeMap. containsKey ( equ. get ( 1 ) ) ) {
nodeMap. put ( equ. get ( 1 ) , count++ ) ;
}
}
List < Pair > [ ] edgeList = new List [ count] ;
for ( int i = 0 ; i < count; i++ ) {
edgeList[ i] = new ArrayList < Pair > ( ) ;
}
for ( int i = 0 ; i < equations. size ( ) ; i++ ) {
int src = nodeMap. get ( equations. get ( i) . get ( 0 ) ) ;
int dst = nodeMap. get ( equations. get ( i) . get ( 1 ) ) ;
edgeList[ src] . add ( new Pair ( dst, values[ i] ) ) ;
edgeList[ dst] . add ( new Pair ( src, 1.0 / values[ i] ) ) ;
}
double [ ] result = new double [ queries. size ( ) ] ;
Arrays . fill ( result, - 1.0 ) ;
for ( int i = 0 ; i < queries. size ( ) ; i++ ) {
List < String > que = queries. get ( i) ;
double v = - 1.0 ;
if ( nodeMap. containsKey ( que. get ( 0 ) ) && nodeMap. containsKey ( que. get ( 1 ) ) ) {
int src = nodeMap. get ( que. get ( 0 ) ) ;
int dst = nodeMap. get ( que. get ( 1 ) ) ;
Queue < Integer > queue = new LinkedList < > ( ) ;
queue. offer ( src) ;
double [ ] target = new double [ count] ;
Arrays . fill ( target, - 1.0 ) ;
target[ src] = 1.0 ;
while ( ! queue. isEmpty ( ) && target[ dst] < 0 ) {
int cur = queue. poll ( ) ;
for ( Pair pair : edgeList[ cur] ) {
int next = pair. dst;
double val = pair. value;
if ( target[ next] < 0 ) {
target[ next] = target[ cur] * val;
queue. offer ( next) ;
}
}
}
result[ i] = target[ dst] ;
}
}
return result;
}
class Pair {
int dst;
double value;
Pair ( int dst, double value) {
this . dst = dst;
this . value = value;
}
}
}
floyd算法* 带权并查集*
时间复杂度O((|输入方程长度|+|查询方程长度|)*log|方程中不同字符的个数|
),空间复杂度O(|方程中不同字符的个数|
)
406 根据身高重建队列
贪心,之前做过 ,使用list,身高从高到底排列,身高相同的前面人数更少的优先,然后按顺序插入队列中,插入时根据前面人数进行插入即可(队列中的都是比当前高的)
class Solution {
public int [ ] [ ] reconstructQueue ( int [ ] [ ] people) {
ArrayList < int [ ] > res = new ArrayList < > ( ) ;
Arrays . sort ( people, ( o1, o2) -> {
if ( o1[ 0 ] == o2[ 0 ] ) return o1[ 1 ] - o2[ 1 ] ;
return o2[ 0 ] - o1[ 0 ] ;
} ) ;
for ( int [ ] p : people) {
res. add ( p[ 1 ] , p) ;
}
return res. toArray ( new int [ people. length] [ ] ) ;
}
}
416 分割等和子集
动态规划,01背包,之前做过 ,相当于target为数组总和的一半,物品和能否达到target。定义dp数组dp[i]
表示容量为i的背包放入数之和的最大值(数的重量=其值)。注意:一维滚动数组优化先物品后背包(大到小)
时间复杂度O(n*target),空间复杂度O(target)
class Solution {
public boolean canPartition ( int [ ] nums) {
if ( nums. length == 1 ) return false ;
int sum = 0 ;
for ( int num : nums) {
sum += num;
}
if ( sum % 2 == 1 ) return false ;
int target = sum / 2 ;
int [ ] dp = new int [ target + 1 ] ;
for ( int i = 0 ; i < nums. length; i++ ) {
for ( int j = target; j >= nums[ i] ; j-- ) {
dp[ j] = Math . max ( dp[ j] , dp[ j - nums[ i] ] + nums[ i] ) ;
}
}
return dp[ target] == target;
}
}
437 路径总和 III
class Solution {
public int pathSum ( TreeNode root, int targetSum) {
if ( root == null ) return 0 ;
int res = preOrder ( root, targetSum) ;
res += pathSum ( root. left, targetSum) ;
res += pathSum ( root. right, targetSum) ;
return res;
}
private int preOrder ( TreeNode root, int targetSum) {
if ( root == null ) return 0 ;
int res = 0 ;
if ( root. val == targetSum) {
res++ ;
}
res += preOrder ( root. left, targetSum - root. val) ;
res += preOrder ( root. right, targetSum - root. val) ;
return res;
}
}
前缀和,定义hashmap保存root到当前节点的前缀和,遍历到当前节点时判断是否存在cur - target
的前缀和,相当于以当前节点为终点,向上查找起点。补充:getOrDefault()
可以自动判断map是否存在key
class Solution {
public int pathSum ( TreeNode root, int targetSum) {
if ( root == null ) return 0 ;
Map < Integer , Integer > map = new HashMap < > ( ) ;
map. put ( 0 , 1 ) ;
return preOrder ( root, targetSum, map, 0 ) ;
}
private int preOrder ( TreeNode root, int targetSum, Map < Integer , Integer > map, int cur) {
if ( root == null ) return 0 ;
int res = 0 ;
cur += root. val;
if ( map. containsKey ( cur - targetSum) ) {
res += map. get ( cur - targetSum) ;
}
if ( ! map. containsKey ( cur) ) {
map. put ( cur, 0 ) ;
}
map. put ( cur, map. get ( cur) + 1 ) ;
res += preOrder ( root. left, targetSum, map, cur) ;
res += preOrder ( root. right, targetSum, map, cur) ;
map. put ( cur, map. get ( cur) - 1 ) ;
return res;
}
}
438 找到字符串中所有字母异位词
滑动窗口,之前做过 ,设置滑动窗口长度与p长度相同,不断移动窗口,定义数组作为哈希表(一共26个字母)存储字符出现次数,若窗口内字符出现次数符合条件,则记录窗口左下标。
class Solution {
public List < Integer > findAnagrams ( String s, String p) {
List < Integer > res = new ArrayList < > ( ) ;
if ( s. length ( ) < p. length ( ) ) return res;
int [ ] fre = new int [ 26 ] ;
for ( char c : p. toCharArray ( ) ) {
fre[ c - 'a' ] ++ ;
}
for ( int i = 0 ; i < s. length ( ) - p. length ( ) + 1 ; i++ ) {
int [ ] fre2 = new int [ 26 ] ;
for ( int j = i; j < i + p. length ( ) ; j++ ) {
fre2[ s. charAt ( j) - 'a' ] ++ ;
}
if ( Arrays . equals ( fre, fre2) ) {
res. add ( i) ;
}
}
return res;
}
}
448 找到所有数组中消失的数字
将原数组当作哈希表,遍历一遍数组,将当前数对应位置上的数加n,第二次遍历时若某个位置上的数不大于n,则说明该数未出现过。
class Solution {
public List < Integer > findDisappearedNumbers ( int [ ] nums) {
List < Integer > res = new ArrayList < > ( ) ;
int n = nums. length;
for ( int num : nums) {
int i = ( num - 1 ) % n;
nums[ i] += n;
}
for ( int i = 0 ; i < n; i++ ) {
if ( nums[ i] <= n) {
res. add ( i + 1 ) ;
}
}
return res;
}
}