我一般喜欢左闭右开,像这样,[lp,rp)
因此rp初始化要记得+1,因为取不到的。
板子:
func(args){
int lp,rp,mid,ans;
lp = initL;
rp = initR+1;
ans = initAns;
while(lp<rp){
mid = (rp-lp)/2 + lp; // rp-lp防溢出,可以移位卡常
if(check(mid,...)){
lp = mid+1;
}
else{
ans = mid;
rp = mid;
}
}
return ans;
}
check(mid,...){
// 检查逻辑
}
1. LC 275 H指数Ⅱ
这题没按板子写。但这种小细节无所谓的。二分h,检查引用次数比h大的文章数量,直至收敛。
class Solution {
public int hIndex(int[] citations) {
int n = citations.length;
int lp=0,rp=n;
while(lp<=rp){
int mid = (lp+rp)/2;
int count=0;
for(int i=n-1;i>=0;i--){
if(citations[i]>=mid){
count++;
if(count>=mid){
break;
}
}
}
if(count>=mid){
lp=mid+1;
}else{
rp=mid-1;
}
}
return rp;
}
}
2. LC 1283 使结果不超过阈值的最小除数
到这里板子还没写熟练,没防爆int。
二分除数,检查除完加和是否小于等于阈值,直至收敛
class Solution {
public int smallestDivisor(int[] nums, int threshold) {
int max = nums[0];
for (int num : nums) {
max = Math.max(num,max);
}
int lp = 1;
int rp = max+1;
int ans = -1;
while(lp<rp){
int mid = (lp+rp)/2;
int res = div(nums, mid);
if(res <= threshold){
ans = mid;
rp = mid;
}
else{
lp = mid+1;
}
}
return ans;
}
private int div(int[] nums,int dop){
int ans = 0;
for (int num : nums) {
ans += (int)Math.ceil((double)num/dop);
}
return ans;
}
}
3. LC 2187 完成旅途的最少时间
为了求二分上限,先对数组排序(其实无必要,很浪费时间)。很明显上限是最慢的那一趟乘以趟数,取不到记得+1。
二分时间,检查是否能完成阈值指定的旅途数量,直至收敛。
import java.util.Arrays;
class Solution {
public long minimumTime(int[] time, int totalTrips) {
long ans=-1;
long lp,rp,mid;
Arrays.sort(time);
lp = 0L;
rp = (long)totalTrips*time[0]+1;
while(lp<rp){
mid = lp+(rp-lp)/2;
if(coverTot(mid,time,totalTrips)){
ans = mid;
rp = mid;
}else{
lp = mid+1;
}
}
return ans;
}
private boolean coverTot(long ticks,int[] time,int threshHold){
for (int i : time) {
threshHold-= (int) (ticks/i);
if(threshHold<=0){
break;
}
}
return threshHold<=0;
}
}
4. LC 2226 每个小孩最多能分到多少糖果
二分最多能拿走的糖果数目。分堆其实也就是看每一堆最多能分成几堆二分结果的糖果,依次检查,直至收敛。
class Solution {
public int maximumCandies(int[] candies, long k) {
long sum = sumCandy(candies);
int rp = (int) (sum / k)+1;
int lp = 1;
int mid;
int ans = 0;
while(lp<rp){
mid = lp+(rp-lp)/2;
if(capable(mid,candies,k)){
ans = mid;
lp = mid+1;
}else{
rp = mid;
}
}
return ans;
}
private long sumCandy(int[] candies){
long ans = 0;
for (int candy : candies) {
ans+=candy;
}
return ans;
}
private boolean capable(int candy,int[] candies,long k){
for (int i : candies) {
k -= i/candy;
if(k<=0){
break;
}
}
return k<=0;
}
}
5. LC 1870 准时到达的列车最小时速
题面直接报出来上限了,不用求直接用就行。注意检查时最后抵达后不用向上取整。
class Solution {
public int minSpeedOnTime(int[] dist, double hour) {
int rp = (int) (1e7+1);
int lp = 1;
int mid;
int ans = -1;
while(lp<rp){
mid = lp+(rp-lp)/2;
if(onTime(mid,dist,hour)){
ans = mid;
rp = mid;
}else{
lp = mid+1;
}
}
return ans;
}
private boolean onTime(int velocity,int[] dist,double hour){
double cost = 0.00;
for (int i = 0; i < dist.length; i++) {
if(i==dist.length-1){
cost+=(double)dist[i]/velocity;
}
else{
cost+=Math.ceil((double)dist[i]/velocity);
}
}
return cost<=hour;
}
}
6. LC 1011 在D天内送达包裹的能力
二分货船载货能力,这题里面days≥1,所以上限可以取一天内送完,也就是weights的和。
import java.util.Arrays;
class Solution {
public int shipWithinDays(int[] weights, int days) {
int lp = Arrays.stream(weights).max().getAsInt();
int rp = slideMaxCapability(weights)+1;
int mid;
int ans = lp;
while(lp<rp){
mid = lp + (rp-lp)/2;
if(check(weights,mid,days)){
ans = mid;
rp = mid;
}else{
lp = mid+1;
}
}
return ans;
}
private int slideMaxCapability(int[] weights){
int ans = 0;
for (int weight : weights) {
ans+=weight;
}
return ans;
}
private boolean check(int[] weights,int capability,int threshold){
int cost = 0;
int rest = capability;
for (int weight : weights) {
if(rest>=weight){
rest-=weight;
}else{
cost+=1;
rest = capability-weight;
}
}
return cost+1<=threshold;
}
}
7. LC 875 爱吃香蕉的珂珂
二分速度,这题每堆香蕉最多1e9,最多1e4堆香蕉。也就是如果我用1e9的速度吃,每一堆最多1个h,所以总时间不超过1e4,也就≤h,因此上限取1e9。
import java.util.Arrays;
class Solution {
public int minEatingSpeed(int[] piles, int h) {
int rp = (int)1e9+1;
int lp = 1;
int mid;
int ans = 1;
while(lp<rp){
mid = lp + (rp-lp)/2;
if(check(mid,piles,h)){
ans = mid;
rp = mid;
}else{
lp = mid+1;
}
}
return ans;
}
private boolean check(int velocity,int[] piles,int h){
for (int pile : piles) {
h -= (int) Math.ceil((double)pile/velocity);
if(h<0){
break;
}
}
return h>=0;
}
}
8. LC 300 最长递增子序列
这题两种做法,O(n²)的序列DP,和O(nlgn)的贪心二分。
维护数组d,令d[i]表示所有长度为i的严格递增子序列的末尾元素的最小值。核心思想就是让该序列上升的尽可能缓慢,这样后续元素才更有可能扩展该序列的长度。
例如,
[0,2,4] 是一个长度为3的严格递增子序列
[0,2,3] 同样
则d[3]=Math.min(4,3)=3
遍历nums,若当前num<d中的最大值,说明可以更新某个长度为j的末尾元素的最小值。
又因为d一定严格单增,可使用二分。
class Solution {
public int lengthOfLIS(int[] nums) {
// O(nlgn)
int n = nums.length;
if(n==0){
return 0;
}
int[] d = new int[n+1];
int len = 1;
d[len] = nums[0];
for(int i=1;i<n;i++){
if(nums[i]>d[len]){
d[++len] = nums[i];
}else{
// 二分更新
int l=1;int r = len+1;int pos = 0;
while(l<r){
int mid = l + (r-l)/2;
if(d[mid]<nums[i]){
l = mid+1;
}else{
pos = mid;
r = mid;
}
}
d[pos] = nums[i];
}
}
return len;
}
}
9. LC 1954 收集足够苹果的最小花园周长
考虑半边长(边长的一半),设为k。则共有2(k+1)(2k²+k)个苹果,对k进行二分即可。 k的上限可以写个程序判断,也即上式>=1e15的值。算出来是62996。
class Solution {
public long minimumPerimeter(long neededApples) {
long lp,rp,ans,mid;
lp = 1;
rp = 62997+1;
ans = -1;
while(lp<rp){
mid = (rp-lp)/2 + lp;
if(check(mid,neededApples)){
ans = mid;
rp = mid;
}else{
lp = mid+1;
}
}
return 8*ans;
}
private boolean check(long mid,long need){
return 2*(mid+1)*(2*mid*mid+mid)>=need;
}
}
10. LC 1898 可移除字符的最大数目
这题灵神标的分数是1913。已经是K级别的题目了,但如果看出来二分板子的话其实没有任何思维量。这题的难点是能不能看出来单调性质,周赛场上基本会被降智20%,临场是否能看出来单调性是胜负手。
题面已知p一定是s的子序列,假设我们根据removable中前k个删除了s中的k个字符而导致p不再是s的子序列,那么删除>k个字符p也一定不是s的子序列。这个可以轻易反证。
同时,如果删除了前k个removable中标记出来的字符,p仍然是s的子序列,那么这样删除<k个字符p也一定是s的子序列,同样可以轻易反证。
根据这两个性质可知,删的越多,p就越不可能是s的子序列。既然有此单调性质,二分k即可。
检查时我也没啥优雅的检查方法,看别人交的也都那个思路:列一个bool数组,根据二分结果标记出所有的删除字符,然后线搜s,跳过bool数组标记的同时检查p的字符是否仍都在s中(线搜自带顺序了)。
import java.util.Arrays;
class Solution {
public int maximumRemovals(String s, String p, int[] removable) {
int lp,rp,mid,ans;
ans = -1;
lp = 0;rp = removable.length+1;
char[] sch = s.toCharArray();
char[] pch = p.toCharArray();
while(lp<rp){
mid = ((rp-lp)>>>1)+lp;
if(check(sch,pch,removable,mid)){
ans = mid;
lp = mid+1;
}else{
rp = mid;
}
}
return ans;
}
private boolean check(char[] sch,char[] pch,int[] removable,int mid){
boolean[] exist = new boolean[sch.length];
Arrays.fill(exist,true);
for (int i = 0; i < mid; i++) {
exist[removable[i]] = false;
}
int pp = 0;
for (int i = 0; i < sch.length; i++) {
if(exist[i]){
if(sch[i]==pch[pp]){
pp++;
if(pp==pch.length){
return true;
}
}
}
}
return false;
}
}
11. LC 1482 制作m束花所需的最少天数
灵神给这题标的1946分,妥妥的K。做了之后我觉得我也行了(笑。但扪心自问一下我临场应该看不出是二分的,这题不标二分我真一点不会往二分上想。
单调性:对于任意的整数p
- 假设等待了p天之后,能够制作m束花,那么等待任意>p天之后,也一定能制作m束花
- 假设等待了p天之后,还是不能制作m束花,那么等待任意<p天之后,也一定不能制作m束花。
下限:等1天(不可能一天都不等的,最早开花要过1天)
上限:题目标了,bloomDay的范围在1e9之内,所以无脑取1e9即可。
检查:线搜统计连续的长度≥k的块,个数应≥m。
import java.util.Arrays;
class Solution {
public int minDays(int[] bloomDay, int m, int k) {
int n = bloomDay.length;
if(m*k>n){
return -1;
}
int lp,rp,mid,ans;
ans = -1;
lp = 1;
rp = (int) (1e9+1);
while(lp<rp){
mid = ((rp-lp)>>>1)+lp;
if(check(bloomDay,m,k,mid)){
ans = mid;
rp = mid;
}else{
lp = mid+1;
}
}
return ans;
}
private boolean check(int[] bloomDay,int m,int k,int mid){
int cm = 0;
int con = 0;
for (int j : bloomDay) {
if (j <= mid) {
con++;
if (con == k) {
con = 0;
cm++;
}
} else {
con = 0;
}
}
return cm>=m;
}
}
12. LC 1276 不浪费原料的汉堡制作方案
这题不在灵神题单里,刷每日一题的时候看到的。
特判:由于巨无霸和小皇堡的番茄片数目都为偶数,偶数加偶数仍为偶数,所以如果给出的番茄片数量不为偶数,就可以直接返回空列表了。
单调性:对于任意数量的小皇堡B(其中B显然小于等于给出的芝士片数量)
- 若制作B个小皇堡会导致番茄片不够用,则显然制作<B个也会导致番茄片不够用,这是因为如果少做小皇堡,那么就会多做巨无霸,巨无霸对番茄片的消耗更大。
- 若制作B个小皇堡导致番茄片有剩余,则制作>B个也会导致番茄片剩余。这是因为如果多做小皇堡,那么就会少做巨无霸,对番茄片的消耗更不够。
题目要求将两种材料恰好用完,我选择直接对小皇堡数量二分,检查二分结果是否能使得材料用完。
下限:不做小皇堡,0。
上限:最多做Math.min(tomatoSlices/2,cheeseSlices)个小皇堡
检查:番茄片剩余数量
题外话:其实这是道鸡兔同笼的题目,但我最近刷二分题单刷魔怔了,就用二分了。
import java.util.ArrayList;
import java.util.List;
class Solution {
public List<Integer> numOfBurgers(int tomatoSlices, int cheeseSlices) {
ArrayList<Integer> ans = new ArrayList<>();
if(tomatoSlices%2!=0){
return ans;
}
int lp,rp,mid,s,check;
s = -1;
lp = 0;
rp = Math.min(tomatoSlices/2,cheeseSlices)+1;
while(lp<rp){
mid = ((rp-lp)>>>1)+lp;
check = check(tomatoSlices, cheeseSlices, mid);
if(check==0){
s= mid;
break;
}else if(check>0){
rp = mid;
}else{
lp = mid+1;
}
}
if(s!=-1){
ans.add(cheeseSlices-s);
ans.add(s);
}
return ans;
}
private int check(int tomato,int cheese,int mid){
return (tomato-mid*2-(cheese-mid)*4);
}
}
13. LC 1642 可以到达的最远建筑
这题灵神标的1962,接近2000分的题了。这道题确实可以用二分做,但并不是一个较好的解法,较好的解法是贪心。但这里是二分题解,就不放贪心了。
单调性:对于任意下标dest
- 若使用给出的bricks和ladders可以到达dest,则必然也能到达<dest的位置(大不了梯子砖块用不完)。
- 若使用给出的bricks和ladders不可以到达dest,则必然也到不了>dest的位置。
下限:0(不一定能到下标1的)
上限:n-1
检查:检查用的贪心。正是因为可以用贪心检查,所以就没必要二分。一切搜索算法都是把求解型问题转换为了判定型问题,但既然能用贪心直接求解,为什么还要搜索判定?所以这是这道题不适合二分的点。
贪心检查的思路是:对于小的高度差用砖块,对于大的高度差用梯子。这是一个很直观的结论,用反证法也能轻易证明。这里出于直观就不写严谨的数学证明了。
假设存在一个策略,其中存在至少一次在使用梯子和使用砖块时,前者对应的高度差小于后者。形如(b,x)和(l,y),其中x>y。那么我们交换在这两次中使用梯子和使用砖块的时机,也即(b,y)和(l,x),既然y个砖块能满足后者的高度差,那么对于x>y显然能满足。而梯子本身是对任意高度差都适用的,因此对于前者,交换后仍满足。
因此对于小的高度差用砖块,对于大的高度差用梯子,一定不比反过来差。但反之不亦然。则贪心思路正确。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
class Solution {
public int furthestBuilding(int[] heights, int bricks, int ladders) {
int lp,rp,mid,ans;
ans = 0;
lp = 0;
int n = heights.length;
rp = n;
int[] diff = new int[n];
diff[0] = 0;
for (int i = 1; i < heights.length; i++) {
diff[i] = Math.max(heights[i] - heights[i-1],0);
}
while(lp<rp){
mid = ((rp-lp)>>>1)+lp;
if(check(diff,mid,bricks,ladders)){
ans = mid;
lp = mid+1;
}else{
rp = mid;
}
}
return ans;
}
private boolean check(int[] diff,int mid,int b,int l){
PriorityQueue<Integer> pq = new PriorityQueue<>(Integer::compare);
for(int i=1;i<=mid;i++){
if(diff[i]!=0){
pq.offer(diff[i]);
}
}
while(!pq.isEmpty()){
Integer poll = pq.poll();
if(poll<=b){
b-=poll;
}else{
l--;
}
}
return l>=0;
}
}
这里小根堆插入时间复杂度nlgn,外面套一层O(lgn)的二分,总体复杂度O(n(lgn)²),复杂度太高了,表现很差。
14. 周赛378 T3 LC 100184 找出出现至少三次的最长特殊子字符串Ⅱ
不知道这题是不是二分才是最优解。我没想出来O(n)的写法。最后过了一个O(nlgn)的。
PS:这把真的,前三题难度都很低,结果我一看榜第四题基本没人做出来,就写个T3题解润了。榜上雪景式、别问问就是数学、张晴川、erutan这帮大佬在11:20左右也都没做出来。灵神一如既往的强,34minAK了。
求最值可转为判定。显然这个特殊子字符串长度越长越不可能出现3次。单调性如下:
对于任意窗口长度m
- 若m能够对应至少一个符合条件的特殊子字符串,那么<m也能(m对应的那个/些子字符串就是)
- 若m不能对应任何满足条件的子字符串,那么>m也不能(显然)
上限:n-mid+1
下限:1(没有长度为0的字符串这一说)
检查:
O(n)的检查。遍历字符串,对于某一位置,建立flag,后续所有字符应和他一致,直到遍历到不一致的停止。给该字符串带来Math.max(end-start+1-mid+1,0)的增量。判断是否≥3即可。字符串由于字符都一致,可以开个26size的哈希表维护。
class Solution {
public int maximumLength(String s) {
int n = s.length();
int lp,rp,mid,ans;
ans = -1;
lp = 1;
rp = n-3+1+1;
while(lp<rp){
mid = ((rp-lp)>>>1)+lp;
if(check(s,mid,n)){
ans = mid;
lp = mid+1;
}else{
rp = mid;
}
}
return ans;
}
private boolean check(String s,int mid,int n){
int[] cnt = new int[26]; // 直接把字符串当字符就行
char[] ch = s.toCharArray();
for(int i=0;i<n;i++){
char flag = ch[i];
int tmp = i;
while(i+1<n && ch[i+1]==flag){
i++;
}
cnt[flag-'a']+=Math.max(0,i-tmp+1-mid+1);
if(cnt[flag-'a']>=3){
return true;
}
}
return false;
}
}
15. LC 33 搜索旋转排序数组
这题是真的汗流浃背乐。应该是一道基础题,但我就是想了很久(基本功不扎实)。
二分一定要在保证单调性的区间使用。而旋转后的数组本身并不是有序的,但可以视为两段数组拼在一起,其中每一个都是有序的。因此这里的思路是“两次二分”,或者叫做分治二分。
例如,现在有个数组:
[ 9,10,11,0,1,2,3,4,5,6,7,8 ]
我们并不知道上述两个有序数组的分界点的索引(这里就是11对应的索引,2),那么只能选择粗暴的从中间切开:
[ 9,10,11,0,1,2 ]
[ 3,4,5,6,7,8 ]
可以证明这样切割一定是有至少一个数组有序。既然该数组有序,我们就可以对其二分查找。具体到这个例子,假设target = 10吧。
- [3,8]区间里没有10,不用看了,直接找左半边
- 左半边并不是单调的,因此先分割
[9,10,11]
[0,1,2]
- [9,11]里包含了10,去左半边找
- 由于左半边单调,一直二分下去,就能找到10
这里每次都要二分两次,先对检索区间二分,然后找到target可能在的区间,如果它有序,就可以一直二分下去了,如果无序,那么就需要再二分,直到找到有序区间为止。而有序无序既然都要二分,在实现时合在一起即可。
class Solution {
public int search(int[] nums, int target) {
int lp,rp,mid,ans;
ans = -1;
lp = 0;
rp = nums.length;
while(lp<rp){
mid = ((rp-lp)>>>1)+lp;
if(nums[mid]==target){
return mid;
}
// 整个数组分为两段
// [lp,mid] (mid,rp)
// 至少有一段是单增的
if(nums[lp]<nums[mid]){
// 左半段单调
if(nums[lp]<=target && target<nums[mid]){
// 在左半段,左半段二分
rp = mid;
}else{
// 不在左半段,那只能找右半段了
lp = mid+1;
}
}else{
// 右半段单调
if(nums[mid]<target && target<=nums[rp-1]){
// 在右半段,右半段二分
lp = mid+1;
}else{
rp = mid;
}
}
}
return ans;
}
}
16. LC 100200 标记所有下标的最早秒数Ⅰ
周赛386T3。
-
单调性:
- 若m秒可以将所有元素标记,显然>m秒也可以。
- 若m秒无法将所有元素标记,显然<m秒也不可以。
-
下界:1
-
上界:changeIndices.length
-
检查:这个比较有意思,考细节的。我们可以这么想,倒着遍历changeIndices,看每个位置的最晚结束时间是多少(也就是最后一次能标记该位置的时间点),只要能在这个最晚结束时间点之前标记上就可以。所以我先统计所有位置的最晚结束时间。然后按照该时间排序。ddl早的先进行减1和标记,这是因为总归要在ddl之前把该位置上的元素递减到0。另外记得标记一个位置也需要1个单位的时间。
我采用的方式是每次新的ddl出现时,相当于给整个任务新增了一段可用的时间,所以就在rest中记录新增的时间。然后把元素减为0并标记需要nums[i]+1个时间。检查有没有超时就可以了。
import java.util.Arrays; import java.util.Comparator; class Solution { public int earliestSecondToMarkIndices(int[] nums, int[] changeIndices) { int l,r,mid,ans; ans = -1; l = 1; r = changeIndices.length+1; while(l<r){ mid = ((r-l)>>>1)+l; if(check(nums,changeIndices,mid)){ ans = mid; r = mid; }else{ l = mid+1; } } return ans; } private boolean check(int[] nums,int[] changeIndices,int mid){ int n = nums.length; int[][] ddl = new int[n][2]; for (int i = 0; i < n; i++) { ddl[i][0] = nums[i]; ddl[i][1] = -1; } for(int i=mid;i>=1;i--){ if(ddl[changeIndices[i-1]-1][1]==-1){ ddl[changeIndices[i-1]-1][1] = i; } } Arrays.sort(ddl, Comparator.comparingInt(o -> o[1])); int rest = 0; int prev = 0; for (int[] arr : ddl) { if(arr[0]>arr[1]){ return false; } rest += arr[1]-prev; prev = arr[1]; rest -= arr[0]+1; if(rest<0){ return false; } } return true; } }
检查的瓶颈在于排序,是nlgn的,二分lgm的,所以总体O(nlgmlgn)的
17. LC 100246 将元素分配到两个数组中Ⅱ
第一次AK。周赛387T4。
这题可以这么想:维护arr1和arr2的两个有序列表。每次只需要二分查找第一个比nums[i]大的位置,然后根据列表长度计算有多少个比nums[i]大的元素即可。
复杂度方面,二分是O(logn)的,有序列表插入也是二分,也是O(logn)的,所以总体O(nlogn),对于1e5的数据,是可以过的。
但是java tmd没有有序列表,所以还得自己手写一个。
import java.util.ArrayList;
class SortedList{
public ArrayList<Integer> list;
public SortedList(){
list = new ArrayList<>();
}
public void insert(int element) {
int index = findInsertionIndex(element);
list.add(index, element);
}
private int findInsertionIndex(int element) {
int left = 0;
int right = list.size() - 1;
int index = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (list.get(mid).compareTo(element) < 0) {
index = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return index + 1;
}
public int findFirstIndexGreaterThan(int target) {
int left = 0;
int right = list.size() - 1;
int firstIndex = list.size(); // 初始化索引为列表长度,表示没有找到大于target的元素
while (left <= right) {
int mid = left + (right - left) / 2;
if (list.get(mid).compareTo(target) > 0) {
// 如果mid位置的元素大于target,则更新firstIndex并继续在左半部分搜索
firstIndex = mid;
right = mid - 1;
} else {
// 如果mid位置的元素小于或等于target,则在右半部分搜索
left = mid + 1;
}
}
return firstIndex;
}
}
class Solution {
public int[] resultArray(int[] nums) {
SortedList sarr1 = new SortedList();
SortedList sarr2 = new SortedList();
ArrayList<Integer> arr1 = new ArrayList<>();
ArrayList<Integer> arr2 = new ArrayList<>();
arr1.add(nums[0]);
sarr1.insert(nums[0]);
arr2.add(nums[1]);
sarr2.insert(nums[1]);
for (int i = 2; i < nums.length; i++) {
int s1 = sarr1.list.size()-sarr1.findFirstIndexGreaterThan(nums[i]);
int s2 = sarr2.list.size()-sarr2.findFirstIndexGreaterThan(nums[i]);
if(s1>s2){
arr1.add(nums[i]);
sarr1.insert(nums[i]);
}else if(s1<s2){
arr2.add(nums[i]);
sarr2.insert(nums[i]);
}else{
if(arr1.size()<=arr2.size()){
arr1.add(nums[i]);
sarr1.insert(nums[i]);
}else {
arr2.add(nums[i]);
sarr2.insert(nums[i]);
}
}
}
int[] ans = new int[nums.length];
for (int i = 0; i < arr1.size(); i++) {
ans[i] = arr1.get(i);
}
for (int i = 0; i < arr2.size(); i++) {
ans[i+arr1.size()] = arr2.get(i);
}
return ans;
}
}