zoukankan      html  css  js  c++  java
  • 阿里巴巴面试题|完美洗牌问题

    题意:给定一个降序的正数数组,要求按【最小、最大、次小、次大…】的顺序重新排序。期望的时间复杂度为O(n),空间复杂度为O(1),即不能申请额外数组。例如:输入【7,6,5,4,3,2,1】输出【1,7,2,6,3,5,4】

    分析:

    首先,计算每个元素要挪到哪里感觉很简单,目测是这样:
    1. 旧位置i < 2/n:新位置j=2 * i +1
    2. 旧位置i >= 2/n: 新位置j = 2 * (n - 1 - i)

    然后就是沿链轮换了。本例中 a[0]=7应该被移到a[1], a[1]本来的数6应该被移到a[3], a[3]本来的数4应该被移到a[6]... 最后发现,把input数组变成output数组, 需要三组轮换:
    1. a[0]->a[1]->a[3]->a[6]->a[0]
    2. a[2]->a[5]-a[2]
    3. a[4]位置不变
    完成每个轮换只需要一个额外的变量,腾出一个数组位置。因此空间复杂度O(1)。每个元素只会被挪动一次,时间复杂度O(n)。
    此外还需要标记哪些元素已经被移动过了。通常另开一个数组标记会比较方便。。但是这题不允许,所以可以把每个移动过的元素都都取负作为标记,之后再还原。(也可以用其他的标记方式)

    这也对应一个群论中的结论:置换可以分解成若干个不相交的轮换。

    代码

    #include <stdio.h>
    
    int trans(int len, int i) {
        if(len % 2 == 0) {
            if(i < len / 2) return i * 2 + 1;
            else return (len - i - 1) * 2;
        } else {
            if(i < len / 2) return i * 2 + 1;
            else if(i > len / 2) return (len - i - 1) * 2;
            else return len - 1;
        }
    }
    
    void swap(int *a, int *b) {
        *a ^= *b;
        *b ^= *a;
        *a ^= *b;
    }
    
    int main(void) {
        int arr[] = {7, 6, 5, 4, 3, 2, 1};
    
        //start
        int len = sizeof(arr) / sizeof(*arr);
        for(int i = 0; i < len; i++) {
            if(arr[i] > 0) {
                int j = i, tmp = arr[i];
                while(arr[j = trans(len, j)] > 0) {
                    swap(&tmp, &arr[j]);    // 每次与前一个交换
                    arr[j] = 0 - arr[j];
                }
            }
        }
        for(int i = 0; i < len; i++) {
            arr[i] = 0 - arr[i];
        }
    
        //print
        for(int i = 0; i < len; i++) {
            printf("%d ", arr[i]);
        }
    }

    这个问题其实是一个经典问题,

    完美洗牌问题:

    玩过扑克牌的朋友都知道,在一局完了之后洗牌,洗牌人会习惯性的把整副牌大致分为两半,两手各拿一半对着对着交叉洗牌。

    2004年,microsoft 的 Peiyush Jain 在他发表一篇名为:“A Simple In-Place Algorithm for In-Shuffle” 的论文中提出了完美洗牌算法。

    什么是完美洗牌问题呢?即给定一个数组

    a1,a2,a3, …, an, b1, b2, b3, ..., bn

    最终把它置换成

    b1, a1, b2, a2, a3, b3,…, bn, an

    这个完美洗牌问题本质上与本题完全一致的,可以在O(n)内改成形式相同。



    参考链接:

    1. https://www.zhihu.com/question/50512830/answer/121386043

    2. https://www.zhihu.com/question/50512830/answer/121334031

    3. https://www.jianshu.com/p/9c841ad88ded

  • 相关阅读:
    git pull origin master命令后发生冲突
    计算属性和侦听器
    微信小程序 wx.navigateBack携带参数
    小程序----路由
    quill工具栏出现提示的功能
    quill修改字体大小
    quill报错,"RangeError: Maximum call stack size exceeded"
    quill添加字体
    新建一个vue项目(补充了vue-cli4.0快速搭建一个项目【站在巨人的肩膀上并亲测】)
    codeforce round 7
  • 原文地址:https://www.cnblogs.com/lfri/p/12550234.html
Copyright © 2011-2022 走看看