算法分析与设计课程02——315. Count of Smaller Numbers After Self (Hard难度)

一、题目描述(中文)

————————————原题链接
任意给一个数组如[5, 2, 6, 1],你需要找出数组中每个数的特征,并返回一个特征数组

特征:这个数后面比这个数小的数 的总和,如第一个数字5,在5后面比5还小的数有2,1**两个数**,所以返回数组第一位是2

所以上面样例数组返回为:[2, 1, 1, 0],见下面具体描述

Given nums = [5, 2, 6, 1]

To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.

二、题目分析

以上面样例进行分析[5, 2, 6, 1]
考虑对归并排序归并过程进行处理,归并排序递归树如下:
这里写图片描述

先定义一个结构封装数组,这个struct有:
1、数组值
2、索引值
3、计数器【用于储存比它小的数,可以用vector实现】

如图,对归并过程进行处理

在5和2归并时,让5成为左边数组L,让2成为右边数组R
进行归并,5>2,所以将2取出,这个时候将左边数组L的每个数的计数器放入1,然后将5取出,得到归并后数组[2, 5],5的计数器里有个2

同样6和1归并,归并后数组[1, 6],6的计数器里有个1

[2, 5] 和 [1, 6] 归并:
①首先取出右边数组R 中的1,将1放入2和5的计数器中
②取出左边数组L的2,不放入任何计数器
③取出左边数组L的5,不放入任何计数器
④取出右边数组R的6,准备放入左边数组L,但L已经没有数了(因为2和5都被取出来了),所以不能放入计数器中

最后归并结果[1, 2, 5, 6],计数器里数值为
1:计数器里没有值
2:1
5:2,1【首先放入2,再放入1】
6:1

根据上面封装的数据结构里的原始索引下标,对于数组[5, 2, 6, 1]
1–>4
2–>2
5–>1
6–>3

结合索引值和计数器里的数值,我们得到最终返回数组[2, 1, 1, 0]

三、源代码【这个代码能够独立运行,如果需要在题目链接下的网站测试,需要做些修改】

实现细节:

1、 封装数据结构不需要默认构造函数是因为,下面使用到的new操作符不会使用这个默认构造函数

2、在数据结构里重载“=”操作符的时候,需要将原来的计数器清空来接收新的计数器值【具体见源码】

#include <iostream>
#include <vector>
using namespace std;

/*定义一个struct封装数组array
这个struct有:1、数组值 2、下标索引 3、smaller_numbers的计数器【一个vector数组】 
*/ 
struct my_array{
    int value;
    int index;
    vector<int> smaller_numbers;
    my_array &operator = (const my_array& b)
    {
        value = b.value;
        index = b.index;
        int size = b.smaller_numbers.size();
        smaller_numbers.clear();
        for(int i = 0; i < size; i ++)
            smaller_numbers.push_back(b.smaller_numbers[i]);
        return *this;
    }
};

vector<int> count_smaller_numbers(my_array* array, int n_size);
void merge_sort(my_array* array, int start, int end);
void merge(my_array* array, int start, int mid, int end);

int main()
{
    //获取数组 
    int n_size = 0;
    cin >> n_size;
    my_array* array = new my_array[n_size];
    for(int i = 0; i < n_size; i ++)
    {
        cin >> array[i].value;
        array[i].index = i;
    }

    vector<int> result = count_smaller_numbers(array, n_size);
    for(int i = 0; i < result.size(); i ++)
        cout << result[i] << " ";
    return 0;
}

vector<int> count_smaller_numbers(my_array* array, int n_size)
{
    merge_sort(array, 0, n_size - 1);
    //根据struct里的索引值和vector大小创建一个数组存储结果
    vector<int> result = vector<int>(n_size, 0);//创建一个大小为 n_size 的 vector 并将其全初始化为 0
    for(int i = 0; i < n_size; i ++)
        result[array[i].index] = array[i].smaller_numbers.size();

    return result;
}

void merge_sort(my_array* array, int start, int end)
{
    if(start < end)
    {
        int mid = (start + end) / 2;
        merge_sort(array, start, mid);
        merge_sort(array, mid + 1, end);

        merge(array, start, mid, end);
    }
}

void merge(my_array* array, int start, int mid, int end)
{
    /*将左边数组与右边数组归并,原则如下:
     只有右边数组归并到结果数组中时,才使剩下的左边数组中的smaller_numbers计数 
    */

    //将其分为左边数组和右边数组,并赋值 
    int left_size = mid - start + 1;
    int right_size = end - mid;
    my_array* left = new my_array[left_size];
    my_array* right = new my_array[right_size];
    for(int i = 0, k = start; k <= mid; k ++)
        left[i ++] = array[k];
    for(int i = 0, k = mid + 1; k <= end; k ++)
        right[i ++] = array[k];

    //进行归并
    int left_count = 0;
    int right_count = 0; 
    int a_count = start;//记录array数组的起始位置 
    while(left_count < left_size && right_count < right_size)
    {
        if(left[left_count].value > right[right_count].value)
        {
            array[a_count ++] = right[right_count];
            int select = right[right_count].value;
            for(int i = left_count; i < left_size; i ++)
                left[i].smaller_numbers.push_back(select);
            right_count ++;
        }
        else
        {
            array[a_count ++] = left[left_count ++];
        }
    }

    while(left_count < left_size)
        array[a_count ++] = left[left_count ++];

    while(right_count < right_size)
        array[a_count ++] = right[right_count ++];

    delete [] left;
    delete [] right; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值