通过指令创建有序数组

通过指令创建有序数组

给你一个整数数组 instructions ,你需要根据 instructions 中的元素创建一个有序数组。一开始你有一个空的数组 nums ,你需要 从左到右 遍历 instructions 中的元素,将它们依次插入 nums 数组中。每一次插入操作的 代价 是以下两者的 较小值 :

nums 中 严格小于  instructions[i] 的数字数目。
nums 中 严格大于  instructions[i] 的数字数目。

比方说,如果要将 3 插入到 nums = [1,2,3,5] ,那么插入操作的 代价 为 min(2, 1) (元素 1 和 2 小于 3 ,元素 5 大于 3 ),插入后 nums 变成 [1,2,3,3,5] 。

请你返回将 instructions 中所有元素依次插入 nums 后的 总最小代价 。由于答案会很大,请将它对 109 + 7 取余 后返回。

示例 1:

输入:instructions = [1,5,6,2]
输出:1
解释:一开始 nums = [] 。
插入 1 ,代价为 min(0, 0) = 0 ,现在 nums = [1] 。
插入 5 ,代价为 min(1, 0) = 0 ,现在 nums = [1,5] 。
插入 6 ,代价为 min(2, 0) = 0 ,现在 nums = [1,5,6] 。
插入 2 ,代价为 min(1, 2) = 1 ,现在 nums = [1,2,5,6] 。
总代价为 0 + 0 + 0 + 1 = 1 。

示例 2:

输入:instructions = [1,2,3,6,5,4]
输出:3
解释:一开始 nums = [] 。
插入 1 ,代价为 min(0, 0) = 0 ,现在 nums = [1] 。
插入 2 ,代价为 min(1, 0) = 0 ,现在 nums = [1,2] 。
插入 3 ,代价为 min(2, 0) = 0 ,现在 nums = [1,2,3] 。
插入 6 ,代价为 min(3, 0) = 0 ,现在 nums = [1,2,3,6] 。
插入 5 ,代价为 min(3, 1) = 1 ,现在 nums = [1,2,3,5,6] 。
插入 4 ,代价为 min(3, 2) = 2 ,现在 nums = [1,2,3,4,5,6] 。
总代价为 0 + 0 + 0 + 0 + 1 + 2 = 3 。

示例 3:

输入:instructions = [1,3,3,3,2,4,2,1,2]
输出:4
解释:一开始 nums = [] 。
插入 1 ,代价为 min(0, 0) = 0 ,现在 nums = [1] 。
插入 3 ,代价为 min(1, 0) = 0 ,现在 nums = [1,3] 。
插入 3 ,代价为 min(1, 0) = 0 ,现在 nums = [1,3,3] 。
插入 3 ,代价为 min(1, 0) = 0 ,现在 nums = [1,3,3,3] 。
插入 2 ,代价为 min(1, 3) = 1 ,现在 nums = [1,2,3,3,3] 。
插入 4 ,代价为 min(5, 0) = 0 ,现在 nums = [1,2,3,3,3,4] 。
​​​​​插入 2 ,代价为 min(1, 4) = 1 ,现在 nums = [1,2,2,3,3,3,4] 。
插入 1 ,代价为 min(0, 6) = 0 ,现在 nums = [1,1,2,2,3,3,3,4] 。
插入 2 ,代价为 min(2, 4) = 2 ,现在 nums = [1,1,2,2,2,3,3,3,4] 。
总代价为 0 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 2 = 4 。

int min(int num,int *n,int val){
    int i=0;
    int sam=0;
    for(i;i<num;i++){
        if(val<n[i]){
            break;
        }
    }
   
    return i;
   
}

int createSortedArray(int* instructions, int instructionsSize){
    int i;
    int sum=0;
    int n[instructionsSize];
    int j;
    int mini;
    int max;
    int r=0;
    int pr;
     int rn[instructionsSize];
    for(i=0;i<instructionsSize;i++){
        if(i==0){
            
            n[r]=instructions[r];
            rn[r++]=1;

        }
        else{

       //    index_min=min(i,n,instructions[i]);
           mini=0;
           max=0;
           pr=0;
           for(j=0;j<r;j++){
               if(instructions[i]==n[j]){
                   rn[j]++;
                   pr=1;
                   
               }

               if(instructions[i]>n[j]){
                   max=max+rn[j];
               }
                if(instructions[i]<n[j]){
                   mini=mini+rn[j];
               }

           }
           if(pr==0){
               printf("r %d ",r);
              n[r]=instructions[i];
             rn[r++]=1;

           }
       //      printf("min %d max %d ",mini,max);
           if(max>mini){
               sum=sum+mini;
           }
           else sum=sum+max;
         
         
       }
    }
    return sum;


}

当然,上面算法时间复杂度肯定是解决不了的,所以我们再给一个解法
instructions数组中的数值,是从左到右的顺序安插到目标数组中的。所以,当执行到instructions[x]时,前面已有的比自己小,与比自己大的数值,全都在[0, x - 1]之间,与[x + 1, instructionsSize)之间的数值无关。
那么,题目的含义就转变成了:求每一个instructions[x]在数组中,它所在位置的左侧,有多少小于自己的数值,和大于自己的数值,求两者数量的较小值,然后,所有instructions[x]的这个最小值的总和,就是结果。
接下来,题目就和另一题《计算右侧小于当前元素的个数》本质上是一样的了。只不过本题是把右侧改为左侧,并且同时计算小于当前元素和大于当前元素的个数。
即设立一棵二叉树,每个instructions[x]存放入这棵二叉树的原则是:从它的二进制最高位到最低位,依次查看每一位是1还是0,如果是1,则往二叉树右侧走,如果是0,则往二叉树左侧走,记录每个分支往左和往右的次数。同时,每次往二叉树中插入instructions[x]时,都顺便检查一下有多少数值是和自己在当前节点分道扬镳的。比如,当前位是1,那么它往右走的同时,已经在当前节点往左走的数值,肯定都小于自己,计数。当前位是0,那么它往左走的同时,已经在当前节点往右走的数值,肯定都大于自己,计数。当它执行到最后一位时,自己存入了二叉树,同时也将小于自己和大于自己的数值数量给计算出来了。
instructions[x]数值的取值范围是[1, 10^5],所以最高位是第20位,即,不用遍历整个int类型的32位。

这种解法类似哈夫曼树上进行求解,当然,这个解法是相当棒的
代码如下:

#define MAX_BITS 0x20000
#define MODULE_NUMBER 1000000007
#define MIN_VAL(a, b) (((a) < (b)) ? (a) : (b))

int createSortedArray(int *instructions, int instructionsSize)
{
    int x = 0, bits = 0, bigger = 0, smaller = 0, result = 0;
    struct TreeNode *root = NULL, *node = NULL;

    /* 二叉树的根节点默认存在,其中root->val的值不重要。 */
    root = (struct TreeNode *)malloc(sizeof(struct TreeNode));
    root->left = NULL;
    root->right = NULL;

    while(instructionsSize > x)
    {
        /* 大于自己的数,和小于自己的数,数量分别用bigger和smaller表示,然后从高位往低位开始执行。 */
        bigger = 0;
        smaller = 0;
        node = root;
        bits = MAX_BITS;
        while(0 != bits)
        {
            /* 当前位是1。 */
            if(instructions[x] & bits)
            {
                /* 当前位是0的一定小于自己。 */
                if(NULL != node->left)
                {
                    smaller += node->left->val;
                }
                /* 往右分支走,并计数加一。如果右分支为空,先创建右分支。 */
                if(NULL == node->right)
                {
                    node->right = (struct TreeNode *)malloc(sizeof(struct TreeNode));
                    node->right->val = 1;
                    node->right->left = NULL;
                    node->right->right = NULL;
                }
                else
                {
                    node->right->val++;
                }
                node = node->right;
            }
            /* 当前位是0。 */
            else
            {
                /* 当前位是1的一定大于自己。 */
                if(NULL != node->right)
                {
                    bigger += node->right->val;
                }
                /* 往左分支走,并计数加一。如果左分支为空,先创建左分支。 */
                if(NULL == node->left)
                {
                    node->left = (struct TreeNode *)malloc(sizeof(struct TreeNode));
                    node->left->val = 1;
                    node->left->left = NULL;
                    node->left->right = NULL;
                }
                else
                {
                    node->left->val++;
                }
                node = node->left;
            }
            bits = bits >> 1;
        }
        /* 把bigger和smaller的较小值加入到结果中,并根据题意,对1e9 + 7取模。 */
        result += MIN_VAL(bigger, smaller);
        if(MODULE_NUMBER <= result)
        {
            result -= MODULE_NUMBER;
        }
        x++;
    }

    return result;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值