算法学习1

位运算

异或

  1. 一个数组中只有一个数出现了奇数次,其他都出现了偶数次。以最快的速度找到这个出现计数次的数。
    利用数字异或。
  public static void main(String[] args) {
      List<Integer> list = Arrays.asList(7, 4, 7, 7, 4, 5, 6, 5, 6, 88, 99, 99, 88);
      System.out.println(getNumber(list));
  }
  private static int getNumber(List<Integer> list){
        int result = 0;
        for (Integer number:list) {
            result = result^number;
        }
        return result;
    }
  1. 两个数做交换不申请额外的空间
    public static void main(String[] args) {
        int first = 4;
        int second = 8;
        System.out.println("first:"+first);
        System.out.println("second:"+second);
        replaceNumber(first,second);
    }
    private static void replaceNumber(int first,int second){
        first = first^second;
        second = first^second;
        first = first^second;
        System.out.println("first:"+first);
        System.out.println("seconde:"+second);
    }
  1. 提取二进制数最右侧的1
    public static void main(String[] args) {
        // 0101
        int a = 5;
        System.out.println(a&(~a+1));
        int b = 5;
        System.out.println(b&(-b));
    }
  1. 一个数组中只有两个数a和b出现了奇数次,其他都出现了偶数次。以最快的速度找到这个出现奇数次的数。
  • 同1.获得一个中间数middle(a&b)
  • 同3.获取middle最右侧1的位置(a≠b),根据这个1的位置将数组分为两部分,一部分包含a,一部分包含b
  • 将第一部分按照1.操作,获得a或者b
    public static void main(String[] args) {
        List<Integer> array = Arrays.asList(7, 11, 7, 11, 23, 23, 56, 56, 7, 89, 9, 89, 9, 67, 67, 9);
        getNumber(array);
    }
    private static void getNumber(List<Integer> array) {
        int middle = 0;
        for (int number : array) {
            middle ^= number;
        }
        int rightOne = middle & (-middle);
        int leftNumber = 0;
        for (int number : array) {
            if ((number & rightOne) == 0) {
                leftNumber ^= number;
            }
        }
        System.out.println(leftNumber);
        System.out.println(leftNumber ^ middle);
    }

二分有序数组

查找某个数

  1. 找到一个数
    private static Integer findNumberLow(List<Integer> array, Integer number) {
        int maxIndex = array.size() - 1;
        int middleIndex = maxIndex / 2;
        int minIndex = 0;
        Integer middle;
        while (!(middle = array.get(middleIndex)).equals(number)) {
            if ((minIndex == middleIndex || maxIndex == middleIndex)) {
                return -1;
            }
            if (middle > number) {
                maxIndex = middleIndex;
                middleIndex = ((maxIndex - minIndex) / 2 + minIndex);
            }
            if (middle < number) {
                minIndex = middleIndex;
                middleIndex = (maxIndex - (maxIndex - minIndex) / 2);
            }
        }
        return middleIndex;
    }
    private static Integer findNumber(List<Integer> array, Integer number) {
        int maxIndex = array.size() - 1;
        int minIndex = 0;
        int middleIndex;
        while (minIndex <= maxIndex) {
            middleIndex = minIndex + ((maxIndex - minIndex) >> 1);
            Integer middle = array.get(middleIndex);
            if (middle > number) {
                maxIndex = middleIndex - 1;
            } else if (middle < number) {
                minIndex = middleIndex + 1;
            } else {
                return middleIndex;
            }
        }
        return -1;
    }
  1. 找到大于一个数最左的位置
    private static Integer findGreatLeft(List<Integer> array, Integer number) {
        int maxIndex = array.size() - 1;
        int middleIndex;
        int minIndex = 0;
        int greatLeft = -1;
        while (minIndex <= maxIndex) {
            middleIndex = minIndex + ((maxIndex - minIndex) >> 1);
            Integer middle = array.get(middleIndex);
            if (middle >= number) {
                greatLeft = middleIndex;
                maxIndex = middleIndex - 1;
            } else {
                minIndex = middleIndex + 1;
            }
        }
        return greatLeft;
    }
  1. 找到小于一个数最右的位置
    private static Integer findLessRight(List<Integer> array, Integer number) {
        int maxIndex = array.size() - 1;
        int middleIndex;
        int minIndex = 0;
        int greatLeft = -1;
        while (minIndex <= maxIndex) {
            middleIndex = minIndex + ((maxIndex - minIndex) >> 1);
            Integer middle = array.get(middleIndex);
            if (middle > number) {
                maxIndex = middleIndex - 1;
            } else {
                greatLeft = middleIndex;
                minIndex = middleIndex + 1;
            }
        }
        return greatLeft;
    }
  1. assert
    private static void assertTrue(){
        Random random = new Random();
        for (int times=0;times<10000;times++){
            List<Integer> array = new ArrayList<>();
            for (int i=0;i<100;i++){
                array.add(random.nextInt(1000));
            }
            array = array.stream().sorted().distinct().collect(Collectors.toList());
            int number = random.nextInt(1000);
            Assert.isTrue(findNumberForeach(array,number).equals(findNumber(array,number)),
                    "findNumberForeach(array,number).equals(findNumber(array,number))"+array+";"+number);
            Assert.isTrue(findNumberForeach(array,number).equals(findNumberLow(array,number)),
                    "findNumberForeach(array,number).equals(findNumberLow(array,number))"+array+";"+number);
            Assert.isTrue(findGreatLeftForeach(array,number).equals(findGreatLeft(array,number)),
                    "findGreatLeftForeach(array,number).equals(findGreatLeft(array,number))"+array+";"+number);
            Assert.isTrue(findLessRightForeach(array,number).equals(findLessRight(array,number)),
                    "findLessRightForeach(array,number).equals(findLessRight(array,number))"+array+";"+number);
        }
    }

二分答案

  • 确定种子范围1-n
  • 确定答案和条件的单调性
  • 二分
  1. 定义一种算法数字和:例如:345:345+34+3=382,则384为345的数字和,345为384的种子数。给定一个数n求他的种子
    @Test
    public void testNumberSum() {
        Assert.isTrue(numberSumRecursion(345) == 382);
        Assert.isTrue(getSeed(382) == 345);
    }

    public int getSeed(int number) {
        int max = number;
        int min = 0;
        int seed;
        int sum = -1;
        while (true) {
            seed = (min + ((max - min) >> 1));
            sum = numberSumRecursion(seed);
            if (sum > number) {
                max = seed - 1;
            }
            if (sum < number) {
                min = seed + 1;
            }
            if (sum == number) {
                break;
            }
        }
        return seed;
    }

    public int numberSumRecursion(int number) {
        int i = number / 10;
        if (i > 0) {
            return number + numberSumRecursion(i);
        } else {
            return number;
        }
    }
  1. 猴子喜欢吃香蕉,这里有n堆香蕉,i堆香蕉的个数为arr[i],猴子以一定的速度k吃香蕉,每小时只吃一次。管理员离开h小时,猴子以最慢的速度吃完,最慢速度为多少。
       // https://leetcode.cn/problems/koko-eating-bananas/submissions/
       public int minEatingSpeed(int[] piles, int h) {
        int maxSpeed = getMaxSpeed(piles);
        int minSpeed = 1;
        int middleSpeed = -1;
        long resultHour;
        // resultHour<=hour
        int resultSpeed = -1;
        while (minSpeed <= maxSpeed) {
            middleSpeed = (minSpeed + ((maxSpeed - minSpeed) >> 1));
            resultHour = getEatH(piles, middleSpeed);
            if (resultHour > h) {
                minSpeed = middleSpeed + 1;
            } else {
                maxSpeed = middleSpeed - 1;
                resultSpeed= middleSpeed;
            }
        }
        return resultSpeed;
    }

    private long getEatH(int[] arr, int k) {
        long resultHour = 0;
        for (int i : arr) {
            resultHour += (i + k - 1) / k;
        }
        return resultHour;
    }

    private int getMaxSpeed(int[] arr) {
        int result = -1;
        for (int i : arr) {
            if (i > result) {
                result = i;
            }
        }
        return result;
    }
  1. 画家画画,加油桶
    需要处理的画作arr[],画家人数k,最短时间完成
    public int splitArray(int[] nums, int k) {
        long result = Long.MAX_VALUE;
        long maxTime = getMaxTime(nums);
        long minTime = 0;
        while (minTime <= maxTime) {
            long middleTime = (minTime + ((maxTime - minTime) >> 1));
            long completeK = getCompleteK(nums, middleTime);
            if (completeK > k) {
                minTime = middleTime + 1;
            } else if (completeK <= k) {
                maxTime = middleTime - 1;
                result = middleTime;
            }
        }
        return (int) result;
    }

    private long getCompleteK(int[] arr, long time) {
        long resultK = 1;
        long everyTime = time;
        for (int i : arr) {
            if (time < i) {
                return Long.MAX_VALUE;
            }
            everyTime -= i;
            if (everyTime < 0) {
                everyTime = time - i;
                resultK += 1;
            }
        }
        return resultK;
    }

    private long getMaxTime(int[] arr) {
        long result = 0;
        for (int i : arr) {
            result += i;
        }
        return result;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值