位运算应用

题目

  题1:找出唯一成对的数。

  题2:找出落单的那个数。

  题3:二进制中1的个数。

  题4:是不是2的整数次方。

  题5:将整数的奇偶位互换。

  题6:0~1间的浮点实数的二进制表示。

  题7:出现k次与出现1次。

  题目来源为蓝桥杯的一个培训课(主讲人:郑未)。

测试设计

 c++程序中使用的产生随机数的测试头文件,如下:

  1 /**
  2     调用格式    
  3             a = GenerateDiffNumber(min,max,num);
  4             a = Repeatedrandomnumber(min,max,num);//默认num为奇数 
  5             a = D_B(N);//默认为32位二进制 
  6             a = random_3();//1个随机数 
  7             a = decimal();//0~1的随机数 
  8             a = radixto(num,base)//十进制转换为其他进制(2<=base<=34) string radixto(int num, int base) 
  9             Stringflip(array, basin, base);//一组十进制数array[len],转换为base进制数,使用string类型的basin[len]存储 
 10             a = oonra(N); //注意使用vector<int>().swap(a);释放容器,vector<int> oonra(int N) 
 11             a = krepetition(k); //重复数为k只有1个不重复,输入k。
 12      
 13                                             [author]:zlc,[date]:20204/19
 14 
 15 **/
 16 #include<vector>
 17 #include<iostream>
 18 #include<cstdlib>
 19 #include<ctime>
 20 #include<cstring>
 21 #include<algorithm>  
 22 using namespace std; 
 23 vector<int> GenerateDiffNumber(int min,int max,int num)
 24 {
 25     int rnd;
 26     vector<int> diff;
 27     vector<int> tmp;//存储剩余的数
 28     //初始化
 29     for(int i = min;i < max+1 ; i++ )
 30         tmp.push_back(i);
 31     srand((unsigned)time(0)); //初始化随机数种子
 32     for(int i = 0 ; i < num ; i++)
 33     {
 34         do{
 35             rnd = min+rand()%(max-min+1);
 36         }while(tmp.at(rnd-min)==-1);
 37         diff.push_back(rnd);
 38         tmp.at(rnd-min) = -1;
 39     }
 40     return diff;
 41 }
 42 
 43 vector<int> Repeatedrandomnumber(int min,int max,int num) 
 44 {
 45     int rnd, i, j;
 46     vector<int> pack;//存储数据 
 47     vector<int> tank(num); 
 48     srand((unsigned)time(0)); //初始化随机数种子
 49     
 50     pack = GenerateDiffNumber(min,max,num/2+1);
 51     int len = pack.size();
 52     for(i=0; i<len-1; i++)
 53         pack.push_back(pack[i]);
 54     tank = GenerateDiffNumber(1,num,num);
 55     for(i=0; i<num; i++){
 56         rnd = pack[tank[i]-1];//数组下标需要-1 
 57         tank[i] = rnd;
 58     }
 59     return tank;
 60 }
 61 
 62 vector<int> D_B(int N)
 63 {
 64     int i=0;
 65     vector<int> can(32);//默认初始值为32个0 
 66     while(N){
 67         if(N%2 == 1)
 68             can[31-i] = 1;
 69         N /= 2;
 70         i++;
 71     } 
 72     return can;
 73 } 
 74 
 75 int random_3()//产生一个随机数 
 76 {
 77     int rnd;
 78     srand((unsigned)time(0)); //初始化随机数种子
 79     rnd = rand();
 80     return rnd;
 81     //return rnd%10;//得到个位数 
 82 }
 83 
 84 double decimal()
 85 {
 86     double x;
 87     x = 1.0 / random_3();
 88     return x;
 89 }
 90 
 91 int base = 10;//默认为十进制
 92 string radixto(int num, int base)//十进制转换为其他进制(2<=base<=34) 
 93 {
 94     int i, cur;
 95     string ch, st;
 96     //思路为短除法,且一次性得到翻转的进制数 
 97     st = "";
 98     if(num==0)
 99         return "0";
100     while(num){
101         cur = num%base;
102         if(cur <= 9) {
103             ch = cur+'0';
104             st.append(ch);    
105         }
106         else {
107             ch = cur-10+'A';
108             st.append(ch);    
109         }
110         num /= base;
111     } 
112     
113     return st;
114 }
115 
116 void* Stringflip(int arr[], string basin[], int base)
117 {
118     string st;
119     int len = sizeof(arr)*2;
120     for(int i=0; i<len; i++){//遍历每一个要转换的数 
121         st = radixto(arr[i], base);
122         reverse(st.begin(),st.end());  
123         basin[i].append(st);
124     } 
125 }
126 
127  
128 vector<int> oonra(int N)//只有一个不重复数组 Only one non repeating array,没有控制的随机长度 
129 {
130     int rnd, i, j, num, sum=0;
131     bool flag = true;
132     vector<int> pack;//存储重复数,清空后存储最终数据  
133     vector<int> holder; //存储未乱序的原始数组
134     srand((unsigned)time(0)); //初始化随机数种子
135     num = rand()%6+1;//得到数组中的种类数,默认不大于6 
136     
137     pack = GenerateDiffNumber(2,10,num);//默认数据的重复个数不超过10个 
138 
139     int len;
140     for(j=0; j<num; j++){//得到需要的随机数组的总长度 
141         sum += pack[j];
142         if(pack[j]==1)
143             flag = false;
144     } 
145 
146     if(flag){
147         sum = sum-pack[0]+1;
148         pack[0] = 1;    
149     }
150     
151     holder =  GenerateDiffNumber(1,N,num);//得到未重复的原始数据,N远大于6
152 
153     for(i=0; i<num; i++){
154         len = pack[i];
155         rnd = holder[i];
156         for(j=1; j<len; j++)
157             holder.push_back(rnd);
158     }
159 
160     vector<int> tank; //储存乱序因子
161     
162     tank = GenerateDiffNumber(1,sum,sum);//乱序因子 
163 
164     pack.clear();
165     for(int i=0; i<sum; i++){
166         rnd = holder[tank[i]-1];//数组下标需要-1 
167         pack.push_back(rnd);
168     }
169 
170     //释放容器 
171     vector<int>().swap(holder);
172     vector<int>().swap(tank);
173        return pack;
174 } 
175 
176 vector<int> krepetition(int k)
177 {
178     vector<int> pack;//存储未乱序的原始数组 
179     vector<int> holder; //存储重复数,清空后存储最终数据
180     int num, i, j;
181     srand((unsigned)time(0)); //初始化随机数种子
182     num = rand()%6+1;//得到数组中的种类数,默认不大于6 
183     pack = GenerateDiffNumber(1,100,num);//默认数据的重复个数不超过10个 
184     int rnd;
185     for(i=0; i<num-1; i++){
186         rnd = pack[i];
187         for(j=1; j<k; j++)
188             pack.push_back(rnd);
189     }
190     int len = pack.size();
191     
192     vector<int> tank; //储存乱序因子
193     
194     tank = GenerateDiffNumber(1,len,len);//乱序因子 
195 
196     for(int i=0; i<len; i++){
197         rnd = pack[tank[i]-1];//数组下标需要-1 
198         holder.push_back(rnd);
199     }
200     
201     vector<int>().swap(pack);
202     vector<int>().swap(tank);
203     return holder;
204 }
205 
206 
207 /**
208     语句解释:
209         1、tmp.at(rnd-min)==-1 // vector的一个函数,查得的资料如下,
210         Access element.Returns a reference to the element at position n in the vector.
211         The function automatically checks whether n is within the bounds of valid elements in the vector,
212         throwing an out_of_range exception if it is not (i.e., if n is greater than, or equal to, its size). 
213         This is in contrast with member operator[], that does not check against bounds.  
214         (中译)
215         访问元素。返回对向量中位置n处元素的引用。
216         该函数自动检查n是否在向量中有效元素的范围内,如果n不在范围外,
217         则抛出一个异常(即,如果n大于或等于其大小)。这与不检查边界的成员运算符[]形成对比。 
218         这便是一个使数组得到不重复数据的关键点。 
219         2、 int min, max, num;//在[min,max]取num个数。 
220 **/                    
randomnumber.h

 笔记与代码

 (代码主要为Java和C++)

 

 1                 import java.util.Random;
 2                 public class wei{
 3                     public static void main(String[] args) {
 4                         int N = 1001;
 5                         int[] arr = new int[N];
 6                         for (int i = 0; i < arr.length; i++) {
 7                             arr[i] = i + 1;
 8                         }
 9                         int index = new Random().nextInt(N);
10                         arr[arr.length - 1] = arr[index];
11                         arr[index] = new Random().nextInt(N - 1) + 1;
12                         int x = 0;
13                         for (int i = 1; i < N; i++) {
14                             x ^= i;
15                         }
16                         for (int i = 0; i < N; i++) {
17                             x ^= arr[i];
18                             System.out.printf("%d  ", arr[i]);
19                         }
20                         System.out.println();
21                         System.out.println(x);
22                         System.out.println("==========");
23                             int[] helper = new int[N];
24                             for (int i = 0; i < N; i++) {
25                                   helper[arr[i]]++;
26                             }
27                             for (int i = 0; i < N; i++) {
28                                   if (helper[i] == 2) {
29                                         System.out.println(i);
30                                         break;
31                                   }
32                             }
33                     }
34                 }
_01找出唯一成对的数
 1 /**
 2     题目数据只有一个重复,其他数据唯一 。
 3     思路1:辅助空间计数法,开辟一个长度为1000的数组 ,扫描题目数组,并计数。
 4     伪代码:
 5             int helper[1001];
 6             memset(helper, 0, sizeof(helper));//全0数组 
 7             for(int i from 1 to 1001) 
 8                 helper[array[i]]++;
 9             for(int j from 1 to 1000) 
10                 if(helper[j] == 2)
11                     cout<< j;
12                     break;
13     时间复杂度为:O(n),但是需要开辟辅助空间
14     思路2:由异或的自反性 A^B^B=A^0=A可设计一个时间复杂度为O(n),且在原址上操作的算法
15     伪代码:
16             int x = 0 ;
17             for(int i from 1 to 1000) 
18                 x ^= i;
19             for(int j from 0 to 1000) 
20                 x ^= array[j]
21             cout<< x;
22     测试设计,设计一个能生成指定范围内的乱序数据的头文件 
23 **/
24 #include "randomnumber.h"//调用生成[a,b]内不重复n个数的头文件 
25 #define N 1000 
26 using namespace std;
27 
28 int main()
29 {
30     int x = 0, i, j;
31     vector<int> array(N+1);
32     array = GenerateDiffNumber(1,N,N);
33     array[N] = rand()%N;//产生一个重复的数 
34     for(i=1; i<=N; i++)
35         x ^= i; 
36     /*测试可用N=10测方便观察 
37     for(i=0; i<N; i++)
38         cout<< array[i] <<' ';
39     cout<< endl;
40     */
41     for(j=0; j<N+1; j++)
42         x ^= array[j];
43     cout<< x; 
44     return 0;
45 }
exp_01

 1 /**
 2     题2:找出落单的那个数。一个数组里除了某一个数字之外,其他的数字出现了
 3     两次。请写程序找出这个出现一次的数字。
 4      
 5     思路1:辅助空间,数组的长度取决于最大值,浪费空间。
 6         伪代码: 
 7                 int maxsize = array[0];
 8                 for(i from 1 to n)
 9                     maxsize = maxsize > array[i] ? maxsize : array[i];
10                 int helper[maxsize];
11                 memset(helper, 0, sizeof(helper));
12                 for(int i from 1 to n) 
13                 helper[array[i]]++;
14                 for(int j from 1 to n) 
15                     if(helper[j] == 1)
16                         cout<< j;
17                         break;
18                 
19     思路2:冒泡查找,最坏情况为O(n^2)
20         伪代码:
21                 int index;
22                 bool flag;
23                 for(i from 0 to n)
24                     index = array[i];
25                     flag = true;
26                     for(j from i to n)
27                          if(index == array[j])
28                              flag = flase;
29                              break;
30                     if(flag)
31                         cout<< index;
32                         break;         
33     思路3:位运算的异或, A^B^B=A^0=A
34         伪代码:
35                 int x = 0;
36                 for(i from 0 to n)
37                     x ^= array[i];
38                 cout<< x; 
39     
40     测试设计,设计一个头文件得到一组如题示的数据。 
41 **/
42 
43 #include "randomnumber.h"
44 #define N 23 
45 using namespace std;
46 int main()
47 {
48     int x = 0, i, j;
49     vector<int> array(N);
50     array = Repeatedrandomnumber(1,100,N);
51     
52     for(i=0; i<N; i++)
53         cout<< array[i] <<' ';
54     cout<< endl;
55     
56     for(j=0; j<N; j++)
57         x ^= array[j];
58     cout<< x; 
59     return 0;    
60 } 
_02找出落单的那个数

 

 1                         import java.util.Scanner;
 2                         public class num1 {
 3                           public static void main(String[] args) {
 4                             Scanner sc = new Scanner(System.in);
 5                             int N = sc.nextInt();
 6                             System.out.println(Integer.toString(N, 2));
 7                             int count = 0;
 8                             for (int i = 0; i < 32; i++) {
 9                               if ((N & (1 << i)) == (1 << i)) {
10                                 count++;
11                               }
12                             }
13                             System.out.println(count);
14                             count = 0;
15                             for (int i = 0; i < 32; i++) {
16                               if (((N >>> i) & 1) == 1)
17                                 count++;
18                             }
19                             System.out.println(count);
20                             count = 0;
21                             while (N != 0) {
22                               N = ((N - 1) & N);
23                               count++;
24                             }
25                             System.out.println(count);
26                           }
27                         }
_03二进制中1的个数
 1 /**
 2     题3:二进制中1的个数
 3     请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。
 4     例:9的二进制表示1001,有2位是1。
 5     
 6     思路1:位运算运用,1&1=1,1&0=0,0&0=0,0&1=0。搭配 <<。
 7          伪代码:
 8                  int count = 0;
 9                  for(i from 0 to 32)
10                      if((N&(1<<i)) == (1<<i))
11                          count++; 
12                          
13     思路2:位运算应用,使用>>>运算符用0填充最高位,使原有的数据一个一个地退到低位,再与1做判断。
14         伪代码:
15                 int count = 0;
16                  for(i from 0 to 32)
17                      if((N>>>i)&1) == 1)
18                          count++; 
19                          
20     思路3:位运算应用,n-1与n使得二者的1错位相与便能消掉该错位的1,直至把n的1全部去掉。
21          伪代码:
22                  int count = 0;
23                  while(N!=0){
24                      N = ((N-1)&N);
25                      count++;
26                 } 
27     
28     测试设计:不妨设计一个进制转换头文件。 
29 **/
30 #include "randomnumber.h"
31 int main()
32 {
33     int N = random_3();
34     int count = 0;
35     vector<int> array;
36     cout<< N <<endl;
37     
38     array = D_B(N);
39     while(N!=0){
40          N = ((N-1)&N);    
41          count++;
42     } 
43     
44     for(int i=0; i<32; i++)
45         cout<< array[i];
46     cout<< endl;
47     cout<< count;
48     
49     return 0;
50 }
exp_03

 1 /**
 2     题4:是不是2的整数次方。
 3     用一条语句判断一个整数是不是2的整数次方。
 4      
 5     思路1:因为2的整数次幂的二进制数只有一个1,所以(N-1)&N就可以置0 。
 6         伪代码:
 7                 if(((N-1)&N) == 0) is an integral multiple of 2.
 8                 else isn't an integral multiple of 2.
 9     
10     测试设计:得到一个随机数。 
11 **/
12 #include "randomnumber.h"
13 int main()
14 {
15     int N = random_3();
16     cout<< N;
17     if(((N-1)&N) == 0)
18         cout<< " yes";
19     else
20         cout<< " no"; 
21     return 0; 
22 } 
_04是不是2的整数倍

 

 1                 import java.util.Scanner;
 2                 public class ji {
 3                     public static void main(String[] args) {
 4                         Scanner scanner=new Scanner(System.in);
 5                         int Number=scanner.nextInt();
 6                         int ou=Number & 0xaaaaaaaa;  
 7                         int ji=Number & 0x55555555; 
 8                         int result=(ou>>1) ^ (ji<<1);  
 9                         System.out.println("交换前:"+Integer.toString(Number, 2));
10                         System.out.println("交换后:"+Integer.toString(result, 2));
11                     }
12                 
13                 }
_05将整数的奇偶位互换
 1 /**
 2     题目:将整数的奇偶位互换
 3     思路1:时间复杂度为O(1)的方法是应用位运算的知识,101010...10&N得到奇数位的值,010101...01得到偶数位的值,再用异或运算把二者拼在一起。
 4     (0xaaaaaaaa为十六进制表示32位的二进制数,0x十六进制标示,每4位二进制数表示一位十六进制数,a表示1010)
 5     
 6     伪代码 :
 7             int ou = N&0xaaaaaaaa;
 8             int ji = N&0x55555555;
 9             int result = (ou>>1) ^ (ji<<1);//奇偶位互换
10     
11     测试设计:得到随机数与二进制数。 
12 **/
13 #include "randomnumber.h"
14 int main()
15 {
16     int N = random_3();
17     vector<int> array1;
18     vector<int> array2;
19     
20     array1 = D_B(N);
21     for(int i=0; i<32; i++)
22         cout<< array1[i];
23     cout<< endl;
24     int ou = N&0xaaaaaaaa;
25     int ji = N&0x55555555;
26     int result = (ou>>1) ^ (ji<<1);
27     array2 = D_B(result);
28     
29     for(int i=0; i<32; i++)
30         cout<< array2[i];
31     
32     return 0;
33 }
exp_04

 1                     public class xiaoshu {
 2                       public static void main(String[] args) {
 3                         double num = 0.625;
 4                         StringBuilder sb = new StringBuilder("0.");
 5                         while (num > 0) {
 6                           double r = num * 2;
 7                           if (r >= 1) {
 8                             sb.append("1");
 9                             num = r - 1;
10                           } else {
11                             sb.append("0");
12                             num = r;
13                           }
14                           if (sb.length() > 34) {
15                             System.out.println("ERROR");
16                             return;
17                           }
18                         }
19                         System.out.println(sb.toString());
20                       }
21                     }
_060~1间浮点实数的二进制表示
 1 /**
 2     题6:0~1间浮点实数的二进制表示。给定一个介于0和1之间的实数,(如0.625),类型为double,打印
 3     它的二进制表示(0.101,因为小数点后的二进制分别表示0.5,0.25,0.123...)。如果该数字无法精确
 4     地用32位以内的二进制表示,则打印"ERROR"
 5     思路1:当小于1大于0时,r = N*2得到0则第一位为0则记忆该数且N=r,否则则记忆该数为1且N=r-1,依
 6     次类推,直到N==0。得到的数组长度长于32则输出"ERROR"。
 7     
 8     伪代码:
 9             string sb = "0.";
10              while(N>0)
11                  double r = N*2;
12                  if(r>=1) sb.append("1"); N = r-1;
13                  else sb.append("0"); N = r;
14              if(sb.length()>34) cout<< "ERROR";
15              else cout<< sb;
16     
17     测试设计:得到0~1的随机数。        
18 **/
19 #include<cstring>
20 #include "randomnumber.h"
21 int main()
22 {
23     string sb = "0.";
24     double r, N = decimal();
25     
26     while(N>0){
27         r = N*2;
28          if(r>=1) {
29              sb.append("1");
30             N = r-1;
31         }
32          else{
33              sb.append("0"); 
34             N = r;    
35         } 
36     }
37     
38     if(sb.length()>34) 
39         cout<< "ERROR";
40     else 
41         cout<< sb;
42     return 0;    
43 } 
exp_06

 1                 public class _07_出现K次 {
 2                   public static void main(String[] args) {
 3                     int[] arr = {2, 2, 2, 9, 7, 7, 7, 3, 3, 3, 6, 6, 6, 0, 0, 0};
 4                     int len = arr.length;
 5                     char[][] kRadix = new char[len][];
 6                     int k = 3;
 7                 
 8                     int maxLen = 0;
 9                     //转成k进制字符数组
10                     //对于每个数字
11                     for (int i = 0; i < len; i++) {
12                       //求每个数字的三进制字符串并翻转,然后转为字符数组
13                       kRadix[i] = new StringBuilder(Integer.toString(arr[i], k)).reverse().toString().toCharArray();
14                       if (kRadix[i].length > maxLen)
15                         maxLen = kRadix[i].length;
16                     }
17                     //不进位加法
18                     int[] resArr = new int[maxLen];
19                     for (int i = 0; i < len; i++) {
20                       //  不进位加法
21                       for (int j = 0; j < maxLen; j++) {
22                         if (j >= kRadix[i].length)
23                           resArr[j] += 0;
24                         else
25                           resArr[j] += (kRadix[i][j] - '0');
26                       }
27                     }
28                 
29                     int res = 0;
30                     for (int i = 0; i < maxLen; i++) {
31                       res += (resArr[i] % k) * (int) (Math.pow(k, i));// 8%3=2,
32                     }
33                     System.out.println(res);
34                   }
35                 }
_07出现k次与出现1次
 1 /**
 2     题7:出现k次与出现1次。数组中只有一个数出现了1次,其他的数都出现了k次,
 3     请输出只出现了1次的数。
 4     
 5     思路: k个相同的k进制数做不进位加法,结果为0(可以用模运算进行解释)
 6         伪代码:
 7                 //转成k进制字符数组并翻转 
 8                 int maxlen=0;
 9                 for(i from 0 to array.length())
10                     kRadix[i] =  tocharArray(array[i]);
11                     if(kRadix[i].length() > maxlen) 
12                         maxlen = kRadix[i].length();
13                 //不进位加法
14                 int resArr[maxlen];
15                 for(i from 0 to array.length()) 
16                     for(j from 0 to maxlen)
17                         if(j>=kRadix[i].length())
18                             resArr[j] += 0;
19                         else
20                             resArr[j] += (kRadix[i][j] - '0');
21                 int res = 0;
22                 for(i from 0 to maxlen)
23                     res += (resArr[i]%k)*(int)(pow(k,i));
24     
25     测试设计:设法得到题目所描述的数组,并设计k进制数的互换与字符串的翻转。 
26 **/
27 #include "randomnumber.h"
28 #include<cmath>
29 //int array[] = {2, 2, 2, 39, 7, 7, 7, 3, 3, 3, 6, 6, 6, 0, 0, 0};//例子 
30 int k;
31 vector<int> array;
32 void init()
33 {
34     int res;
35     cin>> k;
36     array = krepetition(k);
37     for(int i=0; i<array.size(); i++){
38         cout<< array[i] << ' ';
39     }
40     cout<< endl;
41 }
42 
43 int main()
44 {
45     int maxlen=0, i, j, len;
46     init();
47     len = array.size();
48     
49     string kRadix[len];
50     
51     for(i=0; i<len; i++){
52         kRadix[i] =  radixto(array[i], k);//求每个数字的三进制字符串并翻转,然后转为字符数组,默认k为3
53         if(kRadix[i].length() > maxlen) 
54             maxlen = kRadix[i].length();
55     }
56 
57     int resArr[maxlen];
58     memset(resArr, 0, sizeof(resArr));//全0数组 
59     for(i=0; i<len; i++) {
60         for(j=0; j<maxlen; j++)
61             if(j>=kRadix[i].length())
62                 resArr[j] += 0;
63             else
64                 resArr[j] += (kRadix[i][j]- '0');
65     }
66     
67     int res = 0;
68     for(i=0; i<maxlen; i++)
69         res += (resArr[i]%k)*(int)(pow(k,i));
70     cout<< res;
71     vector<int>().swap(array);
72     return 0;
73 } 
exp_07
原文地址:https://www.cnblogs.com/jianle23/p/12708953.html