目录
位图和比较器的简单应用
一、位图
1.位图的功能
记录信息
例如,用一个 int
类型的变量的每个二进制位来记录
0
−
31
0-31
0−31 的出现情况。
2.位图的好处
可以用更小的空间来实现 map 的功能。
3.位图的实现
import java.util.HashSet;
public class Code02_BitMap2 {
// 位图的实现
public static class BitMap {
private long[] bits;
public BitMap(int max) {
bits = new long[(max + 64) >> 6];
}
public void add(int num) {
// 注意要用 1L
bits[num >> 6] |= (1L << (num & 63));
}
public void delete(int num) {
bits[num >> 6] &= ~(1L << (num & 63));
}
public boolean contains(int num) {
return (bits[num >> 6] & (1L << (num & 63))) != 0;
}
}
//对数器
public static void main(String[] args) {
System.out.println("测试开始!");
int max = 10000;
BitMap bitMap = new BitMap(max);
HashSet<Integer> set = new HashSet<>();
int testTime = 10000000;
for (int i = 0; i < testTime; i++) {
int num = (int) (Math.random() * (max + 1));
double decide = Math.random();
if (decide < 0.333) {
bitMap.add(num);
set.add(num);
} else if (decide < 0.666) {
bitMap.delete(num);
set.remove(num);
} else {
if (bitMap.contains(num) != set.contains(num)) {
System.out.println("Oops!");
break;
}
}
}
for (int num = 0; num <= max; num++) {
if (bitMap.contains(num) != set.contains(num)) {
System.out.println("Oops!");
}
}
System.out.println("测试结束!");
}
}
二、用位运算实现加减乘除
用位运算实现+ - * /
1.加法
// 测试链接:https://leetcode.cn/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/
public class BitAddMinusMultiDiv {
public static int add(int a, int b) {
int sum = a;
while (b != 0) {
sum = a ^ b; // 不进位和
b = (a & b) << 1; // 进位和
a = sum;
}
return sum;
}
}
2.减法
// 测试链接: 无
public class BitAddMinusMultiDiv {
// a - b => a + (-b)
public static int minus(int a, int b) {
return add(a, negNum(b));
}
// n => -n
public static int negNum(int n) {
return add(~n, 1);
}
public static int add(int a, int b) {
int sum = a;
while (b != 0) {
sum = a ^ b;
b = (a & b) << 1;
a = sum;
}
return sum;
}
}
3.乘法
原理同十进制乘法
// 测试链接:https://leetcode.cn/problems/recursive-mulitply-lcci/
public class BitAddMinusMultiDiv {
// 递归方法 支持负数
public int multiply(int A, int B) {
if (B == 0) return 0;
if (B == 1) return A;
return multiply(A, (B & 1)) + multiply(A << 1, B >>> 1);
}
// 非递归方法 支持负数
// a * b => a 逐位乘 b 的二进制 ,并且错位相加
public static int multi(int a, int b) {
int res = 0;
while (b != 0) {
if ((b & 1) != 0) {
res = add(res, a); // 加错位后的结果
}
a <<= 1; // a 左移一位 即错位
b >>>= 1; // b 无符号右移一位,即消除最右边用过的一位数
}
return res;
}
public static int add(int a, int b) {
int sum = a;
while (b != 0) {
sum = a ^ b;
b = (a & b) << 1;
a = sum;
}
return sum;
}
}
4.除法
// 测试链接:https://leetcode.cn/problems/divide-two-integers/
public class BitAddMinusMultiDiv {
public static int add(int a, int b) {
int sum = a;
while (b != 0) {
sum = a ^ b;
b = (a & b) << 1;
a = sum;
}
return sum;
}
public static int negNum(int n) {
return add(~n, 1);
}
public static int minus(int a, int b) {
return add(a, negNum(b));
}
public static int multi(int a, int b) {
int res = 0;
while (b != 0) {
if ((b & 1) != 0) {
res = add(res, a);
}
a <<= 1;
b >>>= 1;
}
return res;
}
public static boolean isNeg(int n) {
return n < 0;
}
public static int div(int a, int b) {
int x = isNeg(a) ? negNum(a) : a;
int y = isNeg(b) ? negNum(b) : b;
int res = 0;
for (int i = 30; i >= 0; i = minus(i, 1)) {
if ((x >> i) >= y) {
res |= (1 << i);
x = minus(x, y << i);
}
}
return isNeg(a) ^ isNeg(b) ? negNum(res) : res;
}
public static int divide(int a, int b) {
if (a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) {
return 1;
} else if (b == Integer.MIN_VALUE) {
return 0;
} else if (a == Integer.MIN_VALUE) {
if (b == negNum(1)) {
return Integer.MAX_VALUE;
} else {
// a / b
// (a + 1) / b == c
// a - (b * c) = d
// d / b = e
// c + e
int c = div(add(a, 1), b);
return add(c, div(minus(a, multi(c, b)), b));
}
} else {
return div(a, b);
}
}
}
三、位运算完成大小比较
如何不用任何比较判断语句,就可以返回两个数中较大的那个
/**
* @author 杨思远
* @version 1.0
* @title 位运算完成大小比较
* @date 2022/7/16 0:52
* @description 本方法原创 不一定为最优解 a < b => -1 a == b => -1 a > b => 1
*/
public class BitComparisonOfSize {
// 只使用 !=
public static int compare(int a, int b) {
if (a >>> 31 != b >>> 31) return b >>> 31 - a >>> 31;
for (int i = 30; i > 0; i--) {
if (a >>> i != b >>> i) return a >>> i - b >>> i;
}
return 0;
}
// 不使用任何比较判断语句 ( > < = != >= <= )
public static int compare2(int a, int b) {
return a - b >>> 31;
}
public static void main(String[] args) {
System.out.println("测试开始");
for (int i = 0; i < 1000000; i++) {
int a = (int) (Math.random() * Integer.MIN_VALUE) * Math.random() < 0.5 ? 1 : -1;
int b = (int) (Math.random() * Integer.MIN_VALUE) * Math.random() < 0.5 ? 1 : -1;
int res = compare(a, b);
int res2 = compare2(a, b);
int acceptedAnswer = Integer.compare(a, b);
if (res != acceptedAnswer || res2 != acceptedAnswer) {
System.out.println("错误:a:" + a + ",b:" + b);
}
}
System.out.println("测试结束");
}
}
四、比较器
1.应用在系统排序方法中
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class ShowComparator {
public static class Student {
public String name;
public int id;
public int age;
public Student(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
}
// 谁id大,谁放前!
public static class IdComparator implements Comparator<Student> {
// 如果返回负数,认为第一个参数应该排在前面
// 如果返回正数,认为第二个参数应该排在前面
// 如果返回0,认为谁放前面无所谓
@Override
public int compare(Student o1, Student o2) {
if (o1.id < o2.id) {
return 1;
} else if (o2.id < o1.id) {
return -1;
} else {
return 0;
}
}
}
// 谁age大,谁放前!
public static class AgeComparator implements Comparator<Student> {
// 如果返回负数,认为第一个参数应该排在前面
// 如果返回正数,认为第二个参数应该排在前面
// 如果返回0,认为谁放前面无所谓
@Override
public int compare(Student o1, Student o2) {
if (o1.age < o2.age) {
return 1;
} else if (o2.age < o1.age) {
return -1;
} else {
return 0;
}
}
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void printStudents(Student[] students) {
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].name + ", " + students[i].id + ", " + students[i].age);
}
}
public static void main(String[] args) {
int[] arr = { 8, 1, 4, 1, 6, 8, 4, 1, 5, 8, 2, 3, 0 };
printArray(arr);
Arrays.sort(arr);
printArray(arr);
Student s1 = new Student("张三", 5, 27);
Student s2 = new Student("李四", 1, 17);
Student s3 = new Student("王五", 4, 29);
Student s4 = new Student("赵六", 3, 9);
Student s5 = new Student("左七", 2, 34);
Student[] students = { s1, s2, s3, s4, s5 };
printStudents(students);
System.out.println("=======");
Arrays.sort(students, new IdComparator());
printStudents(students);
System.out.println("=======");
ArrayList<Student> arrList = new ArrayList<>();
arrList.add(s1);
arrList.add(s2);
arrList.add(s3);
arrList.add(s4);
arrList.add(s5);
for (Student s : arrList) {
System.out.println(s.name + ", " + s.id + ", " + s.age);
}
System.out.println("=======");
arrList.sort(new AgeComparator());
for (Student s : arrList) {
System.out.println(s.name + ", " + s.id + ", " + s.age);
}
}
}
2.应用在与排序有关的结构中优先级队列(堆)、有序表
import java.util.Comparator;
import java.util.PriorityQueue;
public class ShowComparator2 {
public static class MyComparator implements Comparator<Integer> {
// 负,第一个参数在前
// 正,第二个参数在前
// 0, 谁放前都行
@Override
public int compare(Integer o1, Integer o2) {
if (o1 < o2) {
return 1;
} else if (o1 > o2) {
return -1;
} else {
return 0;
}
}
}
public static class Student {
public String name;
public int id;
public int age;
public Student(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
}
// 谁id大,谁放前!
public static class IdComparator implements Comparator<Student> {
// 如果返回负数,认为第一个参数应该排在前面
// 如果返回正数,认为第二个参数应该排在前面
// 如果返回0,认为谁放前面无所谓
@Override
public int compare(Student o1, Student o2) {
if (o1.id < o2.id) {
return 1;
} else if (o2.id < o1.id) {
return -1;
} else {
return 0;
}
}
}
public static void main(String[] args) {
String str1 = "abc";
String str2 = "b";
System.out.println(str1.compareTo(str2));
PriorityQueue<Student> heap = new PriorityQueue<>(new IdComparator());
Student s1 = new Student("张三", 5, 27);
Student s2 = new Student("李四", 1, 17);
Student s3 = new Student("王五", 4, 29);
Student s4 = new Student("赵六", 3, 9);
Student s5 = new Student("左七", 2, 34);
heap.add(s1);
heap.add(s2);
heap.add(s3);
heap.add(s4);
heap.add(s5);
System.out.println("=========");
while (!heap.isEmpty()) {
Student s = heap.poll();
System.out.println(s.name + ", " + s.id + ", " + s.age);
}
}
}
五、合并多个有序链表
Leetcode原题 https://leetcode.com/problems/merge-k-sorted-lists
import java.util.Comparator;
import java.util.PriorityQueue;
// 时间复杂度 O(M*log(N))
// 测试链接:https://leetcode.cn/problems/merge-k-sorted-lists/
public class MergeKSortedLists {
public static class ListNode {
public int val;
public ListNode next;
}
public static class ListNodeComparator implements Comparator<ListNode> {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
}
public static ListNode mergeKLists(ListNode[] lists) {
if (lists == null) {
return null;
}
PriorityQueue<ListNode> heap = new PriorityQueue<>(new ListNodeComparator());
for (int i = 0; i < lists.length; i++) {
if (lists[i] != null) {
heap.add(lists[i]);
}
}
if (heap.isEmpty()) {
return null;
}
ListNode head = heap.poll();
ListNode pre = head;
if (pre.next != null) {
heap.add(pre.next);
}
while (!heap.isEmpty()) {
ListNode cur = heap.poll();
pre.next = cur;
pre = cur;
if (cur.next != null) {
heap.add(cur.next);
}
}
return head;
}
}
六、写在后面
欢迎关注,会经常记录一些算法学习中遇到的问题。
欢迎随时留言讨论,与君共勉,知无不答!