权限
例如,在一个系统中,用户一般有查询(Select)、新增(Insert)、修改(Update)、删除(Delete)四种权限,四种权限有多种组合方式,也就是有16中不同的权限状态(2的4次方)。
一般情况下会想到用四个boolean类型变量来保存:
public class Permission {
// 是否允许查询
private boolean allowSelect;
// 是否允许新增
private boolean allowInsert;
// 是否允许删除
private boolean allowDelete;
// 是否允许更新
private boolean allowUpdate;
// 省略Getter和Setter
}
上面用四个boolean类型变量来保存每种权限状态。
下面是另外一种方式,使用位掩码的话,用一个二进制数即可,每一位来表示一种权限,0表示无权限,1表示有权限。
public class NewPermission {
// 是否允许查询,二进制第1位,0表示否,1表示是
public static final int ALLOW_SELECT = 1 << 0; // 0001
// 是否允许新增,二进制第2位,0表示否,1表示是
public static final int ALLOW_INSERT = 1 << 1; // 0010
// 是否允许修改,二进制第3位,0表示否,1表示是
public static final int ALLOW_UPDATE = 1 << 2; // 0100
// 是否允许删除,二进制第4位,0表示否,1表示是
public static final int ALLOW_DELETE = 1 << 3; // 1000
// 存储目前的权限状态
private int flag;
/**
* 重新设置权限
*/
public void setPermission(int permission) {
flag = permission;
}
/**
* 添加一项或多项权限
*/
public void enable(int permission) {
flag |= permission;
}
/**
* 删除一项或多项权限
*/
public void disable(int permission) {
flag &= ~permission;
}
/**
* 是否拥某些权限
*/
public boolean isAllow(int permission) {
return (flag & permission) == permission;
}
/**
* 是否禁用了某些权限
*/
public boolean isNotAllow(int permission) {
return (flag & permission) == 0;
}
/**
* 是否仅仅拥有某些权限
*/
public boolean isOnlyAllow(int permission) {
return flag == permission;
}
}
如果不用任何数就交换两个变量的值
a = a ^ b;
b = a ^ b;
a = a ^ b;
^也叫做无进位加法,它有三个定律:
- 定律一:0
^
N = N - 定律二:N ^ N=0
- 定律三:其加法满足结合律
上面就是结合律的应用
主要,上面的这种交换方法仅仅a和b分别指向不同的内存是才能交换,如果是同一个内存,那么就是第二条定律,会变为0
把int最右侧的1提取出来
t = a & (-a);
出现奇数次的数
一个数组中有一种数出现了奇数次,其他数出现了偶数次,怎么找出并打印这种数
// arr中,只有一种数,出现奇数次
void printOddTimesNum1(std::vector<int> &vec){
int val = 0;
for (int i : vec) {
val ^= i;
}
std::cout << val;
}
int main(){
std::vector<int> arr1 { 3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1 };
printOddTimesNum1(arr1);
}
第二定律 + 第三定律
一个数组中有两种数出现了奇数次,其他数出现了偶数次,怎么找出并打印这种数
关键在于:怎么把两种奇数次的数分离。
(1)遍历一次数字,得到 eor = a ^ b
(2)因为eor一定不为0,所以eor二进制表示中一定有一个1。我们拿出这个1(随便哪个1都可以,这里规定最右侧的1),假设第三位为1
(3)二进制中有1,那么说明a、b这个位上一定不相同。同样的,对于所有的arr,一定也可以分为第三位为1的,和第三位为0的数。
(4)我们申请一个新的eor1,把那些第三位为1的数拉到eor1,这样就可以把a拉出来了。,也就是eor1 = a
(5)然后我们就可以拿到eor2=b了
// arr中,有两种数,出现奇数次
void printOddTimesNum2(std::vector<int> &arr){
int eor = 0;
for (int i = 0; i < arr.size(); ++i) {
eor ^= arr[i];
}
// a 和 b是两种数
// eor != 0
// eor最右侧的1,提取出来
// eor : 00110010110111000
// rightOne :00000000000001000
int rightOne = eor & (-eor); // 提取出最右的1
int onlyOne = 0 ;
for (int i = 0; i < arr.size(); ++i) {
// arr[1] = 111100011110000
// rightOne= 000000000010000
if((rightOne & arr[i]) == 0){
onlyOne ^= arr[i];
}
}
std::cout << onlyOne <<"\t" << (eor ^ onlyOne) <<"\n";
}
int main(){
std::vector<int> arr1 { 1, 1, 1, 1, 2, 2, 2, 3, 3, 3 };
printOddTimesNum2(arr1);
}
一个数组中有两种数出现了M次,其他数出现了M次,并且M > 1, K > M,请怎么找出出现了K次的数,要求:额外空间复杂度O(1),时间复杂度O(N)
一个整形,是32位。对于一个数,比如8,那么它可以转换为一个32位的二进制数组
(1)因此,准备一个固定长度为32位的数组int []t
(2)然后将每一个数转为32位,并加到t中的某个位置上。
(3)对int []t每一个%M,然后转为num,就是答案
// 请保证arr中,只有一种数出现了K次,其他数都出现了M次
int onlyKTimes(std::vector<int> arr, int k, int m){
// t[0] 0位置的1出现了几个
// t[i] i位置的1出现了几个
std::vector<int> t(32);
for(int num : arr){
for (int i = 0; i <= 31; ++i) {
if (((num >> i) & 1) != 0){
t[i]++;
}
}
}
int ans = 0;
// 如果这个出现了K次的数,就是0
// 那么下面代码中的 : ans |= (1 << i);
// 就不会发生
// 那么ans就会一直维持0,最后返回0,也是对的!
for(int i = 0; i < 32; i++){
if(t[i] % m != 0){ //
ans |= (1 << i);
}
}
return ans;
}
下面是对数器代码:
#include <random> // mt19937 and uniform_int_distribution
#include <algorithm> // generate
#include <vector> // vector
#include <iterator> // begin, end, and ostream_iterator
#include <functional> // bind
#include <iostream> // cout
#include <chrono>
#include <map>
#include <set>
// 请保证arr中,只有一种数出现了K次,其他数都出现了M次
int onlyKTimes(std::vector<int> arr, int k, int m){
// t[0] 0位置的1出现了几个
// t[i] i位置的1出现了几个
std::vector<int> t(32);
for(int num : arr){
for (int i = 0; i <= 31; ++i) {
if (((num >> i) & 1) != 0){
t[i]++;
}
}
}
int ans = 0;
// 如果这个出现了K次的数,就是0
// 那么下面代码中的 : ans |= (1 << i);
// 就不会发生
// 那么ans就会一直维持0,最后返回0,也是对的!
for(int i = 0; i < 32; i++){
if(t[i] % m != 0){ //
ans |= (1 << i);
}
}
return ans;
}
static int test(std::vector<int> arr, int k, int m){
std::map<int, int> mp;
for(int num : arr){
mp[num]++;
}
for(auto it : mp){
if(it.second == k){
return it.first;
}
}
return -1;
}
std::default_random_engine e;
int RandomNumber(int minValue, int maxValue){
std::uniform_int_distribution<int> distV(minValue, maxValue);
return distV(e);
}
// 请保证arr中,只有一种数出现了K次,其他数都出现了M次
std::vector<int> generateRandomArray(int maxkinds, int minValue, int maxValue, int k, int m) {
int ktimeNum = RandomNumber(minValue, maxValue);
int numKinds = RandomNumber(2, 2 + maxkinds);
int size = k + (numKinds - 1) * m;
std::vector<int> v(size);
int index = 0;
for (; index < k; ++index) {
v[index] = ktimeNum;
}
numKinds--;
std::set<int> set;
set.insert(ktimeNum);
while (numKinds != 0){
// 要新的数
int curNum = 0;
do{
curNum = RandomNumber(minValue, maxValue);
}while (set.count(curNum));
set.insert(curNum);
numKinds--;
for(int i = 0; i < m; i++){
v[index++] = curNum;
}
}
// 打乱v
std::shuffle(v.begin(), v.end(), e);
return v;
}
int main(){
// e.seed(time(NULL));
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::nanoseconds;
auto start = high_resolution_clock::now();
int kinds = 10;
int range = 200;
int testTime = 10000;
int max = 9;
std::uniform_int_distribution<int> distV(1, 9);
for (int i = 0; i < testTime; ++i) {
int a = distV(e);
int b = distV(e);
int k = std::min(a, b);
int m = std::max(a, b);
if(k == m){
m++;
}
std::vector<int> random_data = generateRandomArray(kinds, -range, range, k, m);
int ans1 = test(random_data, k, m);
int ans2 = onlyKTimes(random_data, k, m);
if(ans1 != ans2){
std::cout << "出错了\r\n";
std::cout << "["<< k << ", "<< m << "] arr: ";
std::sort(random_data.begin(), random_data.end());
copy(begin(random_data), end(random_data),
std::ostream_iterator<int>(std::cout, "\t"));
std::cout <<std::endl;
break;
}
}
auto end = high_resolution_clock::now();
std::cout << "测试结束: "<< duration_cast<nanoseconds>(end - start).count() << "ns\n";
return 0;
}
往右找最接近n的奇数
i | n
#include <stdio.h>
int main(){
for(int i = -10; i < 10; i++){
printf("%d, %d\t\t\t", i, i | 1);
if(i % 10 == 0){
printf("\n");
}
}
}