二进制运算
- 一个数组中有一种数n出现K次,其他都出现了M次 K<M,M>1 返回这个出现K次的数
private static int onleTimes(int[] array, int k, int m) {
// 新建一个长度为32的数组记录这个数组中的数在每个位置上1的个数
int[] a = new int[32];
for (int num : array) {
for (int i = 0; i < 32; i++) {
// i位置是1 进行记录
// if (((num>>i)&1) == 1){
// a[i]++;
// }
// 第二种写法
a[i] += (num >> i) & 1;
}
}
int an = 0;
for (int i = 0; i < 32; i++) {
if ((a[i] % m) != 0) { // 这个位置上不是m的倍数,所以这种数n在这个位置上有1
an |= (1 << i);// 得到结果
}
}
return an;
}
归并排序
1.归并排序
/**
* 将 数组 在 L-R 范围上排序
*
* @author hxwang
* @date 2022/11/26
*/
public static void process(int l, int r, int[] array) {
if (l == r) {
return;
}
// 1.取中点的下标
int mid = (l + (r - l) >> 1);
// 2.让数组的左边有序
process(l, mid, array);
// 3.让数组的右边有序
process(mid + 1, r, array);
// 4.将左右两个合在一起
merge(mid, l, r, array);
}
private static void merge(int m, int l, int r, int[] array) {
int[] newArray = new int[r - l + 1];
int i = 0;
int p1 = l;
int p2 = m + 1;
// 左数组和又数组比较 那边数小取那个 如果相等取左边
while (p1 <= m && p2 <= r) {
newArray[i++] = array[p1] <= array[p2] ? array[p1++] : array[p2++];
}
// p1越界 或者 p2越界
while (p1 <= m) {
newArray[i++] = array[p1++];
}
while (p2 <= r) {
newArray[i++] = array[p2++];
}
// 将排列好的数组复制回原数组
for (int j = 0; j < newArray.length; j++) {
array[l + j] = newArray[j];
}
}
/**
* 使用递归的方式实现归并排序
*
* @author hxwang
* @date 2022/11/26
*/
public static void mergeSort1(int[] array) {
if (array == null || array.length < 2) {
return;
}
process(0, array.length - 1, array);
}
/**
* 使用非递归的方式实现
*
* @author hxwang
* @date 2022/11/26
*/
public static void mergeSort2(int[] array) {
if (array == null || array.length < 2) {
return;
}
int n = array.length;
// 1.设置步长
int mergeSize = 1;
while (mergeSize < n) {
// 当前左组的第一个位置
int l = 0;
while (l < n) {
int m = l + mergeSize - 1;
// 剩余的数不够
if (m >= n) {
break;
}
// 如果对于此步长数据够的话 取 (m+步长),如果不够取 数组最后一位
int r = Math.min(m + mergeSize, n - 1);
merge(m, l, r, array);
l = m + 1;
// 防止整数溢出
if (mergeSize > n / 2) {
break;
}
}
// 步长乘2
mergeSize <<= 1;
}
}
小和问题
给定一个数组,数组左边的数比右边的数小的数之和
例:[3,2,1,1] 小和为0 [2,3,1,4] 小和为2+2+3+1
// 在数组的 l - r 范围上有序并返回小和数
public static int process(int l, int r, int[] array) {
if (l == r) {
return 0;
}
// 加号优先级要 大于 右移
int m = l + ((r - l) >> 1);
return process(l, m, array) + process(m + 1, r, array) + merger(l, r, m, array);
}
public static int merger(int l, int r, int m, int[] array) {
int p1 = l;
int p2 = m + 1;
int res = 0;
int i = 0;
int[] newArray = new int[r - l + 1];
while (p1 <= m && p2 <= r) {
// 当左组中的数小时取左组的数
res += array[p1] < array[p2] ? (r - p2 + 1) * array[p1] : 0;
newArray[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
}
// 有一组数越界
while (p1 <= m) {
newArray[i++] = array[p1++];
}
while (p2 <= r) {
newArray[i++] = array[p2++];
}
// 将排序好的数并入原数组
for (i = 0; i < newArray.length; i++) {
array[l + i] = newArray[i];
}
return res;
}
public static int smallSum(int[] array) {
if (array == null || array.length < 2) {
return 0;
}
return process(0, array.length - 1, array);
}
leetcode 上的题
https://leetcode.cn/problems/count-of-range-sum/submissions/
/**
* <H2> 归并排序</H2>
* 给你一个整数数组nums 以及两个整数lower 和 upper 。求数组中,值位于范围 [lower, upper] (包含lower和upper)之内的 区间和的个数 。
* 区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/count-of-range-sum
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*
* @author hxwang
* @data 2022/12/5
*/
public class CountRangeSum {
/*
问题转换:求出数组的区间和数组
*/
public int countRangeSum(int[] nums, int lower, int upper) {
if (nums == null || nums.length == 0) {
return 0;
}
//1. 获得区间和数组
long [] sum = new long[nums.length];
sum[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
sum[i] = sum[i - 1] + nums[i];
}
return process(sum, 0, sum.length - 1, lower, upper);
}
private int process(long[] sum, int l, int r, int lower, int upper) {
// 当区间内只有一个数时判断这个数是否符合条件
if (l == r) {
return sum[l] >= lower && sum[l] <= upper ? 1 : 0;
}
// 2. 左组符合范围的个数+右组符合范围的个数+合并过程中符合范围的个数
int m = l + ((r - l) >> 1);// 取中点
return process(sum, l, m, lower, upper)
+ process(sum, m + 1, r, lower, upper)
+ merge(sum, l, m, r, lower, upper);
}
private int merge(long[] arr, int l, int m, int r, int lower, int upper) {
// 3. 符合条件的数是 右组的数-左组的数在条件范围内 转化为 左组的数在[右组的数-upper,左组的数-lower]范围内
int ans = 0;
// 设定一个窗口,因为左组和右组分别有序所以窗口不会回退 时间复杂度为
int windowL = l;
int windowR = l;
for (int i = m + 1; i <= r; i++) {
long min = arr[i] - upper;
long max = arr[i] - lower;
// 右边界不能大于max
while (windowR <= m && arr[windowR] <= max) {
windowR++;
}
// 左边界不能小于min,如果小于min边界向右移
while (windowL <= m && arr[windowL] < min) {
windowL++;
}
// 窗口内的数为符合条件的数
ans += windowR - windowL;
}
long[] help = new long[r - l + 1];
int i = 0;
int p1 = l;
int p2 = m + 1;
while (p1 <= m && p2 <= r) {
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
// 有一个不满足条件下面两个while 只会执行一个
while (p1 <= m) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
// 将help数组复制回原始数组
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
return ans;
}
}
快速排序
public class HeLangGuoQi {
/**
* 荷兰国旗问题,以最后一个数为基准,让数组中小于这个数的放左边,大于这个数的放右边,等于这个数的放中间
*
* @param l 数组的做下标
* @param r 数组的有下标
* @return 等于目标数的下标范围
* @author hxwang
* @date 2022/12/15
*/
public static int[] flag(int[] arr, int l, int r) {
int less = l - 1; // 小于边界
int more = r; // 大于边界
int index = l;// 当前数的位置
while (index < more) {
if (arr[index] == arr[r]) { // 当前位置的数和目标数相等时,当前数的位置加一
index++;
} else if (arr[index] < arr[r]) { // 当前位置的数小于目标数时,当前数的位置加一,当前数和小于边界的下一个数交换,小于边界向后移
swap(arr, index++, ++less);
} else if (arr[index] > arr[r]) { // 当前位置的数大于目标数时,当前数的位置不动,当前数和大于边界的前一个数交换,大于向前移
swap(arr, index, --more);
}
}
swap(arr, arr[r], arr[more]);// 让最后一个数和右边界的第一个数交换
return new int[]{less + 1, more};
}
/**
* 使数组[l...r]有序
*
* @author hxwang
* @date 2022/12/15
*/
public static void process(int[] arr, int l, int r) {
if (l >= r) {
return;
}
// 从数组中取一个作为目标数这样可以避免最差情况,时间复杂度为O(N^2),可以将时间复杂度降为O(n*㏒N)
swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
// 荷兰国旗问题
int[] ints = flag(arr, l, r);
process(arr, l, ints[0] - 1);
process(arr, ints[1] + 1, r);
}
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
private static void swap(int[] arr, int index, int less) {
arr[index] ^= arr[less];
arr[less] ^= arr[index];
arr[index] ^= arr[less];
}
}
堆排序
已知一个几乎有序的数组,进行排序
几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离一定不超过K, 并且K相对于数组的擦航渡来说是比较小的 /**
* 思路:设定一个堆大小为(K+1)的小根堆,将数组的前(K+1)的数组成小跟堆,
* 然后弹出最小的数,此后放入一个数弹出一个数,最后弹空
*
*/
public static void sortedArrDistanceLessK(int[] arr, int k) {
if (k == 0) {
return;
}
// 默认小根堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
int index = 0;
while (index <= Math.min(arr.length - 1, k - 1)) {
minHeap.add(arr[index++]);
}
int i = 0;
while (!minHeap.isEmpty()) {
if (index < arr.length) {
minHeap.add(arr[index++]);
}
arr[i++] = minHeap.poll();
}
}
https://leetcode.cn/problems/median-of-two-sorted-arrays/
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
boolean flag = true;
int total = nums1.length + nums2.length;
int m = total / 2;
if (total % 2 == 0) {
flag = false;
}
int p1 = 0;
int p2 = 0;
if (nums1.length == 0) {
if (nums2.length == 1) {
return nums2[0];
}
return flag ? nums2[m] : (double) (nums2[m] + nums2[m - 1]) / 2;
}
if (nums2.length == 0) {
if (nums1.length == 1) {
return nums1[0];
}
return flag ? nums1[m] : (double) (nums1[m] + nums1[m - 1]) / 2;
}
int[] help = new int[nums1.length + nums2.length];
int i = 0;
while (p1 < nums1.length && p2 < nums2.length) {
help[i] = nums1[p1] < nums2[p2] ? nums1[p1++] : nums2[p2++];
if (i == m) {
return flag ? help[m] : (double) (help[m] + help[m - 1]) / 2;
}
i++;
}
while (p1 < nums1.length) {
help[i] = nums1[p1++];
if (i == m) {
return flag ? help[m] : (double) (help[m] + help[m - 1]) / 2;
}
i++;
}
while (p2 < nums2.length) {
help[i] = nums2[p2++];
if (i == m) {
return flag ? help[m] : (double) (help[m] + help[m - 1]) / 2;
}
i++;
}
return 0;
}
}