当前位置: 首页 > news >正文

算法学习day29

一、乘法表中第k小的数(和有序矩阵中第k小的数类似)

题意:

乘法表是大小为 m x n 的一个整数矩阵,其中 mat[i][j] == i * j(下标从 1 开始)。

给你三个整数 mn 和 k,请你在大小为 m x n 的乘法表中,找出并返回第 k 小的数字。

输入:m = 3, n = 3, k = 5
输出:3
解释:第 5 小的数字是 3 。
思路:

如何在乘法表中寻找第K小的数字?

我们可以想象这些数字都在一维数组中,最小值是1,最大值是m*n(行*列)。

要在这里面寻找第k小的数字,可以使用二分法,每次选取中间值,然后判断中间值在乘法表中是第几小的数字,然后跟k作比较

如果mid>=k,说明第k小的元素是在左边的,此时更新right;right=mid;


如果mid<k,说明第k小的元素是在右边的,此时更新left;  left=mid+1;

为什么index<k而不是index<=k? 当index=k的时候,left=mid+1;可能会使left错失正确值。如果mid==k,那么最后的结果是left=right=mid;

此时选择mid>=k,刚好是right=mid;

如果选择mid<=k,那么left会+1;

注意:如何寻找num在乘法表中是第几小的?

因为乘法表是从左往右、从上往下依次递增的。可以以列为单位去寻找

1.以列为单位,从最后一行的第一列开始寻找。x=nums.length-1 y=0;if(nums[x][y]<=k)index+=x;

else x--;

代码:
class Solution {public int findKthNumber(int m, int n, int k) {int left=1,right=m*n;while(left<right){int mid=left+(right-left)/2;int count=getIndexOfNum(m,n,mid);System.out.println("mid: "+mid+" count: "+count);if(count<k){left=mid+1;}else{right=mid;}}return left;}//该函数是寻找mid是在m*n乘法表中的第几小的数字public int getIndexOfNum(int m,int n,int mid){int x=m;int y=1;int res=0;while(x>=1&&y<=n){if(x*y<=mid){res+=x;y++;}else{x--;}}return res;}
}

二、任务调度器(贪心算法)

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表,用字母 A 到 Z 表示,以及一个冷却时间 n。每个周期或时间间隔允许完成一项任务。任务可以按任何顺序完成,但有一个限制:两个 相同种类 的任务之间必须有长度为 n 的冷却时间。返回完成所有任务所需要的 最短时间间隔 。

输入:tasks = ["A","A","A","B","B","B"], n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。 
思路:

为了在更短的时间之内完成调度任务。我们就要在待命期间安排上其它种类的任务。待命期间的长短是由n来决定的。然后要进行多少次这样的操作是由任务数量最多的种类决定的。 就算我们不执行其它任务,我们也要去等冷却时间完毕之后执行该任务。

数量最多的任务max 和 冷却时间n 以及和 同样拥有最.

多数量的种类type 决定了一个范围,(max-1)*n+type

如果在这个范围里面,其他种类的任务不足够执行完,那么结果就是tasks.length;

代码:
class Solution {public int leastInterval(char[] tasks, int n) {Map<Character,Integer> map=new HashMap<>();for(Character ch:tasks){map.put(ch,map.getOrDefault(ch,0)+1);}//1.计算出某个任务的数量最多是多少int max=0;int count=0;Set<Character> set=map.keySet();for(Character ch:set){max=Math.max(max,map.get(ch));}//2.计算出和该任务相同数量的任务有多少for(Character ch:set){if(map.get(ch)==max)count++;}System.out.println("任务数量最多的是:"+max+"相同任务:"+count);return Math.max((max-1)*(n+1)+count,tasks.length);}
}

三、最大数

给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

输入nums = [3,30,34,5,9]
输出:"9534330"
思路:

我们要做的就是将第一位数字放到最前面,这样拼接形成的数字也是最大的。我们可以使用字符串的compareTo()方法,eg:"abc".compareTo("acc");在比较第二个字符bc的时候,b<c所以返回负数(-1)。那我们就可以自定义一个排序规则,使得拼接更大的数字在前面(降序排列)。

Arrays(strs,(a,b)->{

    return (b+a).compareTo(a+b);

});

注意:

如果第一个数字是0的话,那么后面的数字都是0,此时按照正常逻辑拼接的话,会返回“00”;因此该种情况直接返回“0”;

代码: 
class Solution {public String largestNumber(int[] nums) {StringBuilder sb=new StringBuilder();String[] strs=new String[nums.length];for(int i=0;i<nums.length;i++){strs[i]=String.valueOf(nums[i]);}Arrays.sort(strs,(a,b)->{return (b+a).compareTo(a+b);});if(strs[0].equals("0")){return "0";}for(String str:strs){sb.append(str);}return sb.toString();}
}

四、合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]
思路:

首先将二维数组按照升序排序---->自定义一个排序规则。Arrays.sort(intervals,(a,b)->{

return a[0]-b[0];});

排好序之后,对数组进行遍历,使用临时数组cur指向前一个集合(方便合并)

if(intervals[i][0]<=cur[1])说明下一个范围和上一个范围有重合的地方。此时我们更新cur[1]=Math.max(cur[1],intervals[i][1]);为什么不用更新cur[0]呢。 因为我们按照升序排列,cur[0]一定是小于Intervals[i][0]的

else 直接把cur加入到集合中 cur=intervals[i];

代码:
class Solution {public int[][] merge(int[][] intervals) {if(intervals.length==1)return intervals;//对数组进行升序排Arrays.sort(intervals,(a,b)->{return a[0]-b[0];});List<int[]> list=new ArrayList<>();int[] cur=intervals[0];for(int i=1;i<intervals.length;i++){if(intervals[i][0]<=cur[1]){cur[1]=Math.max(intervals[i][1],cur[1]);}else{list.add(cur);cur=intervals[i];}}list.add(cur);int[][] res=new int[list.size()][2];for(int i=0;i<list.size();i++){res[i]=list.get(i);}return res;}
}

五、插入区间

题意:

给你一个 无重叠的 ,按照区间起始端点排序的区间列表 intervals,其中 intervals[i] = [starti, endi] 表示第 i 个区间的开始和结束,并且 intervals 按照 starti 升序排列。

样给定一个区间 newInterval = [start, end] 表示另一个区间的开始和结束。

在 intervals 中插入区间 newInterval,使得 intervals 依然按照 starti 升序排列,且区间之间不重叠(如果有必要的话,可以合并区间)。

思路:

这个题就是让我们把一个区间插入到区间数组里一个合适的地方。

我们可以先将重合部分的左边加入到res中,然后加入重叠部分合成的区域、然后加入右边部分的区域。

代码:
class Solution {public int[][] insert(int[][] intervals, int[] newInterval) {// 分三种情况讨论 重叠左边 重叠部分 重叠右边// 1.左边int index = 0, size = intervals.length;List<int[]> list = new ArrayList<>();while (index < size && intervals[index][1] < newInterval[0]) {list.add(intervals[index]);index++;}// 2.中间while (index < size && intervals[index][0] <= newInterval[1] && newInterval[0] <= intervals[index][1]) {newInterval[0] = Math.min(newInterval[0], intervals[index][0]);newInterval[1] = Math.max(newInterval[1], intervals[index][1]);index++;}list.add(newInterval);// 3.右边while (index < size && intervals[index][0] > newInterval[1]) {list.add(intervals[index++]);}// 转换为int[][]数组int[][] res = new int[list.size()][2];for (int i = 0; i < list.size(); i++) {res[i] = list.get(i);}return res;}
}

六、最长数对链(贪心)

给你一个由 n 个数对组成的数对数组 pairs ,其中 pairs[i] = [lefti, righti] 且 lefti < righti 。

现在,我们定义一种 跟随 关系,当且仅当 b < c 时,数对 p2 = [c, d] 才可以跟在 p1 = [a, b] 后面。我们用这种形式来构造 数对链 。

思路:

有题目可知,要使数对链最长。我们就要使每次结尾的尾部是最小的,这样我们成为最长的可能就最大。

1.那我们就应该对数组进行排序Arrays.sort(pairs,(a,b)->{return a[1]-b[1]});

2.排序完之后,对数组进行遍历就可。

class Solution {public int findLongestChain(int[][] pairs) {Arrays.sort(pairs,(a,b)->{return a[1]-b[1];});int count=1;int[] cur=pairs[0];for(int i=1;i<pairs.length;i++){if(pairs[i][0]>cur[1]){count++;cur=pairs[i];}}return count;}
}

七、摆动排序II

给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

思路:

从排列中我们可以看出 小大小大...。我们可以将数组分成两部分:小数部分和大数部分。

eg:123456 小数部分123 大数部分456 小数放一个大数放一个 142536

但是如果正序放的话,会遇到这种情况 eg:1336 13/36  1336 这样中间两个值就相同了。

注意:

为了防止中间值相同的情况,倒序交叉排序:1336  13/36  3661 从后往前选择

为什么倒序可以?如果倒序所选的两个值还相同的话,倒序中间最少差一个 也就是axb,这样三个值都相同了,而且一共四个值。题目不会出现这样的输入的。

代码:
class Solution {public void wiggleSort(int[] nums) {int[] clone=nums.clone();Arrays.sort(clone);int N=nums.length-1;for(int i=1;i<clone.length;i+=2){nums[i]=clone[N--];}for(int i=0;i<clone.length;i+=2){nums[i]=clone[N--];}}
}

八、参议院

题意:
给你一串字符串,其中包括R阵营\D阵营(R参议员和D参议员);参议员有禁止一名参议员的权利,如果在某轮投票中发现参议院都是一方的。那么就宣布该阵营胜利
思路:

遍历数组

1.当我们遇到R的时候,我们需要判断前面是否有还未使用权利的D;

2.在遇到D的时候,也要判断前面是否有未使用权利的R;

我们使用一个int整数类型的flag变量来记录。如果flag<0,说明前面有D;如果flag>0,说明前面有R.

其次,我们需要判断所有的参议院中是否只有一个阵营的或者两个阵营都有。

boolean D;boolean R。true代表某个阵营存在。

注意:

比较不只是一轮,直到比出结果,循环才会结束。因此在最外层有while循环。

代码:
class Solution {public String predictPartyVictory(String senate) {// 1.禁止权利2.如果有权利投票的参议员都是一个阵营的 胜利// R 天辉 D 夜魇boolean R = true, D = true;int flag = 0;// 如果flag<0说明在这个R前面有D;flag>0说明在这个D前面有Rchar[] senates = senate.toCharArray();while (R && D) {R = false;D = false;for (int i = 0; i < senates.length; i++) {char ch = senates[i];if (ch == 'R') {if (flag < 0)senates[i]='0';else R=true;flag++;}else if(ch=='D'){if (flag > 0)senates[i]='0';else D=true;flag--; }}}return R==true?"Radiant":"Dire";}
}

九、有效的括号字符串(双栈)

给你一个只包含三种字符的字符串,支持的字符类型分别是 '('')' 和 '*'。请你检验这个字符串是否为有效字符串,如果是 有效 字符串返回 true 。

*可以代替(或者) 或者"";

思路:

左括号的数量为:a;*的数量为:b;右括号的数量为:c

利用双栈,一个栈用来存储左括号、另一个栈用来存储*。(栈里面是存放该字符的下标)

1.遍历字符串,遇到左括号就入栈1,遇到*就入栈2;

如果遇到右括号,先判断栈1是否为空;再去判断栈2是否为空。如果栈1栈2都为空直接return false;a>b+c; 无法匹配

如果左括号为空,那么就去判断*,a>b 但是a<b+c;

2.遍历结束之后,如果left栈为空,此时 a=b+c 直接返回true;如果left栈不为空,可能存在a<b的情况,此时就需要*还消除(;但是只有下标在(左边的*才能起到消除作用;

while(!left.isEmpty()){

int a=left.pop();

int b=star.pop();

此时a是最靠近右边的左括号 b是最靠近右边的*号

if(a>b)return false;栈顶的*号都匹配不到,那么其他的也匹配不到

}

代码:
class Solution {public boolean checkValidString(String s) {Stack<Integer> left=new Stack<>();Stack<Integer> star=new Stack<>();for(int i=0;i<s.length();i++){char ch=s.charAt(i);if(ch=='(')left.push(i);else if(ch=='*')star.push(i);else{if(!left.isEmpty()){left.pop();continue;}if(!star.isEmpty()){star.pop();continue;}return false;}}while(!left.isEmpty()){if(star.isEmpty())return false;else{int i=left.pop();int j=star.pop();if(i>j)return false;}}return true;}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • AWS生成式AI项目的全生命周期管理
  • Qt pro文件详解
  • 掌握Dism++,让你的Windows系统更加清爽、流畅!
  • MyIP:强大且简单好用!
  • Langchain-Chatchat+Xinference集成部署
  • 【线性代数】汤家凤线性代数辅导讲义整理
  • Java中基本数据类型包装类的常量池缓存的值得范围是多少?
  • Linux:账号和权限管理(二)
  • 掀起 .NET 风暴:用 Docker 快速打造并部署你的炫酷应用!
  • the request was rejected because no multipart boundary was found
  • FP8量化
  • 精益生产管理培训机构怎么选?三大维度助你精准定位
  • 从科幻到现实:AIGC助力打造个性化数字人
  • MySQL:先插入数据库,然后再查询
  • linux shell 函数
  • ECMAScript6(0):ES6简明参考手册
  • gitlab-ci配置详解(一)
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • java多线程
  • jdbc就是这么简单
  • learning koa2.x
  • php面试题 汇集2
  • React Native移动开发实战-3-实现页面间的数据传递
  • Spring Cloud中负载均衡器概览
  • vue脚手架vue-cli
  • zookeeper系列(七)实战分布式命名服务
  • 服务器从安装到部署全过程(二)
  • 计算机在识别图像时“看到”了什么?
  • 深度学习中的信息论知识详解
  • 温故知新之javascript面向对象
  • 你对linux中grep命令知道多少?
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #if #elif #endif
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (每日一问)操作系统:常见的 Linux 指令详解
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (四)库存超卖案例实战——优化redis分布式锁
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .Net mvc总结
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • @Conditional注解详解
  • @media screen 针对不同移动设备
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [AIGC codze] Kafka 的 rebalance 机制
  • [Android]常见的数据传递方式
  • [Bug]使用gradio创建应用提示AttributeError: module ‘gradio‘ has no attribute ‘inputs‘
  • [BZOJ 2142]礼物(扩展Lucas定理)
  • [C#]C# OpenVINO部署yolov8图像分类模型
  • [C++]多态