好长时间没有写博客了,之前因为期末考试耽误了一段时间,回家又玩了几天,然后又赶来上海入职,所以就把博客这事给忘了,哈哈,懒惰啊。
题目:
/** * 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。 * 在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。 ** 注意: * 假设字符串的长度不会超过 1010。 *
* 示例 1: * 输入: * "abccccdd" * 输出: * 7 * 解释: * 我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。 */
这道题的意思很简单,给你一些字母,问你最长可以组成多长的回文字符串。那就想一下回文字符串的判定条件呗,就是中心对称,那就是说中间可以有一个不一样的字符,两边的字符出现的次数是偶数的,那就这样计算吧。
大体的思想就是先对每个字符出现的次数进行计数然后对偶数加和,只加一个奇数,其它奇数减一变偶数加和
public static int method1(String s) {
int[] a = new int[256];
for (char c : s.toCharArray()) {
a[c]++;
}
int res = 0;
boolean flag = true;
for (int i = 0; i < 256; i++) {
if (a[i] % 2 == 0) {
res += a[i];
} else {
if (flag) {
res += a[i];
flag = false;
} else {
res += a[i] - 1;
}
}
}
return res;
}
题目:
/** * 给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。 * * 示例 1: * 输入: [3, 2, 1] * 输出: 1 * 解释: 第三大的数是 1. * * 示例 2: * 输入: [1, 2] * 输出: 2 * 解释: 第三大的数不存在, 所以返回最大的数 2 . * * 示例 3: * 输入: [2, 2, 3, 1] * 输出: 1 * * 解释: 注意,要求返回第三大的数,是指第三大且唯一出现的数。 * 存在两个值为2的数,它们都排第二。 */
先给出一种不符合要求的解法,先排序在去重前三大的数,如果去重后的数少于三个就返回最大的数,否则返回第三大的数
public static int method1(int[] nums) {
if (nums.length == 1) {
return 0;
}
Arrays.sort(nums);
int[] results = new int[3];
int num = 0;
for (int i = nums.length - 1; i >= 0; i--) {
if (num < 3 && nums[i] != results[num - 1 < 0 ? 0 : num - 1]) {
results[num++] = nums[i];
}
}
if (num < 3) {
return results[0];
} else {
return results[2];
}
}
很明显它的复杂度是超过了O(n)的,sort()的复杂度是O(nlgn)
其实这道题也不难想,就是记录前三大的数都是多少,然后遇到新的数就不断传递就好了,再开始要判断一下数组长度,还有就是在传递数字的时候记录一下改变了多少次,以此来判断有没有第三大的数。
public static int method2(int[] nums) {
if (nums.length == 1) {
return nums[0];
}
if (nums.length == 2) {
return Math.max(nums[0], nums[1]);
}
int max1 = Integer.MIN_VALUE;
int max2 = Integer.MIN_VALUE;
int max3 = Integer.MIN_VALUE;
boolean f = true;
int flag = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == Integer.MIN_VALUE && f) {
flag++;
f = false;
}
if (nums[i] > max1) {
flag++;
//原先第二大传递给第三大
max3 = max2;
//原先最大值传递给第二大
max2 = max1;
//更新最大值
max1 = nums[i];
} else if (nums[i] > max2 && nums[i] < max1) {
flag++;
max3 = max2;
max2 = nums[i];
} else if (nums[i] > max3 && nums[i] < max2) {
flag++;
max3 = nums[i];
}
}
return flag >= 3 ? max3 : max1;
}
还是相同的思想,我们来简化一下代码,不使用flag来计数判断是否有第三大的数了,而是在最后判断表示第三大的变量是否改变过,这样判断第三大的数是否出现过。
public static int method3(int[] nums) {
long first = Long.MIN_VALUE, second = Long.MIN_VALUE, third = Long.MIN_VALUE;
for (long num : nums) {
if (num > first) {
third = second;
second = first;
first = num;
} else if (num > second && num < first) {
third = second;
second = num;
} else if (num > third && num < second) {
third = num;
}
}
return third == Long.MIN_VALUE ? (int) first : (int) third;
}
题目:
/** * 给定一个二叉树,它的每个结点都存放着一个整数值。 * 找出路径和等于给定数值的路径总数。 * 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 * 二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。 * * 示例: * root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 * * 10 * / \ * 5 -3 * / \ \ * 3 2 11 * / \ \ * 3 -2 1 * * 返回 3。和等于 8 的路径有: * * 1. 5 -> 3 * 2. 5 -> 2 -> 1 * 3. -3 -> 11 */ public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } }
一看这道题就感觉要用递归解,其实很多树的题用递归来解都非常合适,有一个比较简单的思路来解这道题,就是以每一个节点作为根节点向下寻找是否有符合条件的路径,有的话就把路径数加一,可以用目标数值减去每一个节点,如果可以得到零的话就说明这是一条符合条件的路径,因为可能出现负值,所以不必对下于零的情况做处理。
private static int pathnumber;
public int method1(TreeNode root, int sum) {
if(root == null) return 0;
Sum(root,sum);
method1(root.left,sum);
method1(root.right,sum);
return pathnumber;
}
public static void Sum(TreeNode root, int sum){
if(root == null) return;
sum-=root.val;
if(sum == 0){
pathnumber++;
}
Sum(root.left,sum);
Sum(root.right,sum);
}
简化一下代码就是这个样子,把计数也合到递归里面
public int method2(TreeNode root, int sum) {
if (root == null) return 0;
return method2(root.left, sum) + method2(root.right, sum) + dfs(root, sum);
}
public static int dfs(TreeNode node, int sum) {
if (node == null) return 0;
int count = 0;
if (node.val == sum) count = 1;
return count + dfs(node.left, sum - node.val) + dfs(node.right, sum - node.val);
}
换汤不换药,可以使用Map来记录每个数值出现的次数,如果加和等于目标数值了便计数,比较重要的有两句话,开始的,<0,1>键值对和最后的回溯。
public static int method3(TreeNode root, int sum) {
HashMap map = new HashMap();
//<和,次数>
map.put(0, 1);
return FindSubTree(root,0,sum,map);
}
public static int FindSubTree(TreeNode root,int sum,int target,Map map) {
if(root==null) {
return 0;
}
sum +=root.val;
int res = map.getOrDefault(sum-target, 0);
map.put(sum, map.getOrDefault(sum, 0)+1);
res +=FindSubTree(root.left,sum,target,map);
res +=FindSubTree(root.right,sum,target,map);
//回溯
map.put(sum, map.get(sum)-1);
return res;
}
题目:
/** * 给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。 * * 例如,给定一个 3叉树 : * LevelOrder.java * * 返回其层序遍历: * * [ * [1], * [3,2,4], * [5,6] * ] * * 说明: * 树的深度不会超过 1000。 * 树的节点总数不会超过 5000。 * */ class Node { public int val; public Listchildren; public Node() {} public Node(int _val,List _children) { val = _val; children = _children; } };
其实很简单,和树的层次遍历一样,可以使用两个队列,先把根节点放到一个队列里,然后取出来的时候加入一个list,再把子节点放到另一个队列里,取出来的时候加入一个list里面,这样不断在两个队列里面捣腾,直到两个队列都为空。
public List> method1(Node root) {
Queue nodeQueueA=new LinkedBlockingQueue<>();
Queue nodeQueueB=new LinkedBlockingQueue<>();
List> lists=new ArrayList<>();
if (root!=null){
nodeQueueA.offer(root);
}
while (!nodeQueueA.isEmpty()||!nodeQueueB.isEmpty()){
List integers=new ArrayList<>();
while (!nodeQueueA.isEmpty()){
Node node=nodeQueueA.poll();
integers.add(node.val);
for (Node node1:node.children){
nodeQueueB.offer(node1);
}
}
if (!integers.isEmpty()){
lists.add(integers);
integers=new ArrayList<>();
}
while (!nodeQueueB.isEmpty()){
Node node=nodeQueueB.poll();
integers.add(node.val);
for (Node node1:node.children){
nodeQueueA.offer(node1);
}
}
if (!integers.isEmpty()) {
lists.add(integers);
}
}
return lists;
}
也可以使用一个队列,这样你就需要控制每一次取出来多少个了,用以实现分层,每次要把当前队列中节点的所有子节点放入一个list,并把所有子节点放入队列。
public List> method2(Node root) {
Queue nodeQueue=new LinkedBlockingQueue<>();
List> lists=new ArrayList<>();
if (root!=null){
nodeQueue.offer(root);
List integers=new ArrayList<>();
integers.add(root.val);
lists.add(integers);
}
while (!nodeQueue.isEmpty()){
List integers=new ArrayList<>();
int size=nodeQueue.size();
for (int i=0;i
简化版本
public List> method3(Node root) {
List> result = new LinkedList<>();
if (root == null) {
return result;
}
Node head = root;
Queue queue = new LinkedList<>();
queue.add(head);
while (!queue.isEmpty()) {
int size = queue.size();
List nextLayer = new ArrayList<>();
List layer = new ArrayList<>();
for (int i = 0; i < size; i++) {
Node node = queue.poll();
nextLayer.addAll(node.children);
layer.add(node.val);
}
result.add(layer);
queue.addAll(nextLayer);
}
return result;
}
还有一种办法就是用dfs,递归的时候带上层数,这样就知道加到哪个list里面了
private static List> list = new ArrayList<>();
public static List> method4(Node root) {
dfs(root, 0);
return list;
}
private static void dfs(Node root, int level){
if(root == null){
return;
}
if(list.size() l = new ArrayList<>();
list.add(l);
}
list.get(level).add(root.val);
for (Node node : root.children){
dfs(node, level+1);
}
}
题目:
/** * 给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。 * 字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。 * 说明: * 字母异位词指字母相同,但排列不同的字符串。 * 不考虑答案输出的顺序。 * * 示例 1: * 输入: * s: "cbaebabacd" p: "abc" * 输出: * [0, 6] * 解释: * 起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。 * 起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。 * * 示例 2: * 输入: * s: "abab" p: "ab" * 输出: * [0, 1, 2] * 解释: * 起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。 * 起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。 * 起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。 */
字母异位词很好判断,只要里面所包含的字母出现的次数一致就叫字母异位词,一个很明显的思路就是存储s子串的字母构成,如果和p一样就记录其实索引,往后不断抛弃s子串尾的字母,加入新的字母进行比对。
public static List method2(String s, String p) {
List result = new ArrayList<>();
int[] p_letter = new int[26];
for (int i = 0; i < p.length(); i++) {
//记录p里面的数字分别有几个
p_letter[p.charAt(i) - 'a']++;
}
int start = 0; int end = 0;
int[] between_letter = new int[26];
//记录两个指针之间的数字都有几个
while (end < s.length()) {
int c = s.charAt(end++) - 'a';
//每一次拿到end指针对应的字母
between_letter[c]++;
//让这个字母的数量+1 如果这个字母的数量比p里面多了,说明这个start坐标需要排除
while (between_letter[c] > p_letter[c]) {
between_letter[s.charAt(start++) - 'a']--;
}
if (end - start == p.length()) {
result.add(start);
}
}
return result;
}
public static List method3(String s, String p) {
List list = new ArrayList<>();
int[] record = new int[128];
for(char c: p.toCharArray()){
record[c]++;
}
char[] arr = s.toCharArray();
int l=0, r=0;
while(r0){
record[arr[r]]--;
r++;
if((r-l) == p.length()){
list.add(l);
}
}
else{
record[arr[l]]++;
l++;
}
}
return list;
}