算法题记录

二进制运算

  1. 一个数组中有一种数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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值