【奇偶数合并】及【奇数偶数排序】

今天分享两道很有意思的算法题目

题目一:

1、题目描述

奇偶数合并

将所有相邻且相同的偶数相加放入相邻偶数中间 将所有相邻且相同奇数相加,替换掉这两个相邻且相同的奇数 最后输出合并后的数组结果。

举例

数组[1,2,2,7,8,3,3,6]

数组奇偶数合并之后[1,2,4,2,7,8,6,12,6]

2、思路

由于要对偶数合并且将合并值增加到相邻且相等2个偶数之间,然后相邻且相等奇数合并,原始数组长度可能会发生改变,因此需要用额外的数据结构进行存储临时计算结果数据。这里用List进行存储,也可以用其它数据结果存储。

另外两个奇数相加等于偶数,因此要先计算偶数,再计算奇数。

第一轮计算之后,需要判断是否还需要递归进行计算,如果需要,则递归奇偶数合并即可,最后输出计算结果。

3、实战

public class Test1 {

    public static void main(String[] args) {
        int[] array = {1,2,2,7,8,3,3,6};
        calculatedNum(array);
    }

    /**
     * 奇偶数合并
     * 将所有相邻且相同的偶数相加放入相邻偶数中间
     * 将所有相邻且相同奇数相加,替换掉这两个相邻且相同的奇数
     * 最后输出合并后的数组结果
     *
     * @param array 待排序数组
     * @return int[] 结果
     */
    public static int[] calculatedNum(int[] array){
        if(array.length == 0){
            return new int[0];
        }

        List<Integer> lists = new ArrayList<>();
        for (int i = 0; i < array.length-1; i++) {
            if(array[i]==array[i+1]){
                if(array[i]%2==0){
                    lists.add(array[i]);
                    lists.add(array[i]*2);
                }else{
                    lists.add(array[i]*2);
                    i++;
                }
            }else{
                lists.add(array[i]);
            }
        }
        // 由于上面for循环结束条件是i < array.length-1,这里需要添加最后一个元素,才能遍历完全
        lists.add(array[array.length-1]);
        // 将list转成array
        int[] list2Array = Arrays.stream(lists.toArray()).mapToInt(x -> (int) x).toArray();
        if(isNeedRepeatCalculated(list2Array)){
           return  calculatedNum(list2Array);
        }
        return list2Array;
    }

    /**
     * 判断是否需要二次合并计算
     *
     * @param array 待判断的数组
     * @return boolean 结果
     */
    public static boolean isNeedRepeatCalculated(int[] array){
        for (int i = 0; i < array.length-1; i++) {
            if(array[i] == array[i+1]){
                return true;
            }
        }
        return false;
    }
}

题目二:

1、题目描述

奇数偶数排序

给一个数组,给数组中奇数按照升序排序,偶数按照降序排序,但是奇数和偶数的相对位置保持不变,输出排序后的数组

输入:[3, 1, 2, 4, 6, 5, 7, 8, 9]
输出:[1, 3, 8, 6, 4, 5, 7, 2, 9]

思路1

利用双重for循环,遍历到偶数,降序排序;遍历到奇数,升序排序

    public static int[] sortArray1(int[] array) {
        // 思路1:利用双重for循环,遍历到偶数,降序排序;遍历到奇数,升序排序
        for (int i = 0; i < array.length; i++) {
            for (int j = i + 1; j < array.length; j++) {
                if (array[i] % 2 == 0 && array[j] % 2 == 0 && array[i] < array[j]) {
                    // 偶数,前者比后者小时,交换位置为前者比后者大
                    int temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }

                if (array[i] % 2 == 1 && array[j] % 2 == 1 && array[i] > array[j]) {
                    // 奇数,前者比后者大时,交换位置为前者比后者小
                    int temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }
            }
        }
        return array;
    }

思路2

首先创建一个临时Flag数组,大小等于原始数组,用于存放原始数组对应位置的奇数和偶数标志。

然后将奇数和偶数分别筛选到两个数组,将原始数组中对应位置的奇数或偶数标志存入到Flag数组,并对奇数和偶数排序。

最后遍历Flag数组,当值为偶数标志,存偶数数组值;当值为奇数标志,存奇数数组值。

public static int[] sortArray2(int[] array) {
        // 思路2:首先创建一个临时Flag数组,大小等于原始数组,用于存放原始数组对应位置的奇数和偶数标志。
        // 然后将奇数和偶数分别筛选到两个数组,将原始数组中对应位置的奇数或偶数标志存入到Flag数组,并对奇数和偶数排序。
        // 然后遍历Flag数组,当值为偶数标志,存偶数数组值;当值为奇数标志,存奇数数组值。
        List<Integer> ouShuList = new ArrayList<>();
        List<Integer> jiShuList = new ArrayList<>();
        int[] flagArray = new int[array.length];

        for (int i = 0; i < array.length; i++) {
            if (array[i] % 2 == 0) {
                ouShuList.add(array[i]);
                flagArray[i] = 0;
            } else {
                jiShuList.add(array[i]);
                flagArray[i] = 1;
            }
        }

        // 偶数降序排序
        ouShuList.sort((o1, o2) -> o2 - o1);
        // 奇数升序排序
        Collections.sort(jiShuList);
        // 偶数
        int m = 0;
        // 奇数
        int n = 0;
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < flagArray.length; i++) {
            // 偶数
            if (flagArray[i] == 0) {
                res.add(ouShuList.get(m));
                m++;
            } else {
                // 奇数
                res.add(jiShuList.get(n));
                n++;
            }
        }

        return res.stream().mapToInt(x -> x).toArray();
    }

### 逆序数奇偶性的定义 在排列中,如果一对数的位置与其数值大小顺序相反,则构成一个逆序。一个排列中的所有逆序数量总被称为该排列的逆序数。当逆序数为奇数时,此排列被定义为奇排列;反之,若逆序数为偶数,则为偶排列[^1]。 ### 计算逆序数的方法 为了计算给定序列的逆序数,可以采用多种方法: - **暴力枚举法**:遍历整个列表,对于每一个元素,统计在其之后有多少个更小的元素。这种方法的时间复杂度较高,达到O(n^2),适用于较小规模的数据集[^4]。 - **改进版冒泡排序**:通过记录交换次数来间接获取逆序数。每当发生一次交换操作,意味着存在至少一对逆序关系。然而,这同样不是最高效的解决方案[^2]。 - **基于归并排序的高效算法**:利用分治策略,在合并阶段同时完成计数工作。具体来说,在每一轮合并过程中,一旦发现左侧子数组当前处理的元素大于右侧对应位置上的任意元素,则说明这些成对组合构成了新的逆序情况。此时只需累加相应的差值即可得到新增的逆序数目。整体时间复杂度降低至接近线性级别——O(n log n)[^5]。 ```go func countInversions(arr []int) int { var mergeAndCount func([]int, []int) (sortedArr []int, inversions int) mergeAndCount = func(left, right []int) ([]int, int) { result := make([]int, 0, len(left)+len(right)) i, j, inversionCount := 0, 0, 0 for i < len(left) && j < len(right) { if left[i] <= right[j] { result = append(result, left[i]) i++ } else { result = append(result, right[j]) j++ inversionCount += len(left) - i // 关键逻辑:这里增加了逆序的数量 } } result = append(result, left[i:]...) result = append(result, right[j:]...) return result, inversionCount } if len(arr) <= 1 { return 0 } mid := len(arr)/2 leftPart := arr[:mid] rightPart := arr[mid:] _, leftInv := mergeAndCount(nil, leftPart) _, rightInv := mergeAndCount(nil, rightPart) _, splitInv := mergeAndCount(leftPart, rightPart) totalInversions := leftInv + rightInv + splitInv return totalInversions } ``` 上述代码展示了如何使用归并排序的思想有效地找出所有的逆序对,并返回总的逆序数。注意这里的`countInversions()`函数不仅实现了排序功能还完成了逆序数目的统计任务。 ### 判断奇偶性 最终要确定某个特定排列下的逆序数究竟是奇数还是偶数非常简单,只需要取模运算 `% 2` 即可得出结论。如果是0则表示偶数(即偶排列),否则就是奇数(即奇排列)。例如,对于序列 `3, 4, 5, 2, 1` 的逆序数为5,因此这是一个奇排列[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值