packageclass019;// 展示填函数风格的测试方式// 子矩阵的最大累加和问题,不要求会解题思路,后面的课会讲// 测试链接 : https://www.nowcoder.com/practice/840eee05dccd4ffd8f9433ce8085946bpublicclassCode01_FillFunction{publicintsumOfSubMatrix(int[][] mat,int n){returnmaxSumSubmatrix(mat, n, n);}// 求子矩阵的最大累加和,后面的课会讲publicstaticintmaxSumSubmatrix(int[][] mat,int n,int m){int max =Integer.MIN_VALUE;for(int i =0; i < n; i++){// 需要的辅助数组,临时动态生成就可以int[] arr =newint[m];for(int j = i; j < n; j++){for(int k =0; k < m; k++){
arr[k]+= mat[j][k];}
max =Math.max(max,maxSumSubarray(arr, m));}}return max;}// 求子数组的最大累加和,后面的课会讲publicstaticintmaxSumSubarray(int[] arr,int m){int max =Integer.MIN_VALUE;int cur =0;for(int i =0; i < m; i++){
cur += arr[i];
max =Math.max(max, cur);
cur = cur <0?0: cur;}return max;}}
packageclass019;// 展示acm风格的测试方式// 子矩阵的最大累加和问题,不要求会解题思路,后面的课会讲// 每一组测试都给定数据规模// 需要任何空间都动态生成,在大厂笔试或者比赛中,这种方式并不推荐// 测试链接 : https://www.nowcoder.com/practice/cb82a97dcd0d48a7b1f4ee917e2c0409?// 请同学们务必参考如下代码中关于输入、输出的处理// 这是输入输出处理效率很高的写法// 提交以下的code,提交时请把类名改成"Main",可以直接通过importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.OutputStreamWriter;importjava.io.PrintWriter;importjava.io.StreamTokenizer;publicclassCode02_SpecifyAmount{publicstaticvoidmain(String[] args)throwsIOException{// 把文件里的内容,load进来,保存在内存里,很高效,很经济,托管的很好BufferedReader br =newBufferedReader(newInputStreamReader(System.in));// 一个一个读数字StreamTokenizer in =newStreamTokenizer(br);// 提交答案的时候用的,也是一个内存托管区PrintWriter out =newPrintWriter(newOutputStreamWriter(System.out));while(in.nextToken()!=StreamTokenizer.TT_EOF){// 文件没有结束就继续// n,二维数组的行int n =(int) in.nval;
in.nextToken();// m,二维数组的列int m =(int) in.nval;// 装数字的矩阵,临时动态生成int[][] mat =newint[n][m];for(int i =0; i < n; i++){for(int j =0; j < m; j++){
in.nextToken();
mat[i][j]=(int) in.nval;}}
out.println(maxSumSubmatrix(mat, n, m));}
out.flush();
br.close();
out.close();}// 求子矩阵的最大累加和,后面的课会讲publicstaticintmaxSumSubmatrix(int[][] mat,int n,int m){int max =Integer.MIN_VALUE;for(int i =0; i < n; i++){// 需要的辅助数组,临时动态生成int[] arr =newint[m];for(int j = i; j < n; j++){for(int k =0; k < m; k++){
arr[k]+= mat[j][k];}
max =Math.max(max,maxSumSubarray(arr, m));}}return max;}// 求子数组的最大累加和,后面的课会讲publicstaticintmaxSumSubarray(int[] arr,int m){int max =Integer.MIN_VALUE;int cur =0;for(int i =0; i < m; i++){
cur += arr[i];
max =Math.max(max, cur);
cur = cur <0?0: cur;}return max;}}
packageclass019;// 展示acm风格的测试方式// 子矩阵的最大累加和问题,不要求会解题思路,后面的课会讲// 每一组测试都给定数据规模// 任何空间都提前生成好,一律都是静态空间,然后自己去复用,推荐这种方式// 测试链接 : https://www.nowcoder.com/practice/cb82a97dcd0d48a7b1f4ee917e2c0409?// 请同学们务必参考如下代码中关于输入、输出的处理// 这是输入输出处理效率很高的写法// 提交以下的code,提交时请把类名改成"Main",可以直接通过importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.OutputStreamWriter;importjava.io.PrintWriter;importjava.io.StreamTokenizer;importjava.util.Arrays;publicclassCode03_StaticSpace{// 题目给定的行的最大数据量publicstaticintMAXN=201;// 题目给定的列的最大数据量publicstaticintMAXM=201;// 申请这么大的矩阵空间,一定够用了// 静态的空间,不停复用publicstaticint[][] mat =newint[MAXN][MAXM];// 需要的所有辅助空间也提前生成// 静态的空间,不停复用publicstaticint[] arr =newint[MAXM];// 当前测试数据行的数量是n// 当前测试数据列的数量是m// 这两个变量可以把代码运行的边界规定下来publicstaticint n, m;publicstaticvoidmain(String[] args)throwsIOException{BufferedReader br =newBufferedReader(newInputStreamReader(System.in));StreamTokenizer in =newStreamTokenizer(br);PrintWriter out =newPrintWriter(newOutputStreamWriter(System.out));while(in.nextToken()!=StreamTokenizer.TT_EOF){
n =(int) in.nval;
in.nextToken();
m =(int) in.nval;for(int i =0; i < n; i++){for(int j =0; j < m; j++){
in.nextToken();
mat[i][j]=(int) in.nval;}}
out.println(maxSumSubmatrix());}
out.flush();
br.close();
out.close();}// 求子矩阵的最大累加和,后面的课会讲publicstaticintmaxSumSubmatrix(){int max =Integer.MIN_VALUE;for(int i =0; i < n; i++){// 因为之前的过程可能用过辅助数组// 为了让之前结果不干扰到这次运行,需要自己清空辅助数组需要用到的部分Arrays.fill(arr,0, m,0);for(int j = i; j < n; j++){for(int k =0; k < m; k++){
arr[k]+= mat[j][k];}
max =Math.max(max,maxSumSubarray());}}return max;}// 求子数组的最大累加和,后面的课会讲publicstaticintmaxSumSubarray(){int max =Integer.MIN_VALUE;int cur =0;for(int i =0; i < m; i++){
cur += arr[i];
max =Math.max(max, cur);
cur = cur <0?0: cur;}return max;}}
packageclass019;// 展示acm风格的测试方式// 测试链接 : https://www.nowcoder.com/exam/test/70070648/detail?pid=27976983// 其中,7.A+B(7),就是一个没有给定数据规模,只能按行读数据的例子// 此时需要自己切分出数据来计算// 请同学们务必参考如下代码中关于输入、输出的处理// 这是输入输出处理效率很高的写法// 提交以下的code,提交时请把类名改成"Main",可以直接通过importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.OutputStreamWriter;importjava.io.PrintWriter;publicclassCode04_ReadByLine{publicstaticString line;publicstaticString[] parts;publicstaticint sum;publicstaticvoidmain(String[] args)throwsIOException{BufferedReader in =newBufferedReader(newInputStreamReader(System.in));PrintWriter out =newPrintWriter(newOutputStreamWriter(System.out));while((line = in.readLine())!=null){
parts = line.split(" ");
sum =0;for(String num : parts){
sum +=Integer.valueOf(num);}
out.println(sum);}
out.flush();
in.close();
out.close();}}
packageclass027;importjava.util.ArrayList;importjava.util.PriorityQueue;// 合并K个有序链表// 测试链接:https://www.nowcoder.com/practice/65cfde9e5b9b4cf2b6bafa5f3ef33fa6publicclassCode01_MergeKSortedLists{// 不要提交这个类publicstaticclassListNode{publicint val;publicListNode next;}// 提交以下的方法publicstaticListNodemergeKLists(ArrayList<ListNode> arr){// 小根堆PriorityQueue<ListNode> heap =newPriorityQueue<>((a, b)-> a.val - b.val);for(ListNode h : arr){// 遍历所有的头!if(h !=null){
heap.add(h);}}if(heap.isEmpty()){returnnull;}// 先弹出一个节点,做总头部ListNode h = heap.poll();ListNode pre = h;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 h;}}
packageclass027;// 最多线段重合问题// 测试链接 : https://www.nowcoder.com/practice/1ae8d0b6bb4e4bcdbf64ec491f63fc37// 测试链接 : https://leetcode.cn/problems/meeting-rooms-ii/// 请同学们务必参考如下代码中关于输入、输出的处理// 这是输入输出处理效率很高的写法// 提交以下的code,提交时请把类名改成"Main",可以直接通过importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.OutputStreamWriter;importjava.io.PrintWriter;importjava.io.StreamTokenizer;importjava.util.Arrays;importjava.util.PriorityQueue;publicclassCode02_MaxCover{publicstaticintMAXN=10001;publicstaticint[][] line =newint[MAXN][2];publicstaticint n;publicstaticvoidmain(String[] args)throwsIOException{BufferedReader br =newBufferedReader(newInputStreamReader(System.in));StreamTokenizer in =newStreamTokenizer(br);PrintWriter out =newPrintWriter(newOutputStreamWriter(System.out));while(in.nextToken()!=StreamTokenizer.TT_EOF){
n =(int) in.nval;for(int i =0; i < n; i++){
in.nextToken();
line[i][0]=(int) in.nval;
in.nextToken();
line[i][1]=(int) in.nval;}
out.println(compute());}
out.flush();
out.close();
br.close();}publicstaticintcompute(){// 堆的清空
size =0;// 线段一共有n条,line[0...n-1][2] : line[i][0] line[i][1], 左闭右闭// 所有线段,根据开始位置排序,结束位置无所谓// 比较器的用法// line [0...n) 排序 : 所有小数组,开始位置谁小谁在前Arrays.sort(line,0, n,(a, b)-> a[0]- b[0]);int ans =0;for(int i =0; i < n; i++){// i : line[i][0] line[i][1]while(size >0&& heap[0]<= line[i][0]){pop();}add(line[i][1]);
ans =Math.max(ans, size);}return ans;}// 小根堆,堆顶0位置publicstaticint[] heap =newint[MAXN];// 堆的大小publicstaticint size;publicstaticvoidadd(int x){
heap[size]= x;int i = size++;while(heap[i]< heap[(i -1)/2]){swap(i,(i -1)/2);
i =(i -1)/2;}}publicstaticvoidpop(){swap(0,--size);int i =0, l =1;while(l < size){int best = l +1< size && heap[l +1]< heap[l]? l +1: l;
best = heap[best]< heap[i]? best : i;if(best == i){break;}swap(i, best);
i = best;
l = i *2+1;}}publicstaticvoidswap(int i,int j){int tmp = heap[i];
heap[i]= heap[j];
heap[j]= tmp;}// 也找到了leetcode测试链接// 测试链接 : https://leetcode.cn/problems/meeting-rooms-ii/// 提交如下代码可以直接通过publicstaticintminMeetingRooms(int[][] meeting){int n = meeting.length;Arrays.sort(meeting,(a, b)-> a[0]- b[0]);PriorityQueue<Integer> heap =newPriorityQueue<>();int ans =0;for(int i =0; i < n; i++){while(!heap.isEmpty()&& heap.peek()<= meeting[i][0]){
heap.poll();}
heap.add(meeting[i][1]);
ans =Math.max(ans, heap.size());}return ans;}}
packageclass027;importjava.util.PriorityQueue;// 将数组和减半的最少操作次数// 测试链接 : https://leetcode.cn/problems/minimum-operations-to-halve-array-sum/publicclassCode03_MinimumOperationsToHalveArraySum{// 提交时把halveArray1改名为halveArraypublicstaticinthalveArray1(int[] nums){// 大根堆PriorityQueue<Double> heap =newPriorityQueue<>((a, b)-> b.compareTo(a));double sum =0;for(int num : nums){
heap.add((double) num);
sum += num;}// sum,整体累加和,-> 要减少的目标!
sum /=2;int ans =0;for(double minus =0, cur; minus < sum; ans++, minus += cur){
cur = heap.poll()/2;
heap.add(cur);}return ans;}publicstaticintMAXN=100001;publicstaticlong[] heap =newlong[MAXN];publicstaticint size;// 提交时把halveArray2改名为halveArraypublicstaticinthalveArray2(int[] nums){
size = nums.length;long sum =0;for(int i = size -1; i >=0; i--){
heap[i]=(long) nums[i]<<20;
sum += heap[i];heapify(i);}
sum /=2;int ans =0;for(long minus =0; minus < sum; ans++){
heap[0]/=2;
minus += heap[0];heapify(0);}return ans;}publicstaticvoidheapify(int i){int l = i *2+1;while(l < size){int best = l +1< size && heap[l +1]> heap[l]? l +1: l;
best = heap[best]> heap[i]? best : i;if(best == i){break;}swap(best, i);
i = best;
l = i *2+1;}}publicstaticvoidswap(int i,int j){long tmp = heap[i];
heap[i]= heap[j];
heap[j]= tmp;}}
28. (必备)基数排序
packageclass028;// 基数排序,acm练习风格// 测试链接 : https://www.luogu.com.cn/problem/P1177// 请同学们务必参考如下代码中关于输入、输出的处理// 这是输入输出处理效率很高的写法// 提交以下的code,提交时请把类名改成"Main",可以直接通过importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.io.OutputStreamWriter;importjava.io.PrintWriter;importjava.io.StreamTokenizer;importjava.util.Arrays;publicclassCode01_RadixSort{// 可以设置进制,不一定10进制,随你设置publicstaticintBASE=10;publicstaticintMAXN=100001;publicstaticint[] arr =newint[MAXN];publicstaticint[] help =newint[MAXN];publicstaticint[] cnts =newint[BASE];publicstaticint n;publicstaticvoidmain(String[] args)throwsIOException{BufferedReader br =newBufferedReader(newInputStreamReader(System.in));StreamTokenizer in =newStreamTokenizer(br);PrintWriter out =newPrintWriter(newOutputStreamWriter(System.out));
in.nextToken();
n =(int) in.nval;for(int i =0; i < n; i++){
in.nextToken();
arr[i]=(int) in.nval;}sort();for(int i =0; i < n -1; i++){
out.print(arr[i]+" ");}
out.println(arr[n -1]);
out.flush();
out.close();
br.close();}publicstaticvoidsort(){// 如果会溢出,那么要改用long类型数组来排序// 找到数组中的最小值int min = arr[0];for(int i =1; i < n; i++){
min =Math.min(min, arr[i]);}int max =0;for(int i =0; i < n; i++){// 数组中的每个数字,减去数组中的最小值,就把arr转成了非负数组
arr[i]-= min;// 记录数组中的最大值
max =Math.max(max, arr[i]);}// 根据最大值在BASE进制下的位数,决定基数排序做多少轮radixSort(bits(max));// 数组中所有数都减去了最小值,所以最后不要忘了还原for(int i =0; i < n; i++){
arr[i]+= min;}}// 返回number在BASE进制下有几位publicstaticintbits(int number){int ans =0;while(number >0){
ans++;
number /=BASE;}return ans;}// 基数排序核心代码// arr内要保证没有负数// m是arr中最大值在BASE进制下有几位publicstaticvoidradixSort(int bits){// 理解的时候可以假设BASE = 10for(int offset =1; bits >0; offset *=BASE, bits--){Arrays.fill(cnts,0);for(int i =0; i < n; i++){// 数字提取某一位的技巧
cnts[(arr[i]/ offset)%BASE]++;}for(int i =1; i <BASE; i++){
cnts[i]= cnts[i]+ cnts[i -1];}for(int i = n -1; i >=0; i--){// 前缀数量分区的技巧// 数字提取某一位的技巧
help[--cnts[(arr[i]/ offset)%BASE]]= arr[i];}for(int i =0; i < n; i++){
arr[i]= help[i];}}}}
packageclass028;importjava.util.Arrays;// 基数排序// 测试链接 : https://leetcode.cn/problems/sort-an-array/publicclassCode02_RadixSort{// 可以设置进制,不一定10进制,随你设置publicstaticintBASE=10;publicstaticintMAXN=50001;publicstaticint[] help =newint[MAXN];publicstaticint[] cnts =newint[BASE];publicstaticint[]sortArray(int[] arr){if(arr.length >1){// 如果会溢出,那么要改用long类型数组来排序int n = arr.length;// 找到数组中的最小值int min = arr[0];for(int i =1; i < n; i++){
min =Math.min(min, arr[i]);}int max =0;for(int i =0; i < n; i++){// 数组中的每个数字,减去数组中的最小值,就把arr转成了非负数组
arr[i]-= min;// 记录数组中的最大值
max =Math.max(max, arr[i]);}// 根据最大值在BASE进制下的位数,决定基数排序做多少轮radixSort(arr, n,bits(max));// 数组中所有数都减去了最小值,所以最后不要忘了还原for(int i =0; i < n; i++){
arr[i]+= min;}}return arr;}// 返回number在BASE进制下有几位publicstaticintbits(int number){int ans =0;while(number >0){
ans++;
number /=BASE;}return ans;}// 基数排序核心代码// arr内要保证没有负数// n是arr的长度// bits是arr中最大值在BASE进制下有几位publicstaticvoidradixSort(int[] arr,int n,int bits){// 理解的时候可以假设BASE = 10for(int offset =1; bits >0; offset *=BASE, bits--){Arrays.fill(cnts,0);for(int i =0; i < n; i++){// 数字提取某一位的技巧
cnts[(arr[i]/ offset)%BASE]++;}// 处理成前缀次数累加的形式for(int i =1; i <BASE; i++){
cnts[i]= cnts[i]+ cnts[i -1];}for(int i = n -1; i >=0; i--){// 前缀数量分区的技巧// 数字提取某一位的技巧
help[--cnts[(arr[i]/ offset)%BASE]]= arr[i];}for(int i =0; i < n; i++){
arr[i]= help[i];}}}}
29. (必备)重要排序算法的总结
30. (必备)异或运算
packageclass030;// 用异或运算交换两数的值publicclassCode01_SwapExclusiveOr{publicstaticvoidmain(String[] args){int a =-2323;int b =10;
a = a ^ b;
b = a ^ b;
a = a ^ b;System.out.println(a);System.out.println(b);int[] arr ={3,5};swap(arr,0,1);System.out.println(arr[0]);System.out.println(arr[1]);swap(arr,0,0);System.out.println(arr[0]);}// 当i!=j,没问题,会完成交换功能// 当i==j,会出错// 所以知道这种写法即可,并不推荐publicstaticvoidswap(int[] arr,int i,int j){
arr[i]= arr[i]^ arr[j];
arr[j]= arr[i]^ arr[j];
arr[i]= arr[i]^ arr[j];}}
packageclass030;// 不用任何判断语句和比较操作,返回两个数的最大值// 测试链接 : https://www.nowcoder.com/practice/d2707eaf98124f1e8f1d9c18ad487f76publicclassCode02_GetMaxWithoutJudge{// 必须保证n一定是0或者1// 0变1,1变0publicstaticintflip(int n){return n ^1;}// 非负数返回1// 负数返回0publicstaticintsign(int n){returnflip(n >>>31);}// 有溢出风险的实现publicstaticintgetMax1(int a,int b){int c = a - b;// c非负,returnA -> 1// c非负,returnB -> 0// c负数,returnA -> 0// c负数,returnB -> 1int returnA =sign(c);int returnB =flip(returnA);return a * returnA + b * returnB;}// 没有任何问题的实现publicstaticintgetMax2(int a,int b){// c可能是溢出的int c = a - b;// a的符号int sa =sign(a);// b的符号int sb =sign(b);// c的符号int sc =sign(c);// 判断A和B,符号是不是不一样,如果不一样diffAB=1,如果一样diffAB=0int diffAB = sa ^ sb;// 判断A和B,符号是不是一样,如果一样sameAB=1,如果不一样sameAB=0int sameAB =flip(diffAB);int returnA = diffAB * sa + sameAB * sc;int returnB =flip(returnA);return a * returnA + b * returnB;}publicstaticvoidmain(String[] args){int a =Integer.MIN_VALUE;int b =Integer.MAX_VALUE;// getMax1方法会错误,因为溢出System.out.println(getMax1(a, b));// getMax2方法永远正确System.out.println(getMax2(a, b));}}
packageclass030;// 数组中只有1种数出现次数少于m次,其他数都出现了m次// 返回出现次数小于m次的那种数// 测试链接 : https://leetcode.cn/problems/single-number-ii/// 注意 : 测试题目只是通用方法的一个特例,课上讲了更通用的情况publicclassCode06_OneKindNumberLessMtimes{publicstaticintsingleNumber(int[] nums){returnfind(nums,3);}// 更通用的方法// 已知数组中只有1种数出现次数少于m次,其他数都出现了m次// 返回出现次数小于m次的那种数publicstaticintfind(int[] arr,int m){// cnts[0] : 0位上有多少个1// cnts[i] : i位上有多少个1// cnts[31] : 31位上有多少个1int[] cnts =newint[32];for(int num : arr){for(int i =0; i <32; i++){
cnts[i]+=(num >> i)&1;}}int ans =0;for(int i =0; i <32; i++){if(cnts[i]% m !=0){
ans |=1<< i;}}return ans;}}
31. (必备)位运算
packageclass031;// Brian Kernighan算法// 提取出二进制里最右侧的1// 判断一个整数是不是2的幂// 测试链接 : https://leetcode.cn/problems/power-of-two/publicclassCode01_PowerOfTwo{publicstaticbooleanisPowerOfTwo(int n){return n >0&& n ==(n &-n);}}
packageclass031;// 判断一个整数是不是3的幂// 测试链接 : https://leetcode.cn/problems/power-of-three/publicclassCode02_PowerOfThree{// 如果一个数字是3的某次幂,那么这个数一定只含有3这个质数因子// 1162261467是int型范围内,最大的3的幂,它是3的19次方// 这个1162261467只含有3这个质数因子,如果n也是只含有3这个质数因子,那么// 1162261467 % n == 0// 反之如果1162261467 % n != 0 说明n一定含有其他因子publicstaticbooleanisPowerOfThree(int n){return n >0&&1162261467% n ==0;}}
packageclass031;// 已知n是非负数// 返回大于等于n的最小的2某次方// 如果int范围内不存在这样的数,返回整数最小值publicclassCode03_Near2power{publicstaticfinalintnear2power(int n){if(n <=0){return1;}
n--;
n |= n >>>1;
n |= n >>>2;
n |= n >>>4;
n |= n >>>8;
n |= n >>>16;return n +1;}publicstaticvoidmain(String[] args){int number =100;System.out.println(near2power(number));}}
packageclass031;// 给你两个整数 left 和 right ,表示区间 [left, right]// 返回此区间内所有数字 & 的结果// 包含 left 、right 端点// 测试链接 : https://leetcode.cn/problems/bitwise-and-of-numbers-range/publicclassCode04_LeftToRightAnd{publicstaticintrangeBitwiseAnd(int left,int right){while(left < right){
right -= right &-right;}return right;}}
packageclass031;// 逆序二进制的状态// 测试链接 : https://leetcode.cn/problems/reverse-bits/publicclassCode05_ReverseBits{// 逆序二进制的状态// 是不是看着头皮发麻啊?代码看着很魔幻吧?别慌publicstaticintreverseBits(int n){
n =((n &0xaaaaaaaa)>>>1)|((n &0x55555555)<<1);
n =((n &0xcccccccc)>>>2)|((n &0x33333333)<<2);
n =((n &0xf0f0f0f0)>>>4)|((n &0x0f0f0f0f)<<4);
n =((n &0xff00ff00)>>>8)|((n &0x00ff00ff)<<8);
n =(n >>>16)|(n <<16);return n;}}
packageclass031;// 返回n的二进制中有几个1// 两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。// 给你两个整数 x 和 y,计算并返回它们之间的汉明距离// 测试链接 : https://leetcode.cn/problems/hamming-distance/publicclassCode06_CountOnesBinarySystem{publicstaticinthammingDistance(int x,int y){returncntOnes(x ^ y);}// 返回n的二进制中有几个1// 这个实现脑洞太大了publicstaticintcntOnes(int n){
n =(n &0x55555555)+((n >>>1)&0x55555555);
n =(n &0x33333333)+((n >>>2)&0x33333333);
n =(n &0x0f0f0f0f)+((n >>>4)&0x0f0f0f0f);
n =(n &0x00ff00ff)+((n >>>8)&0x00ff00ff);
n =(n &0x0000ffff)+((n >>>16)&0x0000ffff);return n;}}
packageclass032;// 位图的实现// Bitset是一种能以紧凑形式存储位的数据结构// Bitset(int n) : 初始化n个位,所有位都是0// void fix(int i) : 将下标i的位上的值更新为1// void unfix(int i) : 将下标i的位上的值更新为0// void flip() : 翻转所有位的值// boolean all() : 是否所有位都是1// boolean one() : 是否至少有一位是1// int count() : 返回所有位中1的数量// String toString() : 返回所有位的状态publicclassCode02_DesignBitsetTest{// 测试链接 : https://leetcode-cn.com/problems/design-bitset/classBitset{privateint[] set;privatefinalint size;privateint zeros;privateint ones;privateboolean reverse;publicBitset(int n){
set =newint[(n +31)/32];
size = n;
zeros = n;
ones =0;
reverse =false;}// 把i这个数字加入到位图publicvoidfix(int i){int index = i /32;int bit = i %32;if(!reverse){// 位图所有位的状态,维持原始含义// 0 : 不存在// 1 : 存在if((set[index]&(1<< bit))==0){
zeros--;
ones++;
set[index]|=(1<< bit);}}else{// 位图所有位的状态,翻转了// 0 : 存在// 1 : 不存在if((set[index]&(1<< bit))!=0){
zeros--;
ones++;
set[index]^=(1<< bit);}}}// 把i这个数字从位图中移除publicvoidunfix(int i){int index = i /32;int bit = i %32;if(!reverse){if((set[index]&(1<< bit))!=0){
ones--;
zeros++;
set[index]^=(1<< bit);}}else{if((set[index]&(1<< bit))==0){
ones--;
zeros++;
set[index]|=(1<< bit);}}}publicvoidflip(){
reverse =!reverse;int tmp = zeros;
zeros = ones;
ones = tmp;}publicbooleanall(){return ones == size;}publicbooleanone(){return ones >0;}publicintcount(){return ones;}publicStringtoString(){StringBuilder builder =newStringBuilder();for(int i =0, k =0, number, status; i < size; k++){
number = set[k];for(int j =0; j <32&& i < size; j++, i++){
status =(number >> j)&1;
status ^= reverse ?1:0;
builder.append(status);}}return builder.toString();}}}
33. (必备)位运算实现加减乘除
packageclass033;// 不用任何算术运算,只用位运算实现加减乘除// 代码实现中你找不到任何一个算术运算符// 测试链接 : https://leetcode.cn/problems/divide-two-integers/publicclassBitOperationAddMinusMultiplyDivide{publicstaticintMIN=Integer.MIN_VALUE;publicstaticintdivide(int a,int b){if(a ==MIN&& b ==MIN){// a和b都是整数最小return1;}if(a !=MIN&& b !=MIN){// a和b都不是整数最小,那么正常去除returndiv(a, b);}if(b ==MIN){// a不是整数最小,b是整数最小return0;}// a是整数最小,b是-1,返回整数最大,因为题目里明确这么说了if(b ==neg(1)){returnInteger.MAX_VALUE;}// a是整数最小,b不是整数最小,b也不是-1
a =add(a, b >0? b :neg(b));int ans =div(a, b);int offset = b >0?neg(1):1;returnadd(ans, offset);}// 必须保证a和b都不是整数最小值,返回a除以b的结果publicstaticintdiv(int a,int b){int x = a <0?neg(a): a;int y = b <0?neg(b): b;int ans =0;for(int i =30; i >=0; i =minus(i,1)){if((x >> i)>= y){
ans |=(1<< i);
x =minus(x, y << i);}}return a <0^ b <0?neg(ans): ans;}publicstaticintadd(int a,int b){int ans = a;while(b !=0){// ans : a和b无进位相加的结果
ans = a ^ b;// b : a和b相加时的进位信息
b =(a & b)<<1;
a = ans;}return ans;}publicstaticintminus(int a,int b){returnadd(a,neg(b));}publicstaticintneg(int n){returnadd(~n,1);}publicstaticintmultiply(int a,int b){int ans =0;while(b !=0){if((b &1)!=0){// 考察b当前最右的状态!
ans =add(ans, a);}
a <<=1;
b >>>=1;}return ans;}}
34. (必备)链表高频题目和必备技巧
packageclass034;// 返回两个无环链表相交的第一个节点// 测试链接 : https://leetcode.cn/problems/intersection-of-two-linked-lists/publicclassCode01_IntersectionOfTwoLinkedLists{// 提交时不要提交这个类publicstaticclassListNode{publicint val;publicListNode next;}// 提交如下的方法publicstaticListNodegetIntersectionNode(ListNode h1,ListNode h2){if(h1 ==null|| h2 ==null){returnnull;}ListNode a = h1, b = h2;int diff =0;while(a.next !=null){
a = a.next;
diff++;}while(b.next !=null){
b = b.next;
diff--;}if(a != b){returnnull;}if(diff >=0){
a = h1;
b = h2;}else{
a = h2;
b = h1;}
diff =Math.abs(diff);while(diff--!=0){
a = a.next;}while(a != b){
a = a.next;
b = b.next;}return a;}}
packageclass034;// 每k个节点一组翻转链表// 测试链接:https://leetcode.cn/problems/reverse-nodes-in-k-group/publicclassCode02_ReverseNodesInkGroup{// 不要提交这个类publicstaticclassListNode{publicint val;publicListNode next;}// 提交如下的方法publicstaticListNodereverseKGroup(ListNode head,int k){ListNode start = head;ListNode end =teamEnd(start, k);if(end ==null){return head;}// 第一组很特殊因为牵扯到换头的问题
head = end;reverse(start, end);// 翻转之后start变成了上一组的结尾节点ListNode lastTeamEnd = start;while(lastTeamEnd.next !=null){
start = lastTeamEnd.next;
end =teamEnd(start, k);if(end ==null){return head;}reverse(start, end);
lastTeamEnd.next = end;
lastTeamEnd = start;}return head;}// 当前组的开始节点是s,往下数k个找到当前组的结束节点返回publicstaticListNodeteamEnd(ListNode s,int k){while(--k !=0&& s !=null){
s = s.next;}return s;}// s -> a -> b -> c -> e -> 下一组的开始节点// 上面的链表通过如下的reverse方法调整成 : e -> c -> b -> a -> s -> 下一组的开始节点publicstaticvoidreverse(ListNode s,ListNode e){
e = e.next;ListNode pre =null, cur = s, next =null;while(cur != e){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;}
s.next = e;}}
packageclass034;// 复制带随机指针的链表// 测试链接 : https://leetcode.cn/problems/copy-list-with-random-pointer/publicclassCode03_CopyListWithRandomPointer{// 不要提交这个类publicstaticclassNode{publicint val;publicNode next;publicNode random;publicNode(int v){
val = v;}}// 提交如下的方法publicstaticNodecopyRandomList(Node head){if(head ==null){returnnull;}Node cur = head;Node next =null;// 1 -> 2 -> 3 -> ...// 变成 : 1 -> 1' -> 2 -> 2' -> 3 -> 3' -> ...while(cur !=null){
next = cur.next;
cur.next =newNode(cur.val);
cur.next.next = next;
cur = next;}
cur = head;Node copy =null;// 利用上面新老节点的结构关系,设置每一个新节点的random指针while(cur !=null){
next = cur.next.next;
copy = cur.next;
copy.random = cur.random !=null? cur.random.next :null;
cur = next;}Node ans = head.next;
cur = head;// 新老链表分离 : 老链表重新连在一起,新链表重新连在一起while(cur !=null){
next = cur.next.next;
copy = cur.next;
cur.next = next;
copy.next = next !=null? next.next :null;
cur = next;}// 返回新链表的头节点return ans;}}
packageclass034;// 判断链表是否是回文结构// 测试链接 : https://leetcode.cn/problems/palindrome-linked-list/publicclassCode04_PalindromeLinkedList{// 不要提交这个类publicstaticclassListNode{publicint val;publicListNode next;}// 提交如下的方法publicstaticbooleanisPalindrome(ListNode head){if(head ==null|| head.next ==null){returntrue;}ListNode slow = head, fast = head;// 找中点while(fast.next !=null&& fast.next.next !=null){
slow = slow.next;
fast = fast.next.next;}// 现在中点就是slow,从中点开始往后的节点逆序ListNode pre = slow;ListNode cur = pre.next;ListNode next =null;
pre.next =null;while(cur !=null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;}// 上面的过程已经把链表调整成从左右两侧往中间指// head -> ... -> slow <- ... <- preboolean ans =true;ListNode left = head;ListNode right = pre;// left往右、right往左,每一步比对值是否一样,如果某一步不一样答案就是falsewhile(left !=null&& right !=null){if(left.val != right.val){
ans =false;break;}
left = left.next;
right = right.next;}// 本着不坑的原则,把链表调整回原来的样子再返回判断结果
cur = pre.next;
pre.next =null;
next =null;while(cur !=null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;}return ans;}}