算法笔记-第02节课

第02节课

1. 安位异或交换两个数

如何不用额外变量交换两个数

代码展示:

static void swap(int[] arr, int i, int j) {
  if(arr == null || arr.length < 2 || i == j) return;
  arr[i] = arr[i]^arr[j];
  arr[j] = arr[i]^arr[j];
  arr[i] = arr[i]^arr[j];
}

2. 找出数组中唯一出现奇数次的数

一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数

思路:

将数组中的所有数字异或,出现了偶数次的元素全部被异或调,剩下唯一的一个出现了奇数次的元素

代码展示:

static int printOddTimesNum1(int[] arr) {
  int eor = 0;
  for (int x : arr) {
    eor ^= x;
  }
  return eor;
}

3. 提取整数最右侧的1

怎么把一个int类型的数,提取出最右侧的1来

思路:

一个数在Java中表示方法为补码,正数的补码为自己,负数的补码为反码+1,一个数按位与上自己的镜像数可以得到右侧的1的正数

代码展示:

static int rightOne(int x) {
  return x & (-x)
}

4. 找出数组中出现奇数次的两个数(其他数都是偶数次)

一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数

思路:

  1. 首先把所有的数按位异或,最终得到的数x只是两个出现奇数次的数的异或结果
  2. 找到结果数x的最右非0位置的数y,用这个数y来区分原来的两个数
  3. 用数y可以把原数组分离成两个数组,每一个数组中只会出现一个出现奇数次的数

代码展示:

static int[] printOddTimesNum2(int[] arr) {
  if(arr == null && arr.length < 2) {
    return new int[]{-1, -1};
  }
  int tmp = 0;
  for(int ele: arr) {
    tmp ^= ele;
  }
  int rightOne = tmp & (-tmp);
  int onlyOne = 0;
  for(int ele: arr) {
    if((ele & rightOne) != 0) {
      onlyOne ^= ele;
    }
  }
  
  return new int[] {onlyOne, tmp ^ onlyOne}

}

5. 数组中其他数都是出现了M次,找出唯一出现了K次的数

一个数组中有一种数出现K次,其他数都出现了M次,M>1K<M。找到,出现了K次的数,要求,额外空间复杂度O(1),时间复杂度O(N)

思路:

对于一个整数,计算机使用32bit表示,整数使用补码表示为32位bit位
如果将每一个整数的32个bit位分别累加,出现了M次的数,对应的二进制位和一定能别M整除,而出现了K次的则不能,因为有条件K<M限制

代码展示:

static int kmNormal(int[] arr, int k, int m) {
  if (arr == null || arr.length < k) {
    return -1;
  }

  Map<Integer, Integer> map = new HashMap<>();
  for (int element : arr) {
    if (map.containsKey(element)) {
      map.put(element, map.get(element) + 1);
    } else {
      map.put(element, 1);
    }
  }

  for (Integer key : map.keySet()) {
    if (map.get(key) == k) {
      return key;
    }
  }
  return -1;
}

public static int onlyKTimes(int[] arr, int k, int m) {
  if(arr == null || arr.length == 0) return -1;

  int[] cells = new int[32];
  for(int i = 0; i < arr.length; i++) {
    for(int j = 0; j < 32; j++) {
      cells[j] += (arr[i] >> j);
    }
  }
  int ans = 0;
  for(int i = 0; i < 32; i++) {
    if(cells[i]%m != 0) {
      if(cells[i]%m == k) {
        ans |= (1 << i);
      } else {
        return -1;
      }
    }
  }

  if(ans == 0) {
    int count = 0;
    for(int i = 0; i < arr.length; i++) {
      if(arr[i] == 0) {
        count++;
      }
    }
    if (count != k) {
      return -1;
    }
  }
  return ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值