算法数据结构中有哪些奇技淫巧

最近刷知乎发现了一个很有意思的话题——算法数据结构中有哪些奇技淫巧,来总结一下,学到不少。(*^▽^*)

 

1、找出只出现一次的数

在一个数组中有一个数只出现了一次,其他数都出现了两次,找出这个只出现了一次的数。

常规解法:用一个哈希表存储该数组,每次存储的时候,记录 某个数出现的次数,最后再遍历哈希表,看看哪个数只出现了一次。这种方法的时间复杂度为 O(n),空间复杂度也为 O(n)。

奇技淫巧:用位运算来做,一个数和 0 异或的结果是它本身,所以我们把这一个数组全部异或一下,异或支持交换律和结合律。也就是说,那些出现了两次的数异或之后会变成0,那个出现一次的数,和 0 异或之后就等于它本身。

int find(int[] arr){
    int tmp = arr[0];
    for(int i = 1;i < arr.length; i++){
        tmp = tmp ^ arr[i];
    }
    return tmp;
}

时间复杂度为 O(n),空间复杂度为 O(1),而且看起来很牛逼。

在一个数组中有2个数只出现了一次,其他数都出现了两次,找出这2个只出现了一次的数。

 

 

2、m的n次方

不用pow求m的n次方。

例如 n = 13,则 n 的二进制表示为 1101, 那么 m 的 13 次方可以拆解为:

m^1101 = m^0001 * m^0100 * m^1000

我们可以通过 & 1和 >>1 来逐位读取 1101,为1时将该位代表的乘数累乘到最终结果。直接看代码吧,反而容易理解:

int pow(int n){
    int sum = 1;
    int tmp = m;
    while(n != 0){
        if(n & 1 == 1){
            sum *= tmp;
        }
        tmp *= tmp;
        n = n >> 1;
    }

    return sum;
}

时间复杂度近为 O(logn)。(个人感觉这个位运算在实际中可读性太差)

 

3、找出第k大的数

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

常规解法:sort排序,取第n-k个值。

奇思淫巧:在不排序的情况下,复杂度为O(n)。堆排序是一种树形选择排序方法。堆排序主要分为(1)如何建立堆(2)如何调整堆。我们可以建立最小根堆,用数组的前k个数,建立最小根堆。然后从第k+1开始,和堆顶比较,如果比堆顶大,就交换,交换完重新维护成最小根堆,直到结束。这样堆里存放的就是数组中前k个最大的元素,而堆顶就是这里面最小的,也就是第k大的元素。

void HeapAdjust(vector<int>& nums,int s,int length)//调整堆的程序
{
    int temp=nums[s];
    int child = 2*s+1;//这里因为下标从0开始,每个节点的子节点就是2s+1
    while(child<length)
    {
        if(child+1<length && nums[child]>nums[child+1])
            child++;
        if(nums[s]>nums[child])
        {
            nums[s]=nums[child];
            s=child;
            child=2*s+1;
        }
        else 
            break;
        nums[s]=temp;//这里的s已经是原来的child了
    }
}
void BuildingHeap(vector<int>& nums,int k)//建立堆,从最后开始筛选
{
    for(int i=(k-1)/2;i>=0;i--)
        HeapAdjust(nums,i,k);
}
int findKthLargest(vector<int>& nums, int k)
{
    int size=nums.size();
    if(k>size) return nums[size-1];
    vector<int> mynum;
    int index=0;
    for(;index<k;index++)
    {
        mynum.push_back(nums[index]);
    }
    BuildingHeap(mynum,index);
    int j=index;
    for(int i=j;i<size;i++)
    {
        if(nums[i]>mynum[0])
        {
            swap(nums[i],mynum[0]);
            HeapAdjust(mynum,0,index);
        }
    }
    return mynum[0];
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值