位运算在排序算法中的运用

Posted mikumikugo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了位运算在排序算法中的运用相关的知识,希望对你有一定的参考价值。

常规选择排序

function selectSort(arr: Number[]) 
    //先排除一些不需要排序的情况
    if (!arr || arr.length < 2) 
        return arr
    
    let a =arr
    //外层循环控制循环n-1次
    for (let i = 0; i < a.length-1; i++) 
        let index = i
        //内层循环获取该轮循环中最小值的下标
        for (let j = i + 1; j < a.length; j++) 
            if (a[j] < a[index]) 
                index = j
            

        
        if(i!==index)
            let temp = a[i]
            a[i]=a[index]
            a[index]=temp
        
    


    return a




使用位运算的选择排序

function selectSortUseByte(arr) 
    if (!arr || arr.length < 2) 
        return arr
    

    for (let i = 0; i < arr.length - 1; i++) 
        for (let j = i; j < arr.length; j++) 
            if (arr[j] < arr[i]) 
                swap(arr, i, j)
            
        
    
    //交换值时使用位运算
    function swap(arr, a, b) 
        arr[a] = arr[a] ^ arr[b]
        arr[b] = arr[a] ^ arr[b]
        arr[a] = arr[a] ^ arr[b]
    
    return arr

异或是如何实现值交换的

异或的性质

  • 满足交换律和结合律

ab=ba

abc=a(bc)

a^a=0

0^a=a

function swap(arr, a, b) 
        arr[a] = arr[a] ^ arr[b]
        //此时arr[a]=arr[a]^arr[b],执行下面的运算后,arr[b]=arr[a]^arr[b]^arr[b]=arr[a]^0=arr[a]
        arr[b] = arr[a] ^ arr[b]
        //执行下面的运算,arr[a]=(arr[a]^arr[b])^arr[a]=arr[b]
        arr[a] = arr[a] ^ arr[b]
        //这样就巧妙地将两个值进行了交换,且没有开辟新的存储空间
    

拓展

找出唯一的出现奇数次的数

现有N个数,除了唯一的一个数出现的次数是奇数,其他的均是出现了偶数次的数,现在请编程找出这个出现奇数次的数

/**
* 
* @param arr 要处理的数组
* @returns 返回出现奇数次的数
*/

function getOddNubmer(arr)
   let r=0
   //挨个遍历数组里面的数进行异或操作,出现偶数次的数最终会被异或成0,最后剩下的就是出现偶数次的数
   for(let k of arr)
       r^=k
   
   return r

找出数组中出现奇数次的两个数

N个数,其中除了两个数出现奇数次,其他数都出现了奇数次,现在找出这两个数

function getOddNumberTwo(arr) 
    let r = 0
    //假设这两个数是a和b,此处获取a^b
    for (let k of arr) 
        r ^= k
    
    //获取a和b中为1的最低位
    let rightone = r & (~r + 1)
    let first = 0
    for (let k of arr) 
        //筛选出满足rightone的数据,其中将只包含a和b其中一个,进行异或操作后就可得到其中一个数
        if ((k & rightone) == 0) 
            first ^= k
        
    
    //将a和b异或的和再与第一个数进行异或运算,就得到了第二个数
    let second = r^first

    return [first,second]

[位运算]签到题

题目描述

作为一道签到题,自然只能包含最基本的算法。本题的任务很简单,给定一个长度为n的序列a,你要将其排序。
由于出题人很菜,不会排序算法,他决定自己编一个。他想找到一个数x,使得序列中的所有数字都异或上x后序列恰好按从小到大排列。
顺带,这个序列会被进行若干次修改,每次修改后你需要回答当前是否存在一个x满足序列中数字异或上x后按从小到大排列,如果有,请你给出最小的x。

 

输入

第一行一个正整数n。
第二行n个非负整数,表示序列a。
第三行一个非负整数q,表示修改次数。
接下来q行,每行一个正整数x和一个非负整数y,表示将序列中第x个元素修改为y

 

输出

输出q+1行,每行一个整数,第一行表示一开始最小的合法x,之后q行依次表示每次修改后最小的合法x,如果不存在则这一行输出−1。

 

样例输入

3
0 1 4
3
2 7
3 3
1 4

样例输出

0
2
-1
4

 

提示

对于100%的数据,n,m≤106,所有数字不超过230。
请注意输入输出效率对程序运行时间的影响。

两个数的大小差异在于其二者二进制位相异的最高一位,若在这一位异或1,两数的大小关系能得到反转,在除这位外的任何一位异或0/1,大小关系不会改变
技术图片
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
 
int n,a[1000005];
int cnt[35][5];
 
void solve(){
  int ret=0;
  for(int i=30;i>=0;--i){
    if(cnt[i][1]&&cnt[i][0]){
        printf("-1
");
        return;
    }
    if(cnt[i][1]){
        ret|=(1<<i);
    }
  }
  printf("%d
",ret);
  return;
}
 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
 
    for(int i=2;i<=n;++i){
        for(int j=30;j>=0;--j){
            if((a[i]^a[i-1])&(1<<j)){
                cnt[j][a[i-1]>a[i]]++;
                break;
            }
        }
    }
 
    solve();
 
    int q;scanf("%d",&q);
    int x,y;
    while(q--){
        scanf("%d%d",&x,&y);
        if(x>1){
            for(int i=30;i>=0;--i){
                if((a[x]^a[x-1])&(1<<i)){
                    cnt[i][a[x-1]>a[x]]--;
                    break;
                }
            }
            for(int i=30;i>=0;--i){
                if((y^a[x-1])&(1<<i)){
                    cnt[i][a[x-1]>y]++;
                    break;
                }
            }
        }
        if(x<n){
            for(int i=30;i>=0;--i){
                if((a[x]^a[x+1])&(1<<i)){
                    cnt[i][a[x]>a[x+1]]--;
                    break;
                }
            }
            for(int i=30;i>=0;--i){
                if((y^a[x+1])&(1<<i)){
                    cnt[i][y>a[x+1]]++;
                    break;
                }
            }
        }
        a[x]=y;
        solve();
    }
    return 0;
}
View Code

 

以上是关于位运算在排序算法中的运用的主要内容,如果未能解决你的问题,请参考以下文章

通过位操作实现四则运算

数字位运算操作与算法简单示例

如何优雅地运用位运算实现产品需求?

[位运算]签到题

AcWing算法基础课排序 二分 高精度 前缀和 差分 双指针 位运算 离散化 区间合并

二进制-高效位运算