[toc]
2022-09-27
https://leetcode.cn/problems/c32eOV/
找循环链表入口,步骤如下:
先是1个快节点,1个慢节点,然后两个能相碰那就说明有环。
然后慢节点再走一遍,和快节点重新相遇,能计算出环的长度
然后再从头部开始,一个节点先走环的长度步, 然后两个节点一起开始走,相遇后就是入口了
2022-09-25
面试题 17.19. 消失的两个数字 - 力扣(LeetCode)
凡是问你找到缺的数字, 都要考虑是否用位运算
1~n中找到缺失的数字, 则你可以在造一个真正的1~n,然后异或起来即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { public int [] missingTwo(int [] nums) { int sum = 0 ; for (int i = 0 ;i<nums.length;i++) { sum ^= nums[i]; } int n = nums.length + 2 ; for (int i = 1 ;i<=n;i++) { sum ^= i; } int bits = (~(sum-1 )) & (sum); int num1 = 0 ; int num2 = 0 ; for (int num : nums) { if ((num & bits) > 0 ) { num1 ^= num; } else { num2 ^= num; } } for (int i = 1 ;i<=n;i++) { if ((i&bits) > 0 ) { num1 ^= i; } else { num2 ^= i; } } return new int []{num1, num2}; } }
2022-09-24
508. 出现次数最多的子树元素和 - 力扣(LeetCode)
搜索后用哈希表存储即可。
List快速转int[]的方法:
1 res.stream().mapToInt(Integer::intValue).toArray();
1130. 叶值的最小代价生成树 - 力扣(LeetCode)
一开始想着贪心,结果不行,并不能最前面选的是最优,那后面都是最优,即不能从上往下。
实际上要从下往上, 用动态规划存储,很容易就能处理了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution { Map<Integer, Integer> caches = new HashMap <>(); public int mctFromLeafValues (int [] arr) { int n = arr.length; int [][] dp = new int [n][n]; int [][] maxs = new int [n][n]; for (int i = 0 ; i < n;i++) { maxs[i][i] = arr[i]; } for (int len = 2 ; len <= n;len++) { for (int i = 0 ; i+len - 1 < n;i++) { int minSum = Integer.MAX_VALUE; int j = i + len - 1 ; for (int k = i;k < j;k++) { int mul = maxs[i][k] * maxs[k+1 ][j]; minSum = Math.min(minSum, mul + dp[i][k] + dp[k+1 ][j]); } dp[i][j] = minSum; for (int k = i;k<=j;k++) { maxs[i][j] = Math.max(maxs[i][j], arr[k]); } } } return dp[0 ][n-1 ]; } }
1652. 拆炸弹 - 力扣(LeetCode)
题目数据量很小,直接暴力即可,如果数据量大了,可以改成使用滑动窗口。
2022-09-23 链·表题做懵逼,注意复用方法
707. 设计链表 - 力扣(LeetCode)
这么简单的链表题为啥写了一个小时
因为数量级很小,不用维护复杂度,因此不用考虑设计一个尾节点,这会增加维护成本。
直接先写一个基础的getNode(index)方法,其他所有方法都基于这个来。
怎么避免写错或者写漏指针?
画一个图, 确认一下这次动作会有2根线变化,还是会有4根线变化,确认变化数量,从而避免遗漏。
注意next可能为空,要经常加判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 class MyLinkedList { static class Node { Node pre; Node next; int val; public Node (int val) { this .val = val; } } Node head = new Node (0 ); int count = 0 ; public MyLinkedList () { } public int get (int index) { Node node = getNode(index); return node != null ? node.val : -1 ; } public Node getNode (int index) { if (index < 0 ) { return head; } Node node = head.next; while (node != null && index>0 ) { node = node.next; index--; } return node; } public void addAtHead (int val) { Node node = new Node (val); if (head.next != null ) { head.next.pre = node; } node.next = head.next; head.next = node; node.pre = head; count++; } public void addAtTail (int val) { Node node = new Node (val); Node tail = getNode(count-1 ); tail.next = node; node.pre = tail; count++; } public void addAtIndex (int index, int val) { if (index > count) { return ; } Node node = getNode(index-1 ); Node newNode = new Node (val); Node next = node.next; node.next =newNode; newNode.pre = node; newNode.next = next; if (next != null ) { next.pre = newNode; } count--; } public void deleteAtIndex (int index) { Node node = getNode(index); if (node == null || head == node) { return ; } Node pre = node.pre; Node next = node.next; pre.next = next; if (next != null ) { next.pre = pre; } count--; } }
改进版L:
实际上addIndex就可以替代Head和tail了,避免反复写
1 2 3 4 5 6 7 8 9 public void addAtHead(int val) { addAtIndex(0, val); } public void addAtTail(int val) { addAtIndex(size, val); }
2022-09-20
划分组可以用DP而不是DFS
698. 划分为k个相等的子集 - 力扣(LeetCode)
先是最基础的DFS搜索,记忆化(因为只有16位很明显要状态压缩记忆化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 class Solution { public boolean canPartitionKSubsets (int [] nums, int k) { Arrays.sort(nums); int sum = Arrays.stream(nums).sum(); if (sum %k != 0 ) { return false ; } int subSum = sum/k; dfs(nums, k, 0 , subSum, 0 , new boolean [nums.length]); return res; } boolean res = false ; Set<Integer> set = new HashSet <>(); void dfs (int [] nums, int k, int count, int needSum, int nowSum, boolean [] vis) { if (res) { return ; } int key = 0 ; for (int i = 0 ; i < vis.length;i++) { if (vis[i]) { key += (1 <<i); } } if (set.contains(key)) { return ; } if (count == k) { int useCount = 0 ; for (int i = 0 ; i < vis.length;i++) { if (vis[i]) { useCount++; } } if (useCount == nums.length) { res = true ; } set.add(key); return ; } for (int i = 0 ; i < nums.length;i++) { if (vis[i]) { continue ; } int nextSum = nowSum + nums[i]; if (nextSum > needSum) { continue ; } vis[i] = true ; if (needSum == nextSum) { dfs(nums, k, count+1 , needSum, 0 , vis); } else { dfs(nums, k, count, needSum, nextSum, vis); } vis[i] = false ; } set.add(key); } }
其实可以用动态规划, 脑子里对这种优先想到的是 明确一个值例如1101, 去掉某个1找到他的前置状态然后判断
这种坏处在于必须按1的个数先遍历, 比较麻烦
可以从0, 找到任意一个1,尝试放入,并修改下一次的状态, 后面遇到对应状态已经设置过了就不处理了, 类似于上卖弄过程的反向的Dp。 前提你要确认Dp的状态是不会被变更的,这里的Dp和dpSum在位一致的情况下是不会更新的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class Solution { public boolean canPartitionKSubsets (int [] nums, int k) { int sum = Arrays.stream(nums).sum(); if (sum %k != 0 ) { return false ; } int subSum = sum/k; Arrays.sort(nums); int n = nums.length; boolean [] dpFlag = new boolean [1 <<n]; int [] dpSum = new int [1 <<n]; dpFlag[0 ] = true ; for (int i = 0 ; i < 1 <<n;i++) { if (!dpFlag[i]) { continue ; } for (int j = 0 ; j < n;j++) { if (dpSum[i] + nums[j] > subSum) { break ; } if ((i &(1 <<j)) > 0 ) { continue ; } int nextIndex = i | (1 <<j); if (dpFlag[nextIndex]) { continue ; } dpSum[nextIndex] = (dpSum[i] + nums[j])%subSum; dpFlag[nextIndex] = true ; } } return dpFlag[(1 <<n)-1 ]; } }
2022-09-16
第一题: 多个连续区间长度、面积问题——扫描线
多个连续区间,叠加部分要去掉或者叠加某个价值,这种题常见于那种任务流
区间的坐标很大导致无法遍历坐标值, 但可以遍历起点和终点坐标。
如果是求多个连续区间叠加后的横向长度(去除覆盖部分) ,就是最基础的扫描线应用
每次碰到关键点, 就将当前宽度(只能是0或者1)乘上(当前位置减去上一个位置)
然后直接更新上一个位置
并修改宽度(你只要考虑当前线段数量是否为0还是1)
求叠加线段长度的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public long getYLen (List<int []> yList) { Collections.sort(yList, (a,b)->(a[0 ] - b[0 ])); int last = -1 ; int count = 0 ; long sum = 0 ; for (int [] yp : yList) { int y = yp[0 ]; boolean isStart = yp[2 ] == 0 ; sum += (count>=1 ?1 :0 ) * (y - last); if (isStart) { count++; } else { count--; } last = y; } sum %= MOD; return (int )sum; }
而这题求的是面积
因此是二维的扫描线应用,即用到了两个方向上的扫描线
先是按x作为扫描点,从左往右遍历
每次遇到起点或者终点时, 先尝试计算 当前宽度 * (当前点-上一个点)
当前宽度则根据上面的y轴叠加线段的计算方式计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public int rectangleArea (int [][] rectangles) { List<int []> xlist = new ArrayList <>(); for (int i = 0 ; i < rectangles.length;i++) { int [] rect = rectangles[i]; int xLeft = rect[0 ]; int xRight = rect[2 ]; xlist.add(new int []{xLeft, i, 0 }); xlist.add(new int []{xRight, i, 1 }); } Collections.sort(xlist, (a,b)->(a[0 ] - b[0 ])); List<int []> ylist = new ArrayList <>(); int lastX = 0 ; long res = 0 ; for (int [] xp : xlist) { int x = xp[0 ]; int i = xp[1 ]; boolean isLeft = xp[2 ] == 0 ; int [] rect = rectangles[i]; int yDown = rect[1 ]; int yUp = rect[3 ]; if (isLeft) { long ylen = getYLen(ylist); long sum = ylen * (x - lastX); sum %= MOD; res += sum; res %= MOD; ylist.add(new int []{yUp, i, 1 }); ylist.add(new int []{yDown, i, 0 }); lastX = x; } else { long ylen = getYLen(ylist); long sum = ylen * (x - lastX); sum %= MOD; res += sum; res %= MOD; Iterator<int []> it = ylist.iterator(); while (it.hasNext()) { if (it.next()[1 ] == i) { it.remove(); } } lastX = x; } } return (int )res; }
2022-09-15
672. 灯泡开关 Ⅱ - 力扣(LeetCode)
这题最佳题解是找规律,逐步定位到只需要判断前面4个灯泡即可。
但是这个找规律其实并不好找,不符合计算机思维。实际上可以不用得到只判断4个灯泡这个条件。而是知道以下几点即可:
每个开关只有开奇数次才算生效。 因此要么0要么1,4个开关可以理解成4位二进制
当二进制中1的个数小于等于按压次数时,只要取余相同,则一定可以得到这种情况
那么基于这个,就可以直接模拟计算出16种按压情况下,n位灯泡的总数(用hashset)不用找那个规律。
注意编号是从1到n的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution { public int flipLights (int n, int presses) { Set<String> sets = new HashSet <>(); for (int i = 0 ; i < 16 ;i++) { int [] arr = new int [n]; int [] bits = new int [4 ]; int sum = 0 ; for (int j = 0 ; j < 4 ;j++) { bits[j] = (i>>j)&1 ; sum += bits[j]; } if (sum <= presses && sum %2 == presses %2 ) { StringBuilder sb= new StringBuilder (); for (int j = 0 ; j < arr.length;j++) { if (bits[0 ] == 1 ) { arr[j] = arr[j] ^ 1 ; } if (bits[1 ] ==1 && j %2 == 0 ) { arr[j] = arr[j] ^ 1 ; } if (bits[2 ] ==1 && j %2 == 1 ) { arr[j] = arr[j] ^ 1 ; } if (bits[3 ] ==1 && j %3 == 0 ) { arr[j] = arr[j] ^ 1 ; } sb.append(arr[j]); } sets.add(sb.toString()); } } System.out.println(sets); return sets.size(); } }
2022-09-14
第一题:前缀和+哈希或者前缀和+单调栈
1124. 表现良好的最长时间段 - 力扣(LeetCode)
又是涉及sum[i]、sun[j]然后求j-i最大的题目
然后果不其然想歪了怎么也写错了
最后要么用哈希记录最左端位置,因为是逐个按1或者-1增加前缀和的,那么当你是-5的时候,前面如果有-6,则一定是最前面的-6, 如果没有-6说明不行。 如果你是5的前缀和,那默认当前位置最长了。
要么是单调栈,但是是特殊的单调栈,不会出栈,只是构造递减的栈,然后反向遍历,这个根本想不到。
这题实际上偏难了不算中等题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution { public int longestWPI (int [] hours) { int [] pre = new int [hours.length]; int sum = 0 ; for (int i = 0 ; i < hours.length;i++) { if (hours[i] > 8 ) { sum++; } else { sum--; } pre[i] = sum; } Map<Integer, Integer> sumToIndexMap = new HashMap <>(); int res = 0 ; for (int i = 0 ; i < hours.length;i++) { if (pre[i] > 0 ) { res = Math.max(res, i + 1 ); } else { int needPre = pre[i] - 1 ; if (sumToIndexMap.containsKey(needPre)) { int lastIndex = sumToIndexMap.get(needPre); res = Math.max(res, i - lastIndex); } if (!sumToIndexMap.containsKey(pre[i])) { sumToIndexMap.put(pre[i], i); } } } return res; } }
第二题: 注意java中|也是特殊正则符号,需要转移
2315. 统计星号 - 力扣(LeetCode)
关键地方:
1 String[] ss = s.split("\\|" );
注意|的写法
第三题
1619. 删除某些元素后的数组均值 - 力扣(LeetCode)
2022-09-11
857. 雇佣 K 名工人的最低成本 - 力扣(LeetCode)
这题想不到的是要按工资比例做排序
最低工资/价值 来从小到大排序
从左到右遍历,选中一个那个就是作为最低工资标准,则前面任意都直接拿价值乘这个工资标准比率即可
而又可以根据k个的quality和可以直接乘……&这个还是蛮难想到的, 但是想懂后其实就是k个最大堆的应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution { public double mincostToHireWorkers (int [] quality, int [] wage, int k) { Integer[] indexs = new Integer [quality.length]; int n = quality.length; for (int i = 0 ; i < quality.length;i++) { indexs[i] = i; } Arrays.sort(indexs, (a,b)->(((double )wage[a]/quality[a] - (double )wage[b]/quality[b] > 0 ? 1 : -1 ))); Queue<Integer> minKQueue = new PriorityQueue <>((a,b)->(b-a)); long sum = 0 ; double res = Double.MAX_VALUE; for (int i = 0 ; i < n;i++) { int index = indexs[i]; int qua = quality[index]; int wag = wage[index]; double rate = (double )wag/qua; if (minKQueue.size() < k) { minKQueue.offer(qua); sum += qua; } else if (minKQueue.size() == k && qua < minKQueue.peek()) { sum -= minKQueue.poll(); minKQueue.offer(qua); sum += qua; } if (minKQueue.size() == k) { res = Math.min(res, sum * rate); } } return res; } }
2022-09-08
667. 优美的排列 II - 力扣(LeetCode)
规律题,还好没纠结
对于1~n的数字, 想要满足有n-1个前后差值不同,必须满足
[1, n, 2 , n-1, 3, n-2] 这样最小和最大值交叉出现
而如果是相同,则就是按顺序出现
那么想要任意k个差值不同,那就前面先都按顺序, 后面再交叉出现即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int [] constructArray(int n, int k) { int [] res = new int [n]; int index = 0 ; for (int i = 1 ; i <n-k;i++) { res[index++] = i; } int left = n-k; int right = n; while (left <=right) { if (index < n) { res[index++] = left; } if (index < n) { res[index++] = right; } left++; right--; } return res; } }
2022-09-07
第一题: 如果计算3个点是否一个直线
1037. 有效的回旋镖 - 力扣(LeetCode)
y1 - y2 / (x1-x2) = (y1-y3) / (x1-x3)
即以(x1,y1)为出发点,确定和另外两个点是否都是同一个斜率
为了防止x1-x2为0的情况,转化为相乘的形式即可
第二题:修改跟节点
1666. 改变二叉树的根节点 - 力扣(LeetCode)
其实就是按照她这个规则修改,而且就是从叶子到root修改
那就是每次要把儿子节点带上来,而且要注意修改父节点的指向,很麻烦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution { public Node flipBinaryTree (Node root, Node node) { dfs(root, node, null ); return node; } public void dfs (Node root, Node node, Node son) { if (node == null ) { return ; } if (node.left == son) { node.left = null ; } else { node.right = null ; } if (node == root) { node.parent = son; return ; } Node left = node.left; Node parent = node.parent; if (left != null ) { node.right = left; } node.left = parent; dfs(root, parent, node); node.parent = son; return ; } }
2022-09-06
1404. 将二进制表示减到 1 的步骤数 - 力扣(LeetCode)
剑指 Offer II 002. 二进制加法 - 力扣(LeetCode)
都是模拟二进制加法,都是注意反过来计算,然后最后末尾补1好一点
剑指 Offer II 093. 最长斐波那契数列 - 力扣(LeetCode)
这个问题因为涉及a+b=c,前面有a和b两个数字才能决定c, 因此状态应该是a和b共同决定,因此dp要有2位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public int lenLongestFibSubseq (int [] arr) { Map<Integer, Integer> map = new HashMap <>(); for (int i = 0 ; i < arr.length;i++) { map.put(arr[i], i); } int [][] dp = new int [arr.length+1 ][arr.length+1 ]; int res = 0 ; for (int i = 0 ; i < arr.length;i++) { int num = arr[i]; for (int j = i-1 ;j>=0 ;j--) { int num2 = arr[j]; int num3 = num - num2; if (!map.containsKey(num3) || num3 >=num2) { dp[j][i] = 2 ; } else { int index3 = map.get(num3); dp[j][i] = dp[index3][j] + 1 ; } res = Math.max(res, dp[j][i]); } } return res != 2 ? res : 0 ; }
2022-09-05
1974. 使用特殊打字机键入单词的最少时间 - 力扣(LeetCode)
1667. 修复表中的名字 - 力扣(LeetCode)
SQL中存在
1 CONCAT(UPPER (left (name, 1 )), LOWER (RIGHT (name, length(name) - 1 )))
这些函数, 可以把第一个大写,其他都小写
1741. 查找每个员工花费的总时间 - 力扣(LeetCode)
group by是可以针对2组key进行的
1 select event_day as day , emp_id, sum (out_time- in_time) as total_time from employees group by event_day,emp_id;
统计每个员工每天的工作总时间
2022-09-04 周赛
2022-09-03:双周赛
2022-09-02
第一题: 递归简单题
364. 加权嵌套序列和 II - 力扣(LeetCode)
题目比较难理解,类似于给了一个树, 注意maxDepth也就是树的最大深度的意思
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Solution { int maxDep = 0 ; public int depthSumInverse (List<NestedInteger> nestedList) { Map<Integer, Integer> counts = new HashMap <>(); for (NestedInteger nest : nestedList) { dfs(nest, 1 ); } int sum = 0 ; for (NestedInteger nest : nestedList) { sum += dfs2(nest, 1 , maxDep); } return sum; } public void dfs (NestedInteger nest, int dep) { maxDep = Math.max(maxDep, dep); if (nest.isInteger()) { return ; } else { List<NestedInteger> nexts = nest.getList(); int maxDep = 0 ; for (NestedInteger next : nexts) { dfs(next, dep + 1 ); } } } public int dfs2 (NestedInteger nest, int dep, int maxDep) { if (nest.isInteger()) { int num = nest.getInteger(); return num * (maxDep - dep + 1 ); } else { List<NestedInteger> nexts = nest.getList(); int sum = 0 ; for (NestedInteger next : nexts) { sum += dfs2(next, dep + 1 , maxDep); } return sum; } } }
第二题:对角线判断
2319. 判断矩阵是否是一个 X 矩阵 - 力扣(LeetCode)
主要考察对 正方形对角线的处理, 确定y的情况下,这一行上符合对角线的点是(y, y)和(y, xlen - 1 - y)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public boolean checkXMatrix (int [][] grid) { int ylen = grid.length; int xlen = grid[0 ].length; for (int y = 0 ; y < ylen;y++) { int x1 = y; int x2 = xlen - 1 - y; if (grid[y][x1] == 0 || grid[y][x2] == 0 ) { return false ; } for (int x = 0 ; x < xlen;x++) { if (x == x1 || x == x2) { continue ; } if (grid[y][x] != 0 ) { return false ; } } } return true ; }
第三题:加法树只要元素都一致那就是相等
1612. 检查两棵二叉表达式树是否等价 - 力扣(LeetCode)
其实无论什么顺序, 因为只有加法,都是一样的,只要元素(叶子节点)完全一致即可,也可以用map判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public boolean checkEquivalence (Node root1, Node root2) { List<Integer> list1 = new ArrayList <>(); List<Integer> list2 = new ArrayList <>(); get(root1, list1); get(root2, list2); Collections.sort(list1); Collections.sort(list2); return list1.equals(list2); } public void get (Node node, List<Integer> list) { if (node == null ) { return ; } if (node.val != '+' ) { list.add((int )node.val); } get(node.left, list); get(node.right, list) ; }
2022-09-01
第一题:简单二叉树递归
2331. 计算布尔二叉树的值 - 力扣(LeetCode)
没啥好说的直接遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public boolean evaluateTree (TreeNode root) { int val = root.val; if (val == 1 ) { return true ; } else if (val == 0 ) { return false ; } else if (val == 2 ){ return evaluateTree(root.left) || evaluateTree(root.right); } else { return evaluateTree(root.left) && evaluateTree(root.right); } } }
第二题:简单回文判断
2330. 有效的回文 IV - 力扣(LeetCode)
没看懂这题的意义是啥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public boolean makePalindrome (String s) { int left = 0 , right = s.length()-1 ; int count = 0 ; while (left < right) { if (s.charAt(left) != s.charAt(right)) { count++; } left++; right--; } return count <= 2 ; } }
第三题:贪心间隔摆放字符要求最长
这种给几种字母让你排列, 一般都是要间隔放,而且每次都是优先放大的那2个。
这题我就是每次选2个当前最大的,然后放进去。 当都放完,看下有剩余的(且肯定是单个),再找到那个字母数量都+1, 再有剩余就不处理了。和答案思路基本一致
这题里面我用了int[]来表示, 答案定义了Pair类,以后可以参考下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { public String longestDiverseString (int a, int b, int c) { List<int []> list = new ArrayList <>(); int [][] arr = new int [][]{{'a' , a},{'b' , b},{'c' ,c}}; while (arr[0 ][1 ] > 0 || arr[1 ][1 ] > 0 || arr[2 ][1 ] > 0 ) { Arrays.sort(arr, (x,y)->(y[1 ] - x[1 ])); if (arr[1 ][1 ] != 0 ) { list.add(new int []{arr[0 ][0 ], 1 }); arr[0 ][1 ]--; list.add(new int []{arr[1 ][0 ], 1 }); arr[1 ][1 ]--; } else { list.add(new int []{arr[0 ][0 ], 1 }); arr[0 ][1 ]--; for (int i = 0 ; i < list.size() && arr[0 ][1 ] > 0 ;i++) { int [] ar = list.get(i); if (ar[0 ] == arr[0 ][0 ]) { ar[1 ]++; arr[0 ][1 ]--; } } break ; } } StringBuilder sb = new StringBuilder (); for (int [] ar : list) { sb.append((char )ar[0 ]); if (ar[1 ] ==2 ) { sb.append((char )ar[0 ]); } } return sb.toString(); } }