LeeCode_贪心算法

Leecode1053

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

public class Leecode1053 {

    /**
     * 给你一个正整数数组 arr(可能存在重复的元素),请你返回可在 一次交换(交换两数字 arr[i] 和 arr[j] 的位置)后得到的、按字典序排列小于 arr 的最大排列。
     * 如果无法这么操作,就请返回原数组
     * 
     * 示例 1:
     * 输入:arr = [3,2,1]
     * 输出:[3,1,2]
     * 解释:交换 2 和 1
     * 
     * 
     * 示例 2:
     * 输入:arr = [1,1,5]
     * 输出:[1,1,5]
     * 解释:已经是最小排列
     * 
     * 
     * 示例 3:
     * 输入:arr = [1,9,4,6,7]
     * 输出:[1,7,4,6,9]
     * 解释:交换 9 和 7
     * 。
     */

    /*  题解
       记数组arr的长度为n, 对于0 <= i < n, 如果交换 arr[i] 和 arr[j]后得到的新数组按字典排列比原
     数组小,显然有 arr[i] > arr[j]成立。因此符合题意要求的交换会使得数组arr在下标i处的元素变小。那么
     为了得到按字典排序小于原来数组的最大新数组,尽可能地保持前面的元素不变是最优的,即让i最大化。
         1. 如何最大化i
            我们可以从大到小枚举 i ∈ [0, n - 2], 然后枚举 j ∈ [i + 1, n], 如果存在j使 arr[i] >
            arr[j] 成立,那么说明当前枚举的i是最大化的。这里只需要判断 arr[i] > arr[i + 1] 是
            否成立即可, 那么说明当前枚举的i是最大化的。这里只需要判断 arr[i] > arr[i + 1] 是否
            成立即可,因为后面的元素是符合非递减的,即 arr[i + 1]是arr[i] 后面的最小元素。
            
            -- 当我们枚举i时,显然不存在k > i, 使得 arr[k] > arr[k + 1]成立,因此 arr[i]后面
            的元素是符合非递减的。
         
        2.  i 最大化后, j ∈ [i + 1, n]应该怎么选择
            显然在满足 arr[j] < arr[i] 的条件下,取最大的arr[j]是最优的。但是题目并没有元素不重
            复的要求,最大的arr[j]可能有重复值,那么选择其中下标最小的arr[j]是最优的。
            
            由前面推导可知,区间[i + 1,n)的元素是非递减的,因此我们从大到小枚举j, 直到 arr[j] < arr[i]
            且 arr[j] ≠ arr[j - 1]成立, 那么得到的j就是符合要求的。交换arr[i] 和 arr[j]。
     
    
     */

    /**
     * 方法一
     * @param A
     * @return
     */
    public int[] prevPermOpt1_1(int[] A) {
        int len = A.length;
        int curMax = -1;
        int index = -1;
        boolean hasResult = false;
        for (int i = len - 2; i >= 0; i--) {
            if (A[i+1] < A[i]) {                    // 此处逆序,需要移动A[i]
                for (int j = i + 1; j < len; j++) { // 寻找与 A[i] 交换的位置
                    if (A[i] > A[j]) {               // 必须满足 A[i] > A[j],否则不能满足交换后的字典序小于原始字典序
                        hasResult = true;
                        if (A[j] > curMax) {
                            curMax = A[j];
                            index = j;
                        }
                    }
                }
                if (hasResult) {
                    int tmp = A[i];
                    A[i] = A[index];
                    A[index] = tmp;
                    return A;
                }
            }
        }
        return A;
    }


    /**
     * 
     * @param arr
     * @return
     */
    public int[] prevPermOpt1_2(int[] arr) {
        int n = arr.length;
        for (int i = n - 2; i >= 0; i--) {
            if (arr[i] > arr[i + 1]) {
                int j = n - 1;
                while (arr[j] >= arr[i] || arr[j] == arr[j - 1]) {
                    j--;
                }
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                break;
            }
        }
        return arr;
    }



    /**
     * 输出
     * @param ints
     * @return
     */
    public static List createList(int[] ints) {
        List<Integer> arrayList = new ArrayList();
        StringBuilder sBuil = new StringBuilder();
        for (int anInt : ints) {
            arrayList.add(anInt);
        }
        return arrayList;
    }


    public static void main(String[] args) {
        Leecode1053 leecode1053 = new Leecode1053();
        // 方法一测试
        int[] a = new int[]{1,9,4,6,7};
        int[] ints1 = leecode1053.prevPermOpt1_1(a);
        System.out.println(createList(ints1));
        
        // 方法二测试
        int[] b = new int[]{1,9,4,6,7};
        int[] ints2 = leecode1053.prevPermOpt1_2(b);
        System.out.println(createList(ints2));
    }
    
    
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值