力扣——数组序号转换思路分析

题目:
给你一个整数数组 arr ,请你将数组中的每个元素替换为它们排序后的序号。
序号代表了一个元素有多大。序号编号的规则如下:

  • 序号从 1 开始编号。
    一个元素越大,那么序号越大。如果两个元素相等,那么它们的序号相同。
    每个数字的序号都应该尽可能地小。
  • 示例 1:
    输入:arr = [40,10,20,30]
    输出:[4,1,2,3]
    解释:40 是最大的元素。 10 是最小的元素。 20 是第二小的数字。 30 是第三小的数字。
  • 示例 2:
    输入:arr = [100,100,100]
    输出:[1,1,1]
    解释:所有元素有相同的序号。
  • 示例 3:
    输入:arr = [37,12,28,9,100,56,80,5,12]
    输出:[5,3,4,2,8,6,7,1,3]

解题思路1:首先阅读题目,我觉得这应该是一个很简单的排序问题,我们只需要过滤掉原数组当中的重复元素,然后将其余元素进行排序,然后把原数组遍历每个元素在排序列表中的位置,再进行对照序号赋值,就能得出正确答案,于是我就按照这个思路开始编写算法了。
所遇问题1:虽然这个算法解题思路很清晰,但是因为算法效率太低,计算时间太长,卡在了最后一个测试案例上,最终以失败告终。

package 力扣;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;



public class 数组序号转换 {
    public static void main(String[] args) {
        int[] arr = {2,-11,24,15,26,-31,-48,-49,22,37,-36};
        arrayRankTransform(arr);
    }
    public static int[] arrayRankTransform(int[] arr) {
        ArrayList list = new ArrayList();
        int s = arr.length;
        //1.用于过滤重复数组元素
        for (int i = 0; i < s; i++) {
            if (!list.contains(arr[i])){
                list.add(arr[i]);
            }
        }
        int temp = list.size();
        //2.转为数组之后进行排序
        Object[] brr = list.toArray();
        Arrays.sort(brr);
        //3.对于原数组遍历寻找在排序位置的值然后标记
        for (int i = 0; i < s; i++) {
            for (int j = 0; j < temp; j++) {
                if (arr[i] == (int)brr[j]){
                    arr[i] = j+1;
                    break;
                }
            }
        }
        //4.输出相应的值(提交代码时请删掉)
        for (int i = 0; i < s; i++) {
            System.out.print(arr[i]+",");
        }
        return arr;
    }
}

解题思路2:通过对于第一个算法经验总结,我觉得应该换一种思路来解决问题,于是我思考能不能用一个二维数组分别来记录原数组的值和序号,先以原数组元素值大小进行排序,然后将元素值替换成他们现在的位序,最后再以原数组值的原本位序进行排序,这样就能按顺序得出每一个元素在排序列中的编号,优化了第一种算法当中排序完之后一一对照遍历查找的低效率。
所遇问题2:首先在这个算法当中,我卡在了[ ]空数组的测试值当中,会因为数组越界而报错,所以我添加了对数组长度判断而进行的两种情况,但是最后还是因为算法超时,效率过低而以失败告终。

package 力扣;
import java.util.Arrays;

public class 数组序号转换优化 {
    public static void main(String[] args) {
        int[] arr = {19,22,-47,24,-37};
        arr = arrayRankTransform(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
    }
    public static int[] arrayRankTransform(int[] arr) {
        int s = arr.length;
        if (s != 0){
            int[][] brr = new int[2][s];
            int add = 1;
            int i = 0,j = 0;
            //1.用二维数组来存储一维数组,第二层表示序号
            for (i = 0; i < s; i++) {
                brr[0][i] = arr[i];
                brr[1][i] = add++;
            }
            //比较数字
            int temp = 0;
            //下标记录
            int flag = 0;
            //2.先以原数组进行排序
            for (i = 0; i < s-1; i++) {
                temp = brr[0][i];
                flag = i;
                for (j = i; j < s; j++) {
                    if(brr[0][j] < temp){
                        temp = brr[0][j];
                        flag = j;
                    }
                }
                int temp1 = brr[0][i];
                int temp2 = brr[1][i];
                brr[0][i] = brr[0][flag];
                brr[1][i] = brr[1][flag];
                brr[0][flag] = temp1;
                brr[1][flag] = temp2;
            }
            //3.进行位序替换
            add = 1;
            temp = brr[0][0];
            brr[0][0] = add;
            for (int k = 1; k < s; k++) {
                if(brr[0][k] != temp){
                    temp = brr[0][k];
                    brr[0][k] = ++add;
                }else{
                    brr[0][k] = add;
                }
            }
            //4.再以序号原数组进行排序
            for (i = 0; i < s-1; i++) {
                temp = brr[1][i];
                flag = i;
                for (j = i; j < s; j++) {
                    if(brr[1][j] < temp){
                        temp = brr[1][j];
                        flag = j;
                    }
                }
                if(flag != i){
                    int temp1 = brr[0][i];
                    int temp2 = brr[1][i];
                    brr[0][i] = brr[0][flag];
                    brr[1][i] = brr[1][flag];
                    brr[0][flag] = temp1;
                    brr[1][flag] = temp2;
                }
            }
            return brr[0];
        }
        else{
            return arr;
        }
    }
}

解题思路3:最后我在浏览解题评论区当中发现了超级优化的解题思路,原作者是这样解释他的思路的:代码详解
在这里插入图片描述


    public static int[] arrayRankTransform(int[] arr) {
        //分别设置最大无穷和最小无穷
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
    //分别找出数组中最小和最大值存入min和max当中
        for (int num : arr) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
    //创建数组,容量为最大值减去最小值+1
        int[] count = new int[max - min + 1];
    //遍历数组,在原数组每项值减去最小值的位置处赋值1
        for (int num : arr)
            count[num - min] = 1;
    //创建数组,容量为最大值减去最小值+2
        int[] preSum = new int[count.length + 1];
    //遍历preSum数组,将每个位置赋值是preSum[i - 1] + count[i - 1]
        for (int i = 1; i < preSum.length; i++)
            preSum[i] = preSum[i - 1] + count[i - 1];
    //创建数组,容量为原数组长度
        int[] ans = new int[arr.length];
        int index = 0;
        for (int num : arr)
            ans[index++] = preSum[num - min] + 1;
        return ans;
    }

代码理解:作者的这个代码确实十分简洁精炼,但是理解起来却显得有些吃力,我对作者的代码添加上注释,然后逐句分析,有了自己的理解,首先作者想要利用桶排序算法来对于庞大的数组进行排序以提高效率,我还特意去查看了这个排序算法,整体思路就是对于无序数组,按范围拆分进行入桶操作,然后再在桶中进行排序后再拼接。

  1. 首先的关键在于我们要先知道如何定制桶范围的大小,即确定数组中的大和最小值。
  2. 然后创建桶,我们确保每个桶中的元素只有一个,这样就相当递归算法求解到了最后的出口,作者想到的是用数组中每一个值减去最小值的数字作为桶的标志,然后在创建的count 数组中给他赋值1。
  3. 由于数字越大,它减去最小值得到的值也越大,所以上述操作相当于是给数组排序了,然后preSum 数组的作用是将桶中零碎的数字拼接起来,由于操作2全部赋值了1,我们并不知道每个数字实际排在第几位,这时候我们只需要将每位数字累加,这样越靠后的值累加起来也就越大,而且每次加一,我们就能清晰的知道每个元素的位序了。
  4. 做完以上操作,我们只需要再创建一个和原数组长度相同的数组,再利用值减去最小值得方法,去相应的位置找到对应的位序值,给他存入保存。

学习收获

  • 想要将数组去重,可以利用ArrayList
 ArrayList list = new ArrayList();
        int s = arr.length;
        //1.用于过滤重复数组元素
        for (int i = 0; i < s; i++) {
            if (!list.contains(arr[i])){
                list.add(arr[i]);
            }
        }
  • 想要获取无穷最小和无穷最大
 //分别设置最大无穷和最小无穷
 int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
请问您需要的是两个数组的交集元素还是交集元素的数量? 如果是交集元素,可以采用双指针的方式,先将两个数组排序,然后设定两个指针 i 和 j,分别指向两个数组的开头,比较两个指针所指向的元素的大小,如果相等,则说明找到了一个交集元素,将其存到结果数组中;如果不相等,则将指向小的元素的指针向后移动一位,直到其中一个指针越界为止。 代码实现如下: ```c int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){ int i = 0, j = 0, k = 0; int* res = (int*)malloc(sizeof(int) * (nums1Size > nums2Size ? nums2Size : nums1Size)); // 排序 qsort(nums1, nums1Size, sizeof(int), cmp); qsort(nums2, nums2Size, sizeof(int), cmp); while (i < nums1Size && j < nums2Size) { if (nums1[i] == nums2[j]) { res[k++] = nums1[i]; i++; j++; } else if (nums1[i] < nums2[j]) { i++; } else { j++; } } *returnSize = k; return res; } int cmp(const void* a, const void* b) { return *(int*)a - *(int*)b; } ``` 如果是交集元素的数量,可以采用哈希表的方式,用一个哈希表来记录第一个数组中的元素出现的次数,然后遍历第二个数组,在哈希表中查找是否存在相同的元素,如果存在,则说明找到了一个交集元素,将其计数并从哈希表中删除该元素,最后返回计数结果即可。 代码实现如下: ```c int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){ int i = 0, j = 0, k = 0; int* res = (int*)malloc(sizeof(int) * (nums1Size > nums2Size ? nums2Size : nums1Size)); int* hash = (int*)malloc(sizeof(int) * 1001); // 由于题目条件限制在 [0, 1000] 范围内,所以哈希表可以开得比较小 memset(hash, 0, sizeof(hash)); for (i = 0; i < nums1Size; i++) { hash[nums1[i]]++; } for (j = 0; j < nums2Size; j++) { if (hash[nums2[j]] > 0) { res[k++] = nums2[j]; hash[nums2[j]]--; } } *returnSize = k; return res; } ``` 希望这些代码能够帮到您,如有疑问请随时问我~

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值