870--优势洗牌
public static int[] advantageCount(int[] A, int[] B) {
int[] sortedA=A.clone();
Arrays.sort(sortedA);//升序排列
int[] sortedB=B.clone();
Arrays.sort(sortedB);
//Integer存放B的值,后面的队列存放大于B值的A中的值
HashMap<Integer, Deque<Integer>> map = new HashMap<>();
for (int i : sortedB) {
map.put(i,new LinkedList<>());
}
//摆烂队列,这里放的都是比拼失败的下等马
Deque<Integer> remaining=new LinkedList<>();
int j=0;
for (int i : sortedA) {
//如果当前位置的a大于当前位置的b,存放进map里,并且让j++
if(i>sortedB[j]){
map.get(sortedB[j++]).add(i);
//不然就存放进队列中
}else{
remaining.add(i);
}
}
int[] ans=new int[B.length];
for (int i = 0; i < B.length; i++) {
//正常从map里的deque拿,这里都是a的上等马
if(map.get(B[i]).size()>0)ans[i]=map.get(B[i]).pop();
//打不过就摆烂
else ans[i]=remaining.pop();
}
return ans;
}
704--二分查找
public int search(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while (left<=right){//搜索区间为空时才应该被制止,所以这里的条件是<=
int mid=left+(right-left)/2;//防止溢出
if(nums[mid]==target)return mid;
else if(nums[mid]<target)left=mid+1;//mid已经搜索过了,所以应该排除
else if(nums[mid]>target)right=mid-1;
}
return -1;
}
34--排序数组中查找元素的第一个位置和最后一个位置
public int[] searchRange(int[] nums, int target) {
int left=0;
int right=nums.length-1;
int ans=-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]==target){
ans=mid;
break;
}
else if(nums[mid]>target)right=mid-1;
else if(nums[mid]<target)left=mid+1;
}
if(ans==-1)return new int[]{-1,-1};
int ansLeft=ans,ansRight=ans;
//移动指针并且检验是否越界
while(ansLeft>=0&&nums[ansLeft]==target){
ansLeft--;
}
while(ansRight<=nums.length-1&&nums[ansRight]==target){
ansRight++;
}
return new int[]{ansLeft+1,ansRight-1};
}
857--爱吃香蕉的人(二分法的妙用)
/**
* 当吃香蕉的速度为x的时候,需要H个小时才能吃完香蕉,H=f(x),其中H关于x单调递减
* @param arr 代表香蕉的数组
* @param speed 吃的速度 根/小时
* @return 吃完香蕉的时间
*/
int f(int[] arr,int speed){
int time=0;
for (int i = 0; i < arr.length; i++) {
time+=arr[i]/speed;
int res=arr[i]%speed;
if(res>0){
time++;
}
}
return time;
}
/**
* @param piles 吃香蕉的数组
* @param h 警卫回来的时间
* @return 最小的速度
*/
public int getMinSpeed(int[] piles,int h){
if(piles==null)return 0;
int left=1;//最小的速度应该是1
int right=1000000000;//最大的速度题目中规定为10^9
while (left<=right){
int mid=left+(right-left)/2;
//我们想让返回的值尽量小,所以我们得让left尽量靠左,所以我们让相当的情况和移动右点到左点的情况一致
if(f(piles,mid)<=h)right=mid-1;
else left=mid+1;
}
return left;
}
1011-在D天内送达包裹的能力(二分法的妙用)
/**
* @param arr 数组
* @param hold 载重量
* @return 最低天数
* 载重量越高,天数越短,所以这也是一个单调递减的函数
*/
//方法一,效率比较低,判断条件过于冗杂
public int f(int[] arr,int hold){
int days=1;
int num=0;
for (int i = 0; i < arr.length; i++) {
num+=arr[i];
//等于的时候也需要开了,因为已经装不下了,此时如果还有货物就加一天,没有货物就不加了
if(num>hold){
days++;
num=arr[i];//大于的情况需要把这次的货加上去
}else if(num==hold&&i!=arr.length-1){
days++;
num=0;
}else if(num==hold&&i==arr.length-1){
days--;
}
}
return days;
}
//方法二,效率比一略高
public int f(int[] weights, int x) {
int days = 0;
for (int i = 0; i < weights.length; ) {
// 尽可能多装货物
int cap = x;
while (i < weights.length) {
if (cap < weights[i]) break;
else cap -= weights[i];
i++;
}
days++;
}
return days;
}
/**
* @param weights 物品重量
* @param days 天数
* @return 最小运载量,返回值尽量小
*/
public int shipWithinDays(int[] weights, int days) {
int left=1;//最低载重量至少为1
int right=50000;//题目条件
//最终返回的结果至少要比所有的weight值要大,而rignt值可以让他为所有值的和
for (int weight : weights) {
left=Math.max(left,weight);
right+=weight;
}
while(left<=right){
int mid=left+(right-left)/2;
if(days>=f(weights,mid)){right=mid-1;}
else{left=mid+1;}
}
return left;
}
410--分割数组的最大值(二分法的妙用)
//限定一个最大数组和,返回最少可以分割成几个数组,数字越大,可分割的次数越少,这是一个单调递减的函数
int f(int[] arr,int max){
int count=1;
int sum=0;
for (int i = 0; i < arr.length; i++) {
if(max<sum+arr[i]){
//不可以添加元素了
sum=arr[i];
count++;
}else{
sum+=arr[i];
}
}
return count;
}
//我们要使得分割数组的最大值最小
public int splitArray(int[] nums, int m) {
int left=getLeft(nums);
int right=getRight(nums);
while (left<=right){
int mid=left+(right-left)/2;
if(m>=f(nums,mid))right=mid-1;
else{left=mid+1;}
}
return left;
}
//获取数组中的最大值,因为它至少是最大值,才能保证数组被分割
public int getLeft(int[] arr){
int res=0;
for (int i : arr) {
res=Math.max(res,i);
}
return res;
}
public int getRight(int[] arr){
int res=0;
for (int i = 0; i < arr.length; i++) {
res+=arr[i];
}
return res;
}
380--常数时间插入,删除和获取随机元素
分析:HashSet,LinikedHashSet的时间复杂度都为O(1)但是只要底层是链表实现的,我们就没有办法在插入的时候实现O(1),所以我们一定要使用底层为数组实现的,并且数组需要是紧凑的,那数据插入,删除的时候怎么保证呢?可以直接在尾部插入,所以在删除的时候我们把它移动到尾部然后再pop掉,每个数对应的索引使用hashmap来存储
public class likou380 {
private HashMap<Integer,Integer> map;
private ArrayList<Integer> list;
Random random = new Random();
public likou380(){
map=new HashMap<>();
list=new ArrayList<>();
}
public boolean insert(int val) {
if(map.containsKey(val))return false;
//先放到map里面
map.put(val,list.size());
list.add(val);//尾插
return true;
}
public boolean remove(int val) {
if(!map.containsKey(val))return false;
int index=map.get(val);
int lastEleValue=list.get(list.size()-1);
map.put(lastEleValue,index);
list.set(index,lastEleValue);
list.remove(list.size()-1);
map.remove(val);
return true;
}
int getRandom() {
// 随机获取 nums 中的一个元素
return list.get(random.nextInt(list.size()));
}
}
710--黑名单中的随机数
白名单中数的个数为 N - len(B),那么可以直接在 [0, N - len(B)) 中随机生成整数。我们把所有小于 N - len(B) 且在黑名单中数一一映射到大于等于 N - len(B) 且出现在白名单中的数。这样一来,如果随机生成的整数出现在黑名单中,我们就返回它唯一对应的那个出现在白名单中的数即可。
例如当 N = 6,B = [0, 2, 3] 时,我们在 [0, 3) 中随机生成整数,并将 2 映射到 4,3 映射到 5,这样随机生成的整数就是 [0, 1, 4, 5] 中的一个。
public class likou710 {
Random r;
int wlen;
Map<Integer,Integer> m;
likou710(int n,int[] b){
m=new HashMap<>();
r=new Random();
wlen=n-b.length;
Set<Integer> w = new HashSet<>();
//创建[wlen,n)的集合(映射集合也就是比[0,wlen)大的数)
for (int i = wlen; i < n; i++) {
w.add(i);
}
//排除掉映射集合中黑名单的有元素
for (int x : b) {
w.remove(x);
}
//剩下的元素加入到集合中
Iterator<Integer> wi = w.iterator();
//对于小于wlen长度的元素我们需要映射到我们的白名单元素
for (int i : b) {
if(i<wlen)m.put(i,wi.next());
}
}
public int pick(){
int k=r.nextInt(wlen);
return m.getOrDefault(k,k);//如果有值那么说明它需要被映射,没有值说明不需要被映射,可以直接返回
}
public static void main(String[] args) {
likou710 likou710 = new likou710(10, new int[]{0, 1, 4,9});
for (int i = 0; i < 20; i++) {
System.out.println(likou710.pick());
}
}
}
402--移除掉k位有效数字
public String removeKdigits(String num, int k) {
Deque<Character> deque = new LinkedList<Character>();
int length = num.length();
for (int i = 0; i < length; ++i) {
char digit = num.charAt(i);
//只要队列不为空,并且k还没有减到0,并且栈的最近的元素大于当前元素进入下面的循环
while (!deque.isEmpty() && k > 0 && deque.peekLast() > digit) {
deque.pollLast();//栈中元素出来
k--;//k减少
}
//入队列
deque.offerLast(digit);
}
//继续弹出k个元素,此时队列里的数字已经是单调不降了,但是还没有达到k=0,所以我们要继续弹出元素
for (int i = 0; i < k; ++i) {
deque.pollLast();
}
StringBuilder ret = new StringBuilder();
boolean leadingZero = true;
while (!deque.isEmpty()) {
char digit = deque.pollFirst();//从头部取出元素
if (leadingZero && digit == '0') {//如果是0就跳过
continue;
}
leadingZero = false;
ret.append(digit);//组装字符串
}
//最后长度为0就返回0否则返回拼接的字符串
return ret.length() == 0 ? "0" : ret.toString();
}
26--删除有序数组中的重复项
public int removeDuplicates(int[] nums) {
if(nums==null||nums.length==0)return 0;
int p=0;
int q=1;
while(q<nums.length){
//如果两个指针的数值不相等
if(nums[p]!=nums[q]){
//如果q-p>1的时候才把nums[p+1]重新赋值为nums[q],q=p+1的时候是不需要赋值的
if(q-p>1){
nums[p+1]=nums[q];
}
//p指针后移
p++;
}
//两个指针相等q指针后移
q++;
}
return p+1;
}
public int removeDuplicates2(int[] nums) {
int slow=0;
int fast=0;
while (fast<nums.length){
if(nums[slow]==nums[fast]){
fast++;
continue;
}
slow++;
nums[slow]=nums[fast];
}
return slow+1;
}
83--和上题相似的一道类似的链表题
public ListNode deleteDuplicates(ListNode head) {
ListNode cur = head;
while(cur != null && cur.next != null) {
if(cur.val == cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
public ListNode deleteDuplicates2(ListNode head) {
if(head==null)return null;
ListNode slow = head;
ListNode fast = head;
while (fast != null) {
if (slow.val != fast.val) {
slow.next = fast;
slow = slow.next;
}
fast = fast.next;
}
//断开后面重复元素的连接
slow.next=null;
return head;
}
27--移除元素
public int removeElement(int[] nums, int val) {
int fast=0,slow=0;
while(fast<nums.length){
if(nums[fast]!=val){
//不相等的时候给慢指针赋值为快指针当前的值
nums[slow]=nums[fast];
//慢指针赋值完往前走一步,准备下一次赋值
slow++;
}
//相等的时候就让快指针往前走
fast++;
}
return slow;
}
283--移动零到数组的结尾
public void moveZeroes(int[] nums) {
int i = removeElement(nums, 0);
for (; i <nums.length ; i++) {
nums[i]=0;
}
}
public int removeElement(int[] nums, int val) {
int fast=0,slow=0;
while(fast<nums.length){
if(nums[fast]!=val){
//不相等的时候给慢指针赋值为快指针当前的值
nums[slow]=nums[fast];
//慢指针赋值完往前走一步,准备下一次赋值
slow++;
}
//相等的时候就让快指针往前走
fast++;
}
return slow;
}