不是怎么还在转啊...

001-算法随记

异或

  1. 异或^:无进位相加(同0异1)

  2. 0^N = N, N^N=0

  3. 交换律、结合律

  4. 一串数异或,改顺序不影响结果

  5. 交换两个数,不申请额外空间

    • 前提:a和b指向的内存是两块东西,比如在数组中,i位置不能等于j位置,如果i位置等于j位置,这个位置会被洗成0,所以不建议这么换
    // a = 甲,b = 乙
    a = a ^ b;  // a = 甲^乙,b = 乙
    b = a ^ b;  // a = 甲^乙,b = 甲^乙^乙 = 甲
    a = a ^ b;  // a = 甲^乙^甲 = 0^乙 = 乙,b = 甲
    

题目

  1. 一个数组中只有一种数出现奇数次,其余数出现偶数次,如何找到这个数?(要求时O(N),空O(1))

    • 从头到尾异或,所有偶数次的数异或结果是0,0和剩下一个数字异或得到哪个数字
  2. 一个数组中只有两种数出现奇数次,其余数出现偶数次,如何找到这个数?(要求时O(N),空O(1))

    • 从头异或到尾,假设两个目标数字是a和b,最终结果eor=a^b

    • 又a和b不相等,所以eor不为0

    • 此时a和b至少有1位不同,例如第八位不同,会使eor的第八位为1

    • 实际中可以提取最右边的1

      // 取最右侧的1,其余位为0
      int rightOne = eor & (~eor + 1);
      

      image-20250817044137061

    • 所有数可以被分为第8为是1和第8位是0的数,a和b被分别分在两类中

    • 让eor2等于所有第8位是1的数异或得到的值,那这个值一定是a和b中的一个

      • 代码里让数组里的数和rightOne做与运算来判断这一位是不是1,是1才会返回1
    • 再让eor^eor2,得到另一个数

排序

插入排序

  • 从0-0有序一直扩容到0-N上有序
  • 每到一个数都一直往前换到它该在的位置
  • \(O(N^2)\)
  • 空间\(O(1)\)

冒泡排序

  • 相邻两个数不断往右换,每次往最后一个位置放一个最值
  • \(O(N^2)\)
  • 空间\(O(1)\)

选择排序

  • 每次选一个最值放最左边,然后再在这之外找一个最值放在这个右边一个位置,以此类推

二分法

  • 只要问题能甩到一边中,就可以二分,不是一定要有序

  • 常规二分:找一个有序数组中是否存在某个数

    • 找中间的数,然后根据大小找右边的中间或左边的中间,以此类推
  • 略微进阶:一个有序数组,找大于等于某个数的最左侧的位置或小于等于某个数最右侧的位置

    • 一直二分到不能二分,只要满足要求就继续二分,用找到的新结果替代旧结果
  • 进阶:一个无序数组,相邻数字一定不相等,求一个局部最小(可以用极小值理解:一个数小于左边且小于右边,首尾元素小于其相邻的数)的数字

    • 单独看0位置和N-1位置
    • 如果0到1下降,N-2到N-1上升,那么0到N-1一定有局部最小值,取中点M
    • 判断M是否为局部最小
      • 如果不是,则看左右的大小,来确定左右某一边有局部最小值,再接着二分

对数器

  • 每次测试生成一个随机数

    image-20250818015017905

  • 创建两个长度和值都随机的数组

    image-20250818015112975

  • 用一个绝对正确的方法跑其中一个数组

  • 用对数器方法(自己的算法)跑第二个数组

  • 检查结果是否相等

posted @ 2025-08-23 16:00  Quirkygbl  阅读(4)  评论(0)    收藏  举报