如何对N位整数执行K-swap操作以获得最大可能的数字

时间:2012-08-29 16:53:07

标签: algorithm

我最近接受了采访,并被问到这个问题。让我正确解释一下这个问题:

  

给定数字M(N位整数)和K个交换操作(交换   操作可以交换2位数,设计算法以获得最大值   可能的整数?
  例子:
  M = 132 K = 1输出= 312
  M = 132 K = 2输出= 321
  M = 7899k = 2输出= 9987

我的解决方案(伪代码中的算法)。我使用max-heap来获取每个K操作中N位数的最大数字,然后适当地交换它。

for(int i = 0; i<K; i++)
{
    int max_digit_currently = GetMaxFromHeap();
    // The above function GetMaxFromHeap() pops out the maximum currently and deletes it from heap

    int index_to_swap_with = GetRightMostOccurenceOfTheDigitObtainedAbove();
    // This returns me the index of the digit obtained in the previous function  
    // .e.g If I have 436659 and K=2 given,   
    // then after K=1 I'll have 936654 and after K=2, I should have 966354 and not 963654.

    // Now, the swap part comes. Here the gotcha is, say with the same above example, I have K=3.
    // If I do GetMaxFromHeap() I'll get 6 when K=3, but I should not swap it, 
    // rather I should continue for next iteration and 
    // get GetMaxFromHeap() to give me 5 and then get 966534 from 966354.

    if (Value_at_index_to_swap == max_digit_currently)
        continue;
    else
        DoSwap();
}

时间复杂度:O(K *(N + log_2(N)))
// K次[log_2(N)用于从堆中弹出数字&amp; N得到最右边的索引与交换]

上述策略在此示例中失败:
M = 8799,K = 2 按照我的策略,在K = 1后,我将得到M = 9798,在K = 2之后,M = 9978。但是,在K = 2之后,我得到的最大值是M = 9987。

我错过了什么?
还建议其他解决问题的方法&amp;优化我的解决方案的方法。

7 个答案:

答案 0 :(得分:1)

这是一个递归函数,它为每个(当前最大)数字排序可能的交换值:

function swap2max(string, K) {
    // the recursion end:
    if (string.length==0 || K==0)
        return string

    m = getMaxDigit(string)
    // an array of indices of the maxdigits to swap in the string
    indices = []
    // a counter for the length of that array, to determine how many chars
    // from the front will be swapped
    len = 0
    // an array of digits to be swapped
    front = []
    // and the index of the last of those:
    right = 0
    // get those indices, in a loop with 2 conditions:
    // * just run backwards through the string, until we meet the swapped range
    // * no more swaps than left (K)
    for (i=string.length; i-->right && len<K;)
        if (m == string[i])
            // omit digits that are already in the right place
            while (right<=i && string[right] == m)
                right++
            // and when they need to be swapped
            if (i>=right)
                front.push(string[right++])
                indices.push(i)
                len++
    // sort the digits to swap with
    front.sort()
    // and swap them
    for (i=0; i<len; i++)
        string.setCharAt(indices[i], front[i])
    // the first len digits are the max ones
    // the rest the result of calling the function on the rest of the string
    return m.repeat(right) + swap2max(string.substr(right), K-len)
}

答案 1 :(得分:1)

我认为缺少的部分是,在您按照OP描述的算法执行K交换后,您将留下一些可以在它们之间交换的数字。例如,对于数字87949,在初始算法之后我们将获得99748.但是,之后我们可以交换7和8“免费”,即不消耗任何K交换。这意味着“我宁愿不用第二个9交换7,而是用第一个交换。”

因此,要获得最大数量,可以执行OP描述的算法并记住向右移动的数字以及它们移动到的位置。然后,按降序对这些数字进行排序,并将它们放在从左到右的位置。

这类似于两个阶段的算法分离 - 在第一个阶段,您可以选择前面的哪些数字以最大化前K个位置。然后,您可以确定将它们与其所占位置的数字交换的顺序,以便数字的其余部分也最大化。

并非所有细节都清楚,我并不是100%确定它能正确处理所有情况,所以如果有人可以打破它 - 继续。

答案 2 :(得分:0)

这都是伪代码,但很容易转换为其他语言。该解决方案是非递归的,并且在线性最坏情况和平均情况下运行。

您将获得以下功能:

function k_swap(n, k1, k2):
    temp = n[k1]
    n[k1] = n[k2]
    n[k2] = temp

int : operator[k]
    // gets or sets the kth digit of an integer

property int : magnitude
    // the number of digits in an integer

您可以执行以下操作:

int input = [some integer] // input value

int digitcounts[10] = {0, ...} // all zeroes
int digitpositions[10] = {0, ...) // all zeroes
bool filled[input.magnitude] = {false, ...) // all falses

for d = input[i = 0 => input.magnitude]:
    digitcounts[d]++ // count number of occurrences of each digit

digitpositions[0] = 0;
for i = 1 => input.magnitude:
    digitpositions[i] = digitpositions[i - 1] + digitcounts[i - 1] // output positions

for i = 0 => input.magnitude:
    digit = input[i]
    if filled[i] == true:
        continue
    k_swap(input, i, digitpositions[digit])
    filled[digitpositions[digit]] = true
    digitpositions[digit]++

我将使用数字input = 724886771

来浏览它
computed digitcounts:
{0, 1, 1, 0, 1, 0, 1, 3, 2, 0}

computed digitpositions:
{0, 0, 1, 2, 2, 3, 3, 4, 7, 9}

swap steps:
swap 0 with 0: 724886771, mark 0 visited
swap 1 with 4: 724876781, mark 4 visited
swap 2 with 5: 724778881, mark 5 visited
swap 3 with 3: 724778881, mark 3 visited
skip 4 (already visited)
skip 5 (already visited)
swap 6 with 2: 728776481, mark 2 visited
swap 7 with 1: 788776421, mark 1 visited
swap 8 with 6: 887776421, mark 6 visited

output number: 887776421

编辑:

这不能正确解决问题。如果我以后有时间,我会修理它,但我现在没有。

答案 3 :(得分:0)

我怎么做(在伪c中 - 没什么特别的),假设传递幻想整数数组,其中每个元素代表一个十进制数字:

int[] sortToMaxInt(int[] M, int K) {
    for (int i = 0; K > 0 && i < M.size() - 1; i++) {
        if (swapDec(M, i)) K--;
    }
    return M;
}

bool swapDec(int[]& M, int i) {
    /* no need to try and swap the value 9 as it is the 
     * highest possible value anyway. */
    if (M[i] == 9) return false;

    int max_dec = 0;
    int max_idx = 0;
    for (int j = i+1; j < M.size(); j++) {
        if (M[j] >= max_dec) {
            max_idx = j;
            max_dec = M[j];
        }
    }

    if (max_dec > M[i]) {
        M.swapElements(i, max_idx);
        return true;
    }
    return false;
}

从我的头顶如此,如果有人发现一些致命的缺陷,请告诉我。

编辑:基于此处发布的其他答案,我可能会误解这个问题。有人在意吗?

答案 4 :(得分:0)

您从max-number(M, N, 1, K)开始。

max-number(M, N, pos, k)
{
    if k == 0
        return M
    max-digit = 0
    for i = pos to N
        if M[i] > max-digit
            max-digit = M[i]
    if M[pos] == max-digit
        return max-number(M, N, pos + 1, k)
    for i = (pos + 1) to N
        maxs.add(M)
        if M[i] == max-digit
            M2 = new M
            swap(M2, i, pos)
            maxs.add(max-number(M2, N, pos + 1, k - 1))
    return maxs.max()
}

答案 5 :(得分:0)

这是我的方法(这不是万无一失的,但涵盖了基本情况)。首先,我们需要一个将INT的每个DIGIT提取到容器中的函数:

std::shared_ptr<std::deque<int>> getDigitsOfInt(const int N)
{
    int number(N);
    std::shared_ptr<std::deque<int>> digitsQueue(new std::deque<int>());
    while (number != 0)
    {
        digitsQueue->push_front(number % 10);
        number /= 10;
    }
    return digitsQueue;
}

你显然想要创建它的反转,所以将这样的容器转换回INT:

const int getIntOfDigits(const std::shared_ptr<std::deque<int>>& digitsQueue)
{
    int number(0);
    for (std::deque<int>::size_type i = 0, iMAX = digitsQueue->size(); i < iMAX; ++i)
    {
        number = number * 10 + digitsQueue->at(i);
    }
    return number;
}

您还需要找到MAX_DIGIT。使用std :: max_element会很好,因为它会将迭代器返回到容器的最大元素,但如果还有更多,则需要最后一个容器。所以让我们实现我们自己的最大算法:

int getLastMaxDigitOfN(const std::shared_ptr<std::deque<int>>& digitsQueue, int startPosition)
{
    assert(!digitsQueue->empty() && digitsQueue->size() > startPosition);
    int maxDigitPosition(0);
    int maxDigit(digitsQueue->at(startPosition));
    for (std::deque<int>::size_type i = startPosition, iMAX = digitsQueue->size(); i < iMAX; ++i)
    {
        const int currentDigit(digitsQueue->at(i));

        if (maxDigit <= currentDigit)
        {
            maxDigit = currentDigit;
            maxDigitPosition = i;
        }
    }
    return maxDigitPosition;
}

从这里开始你要做的事情,把最右边(最后)的MAX DIGITS放到他们的位置,直到你可以交换:

const int solution(const int N, const int K)
{
    std::shared_ptr<std::deque<int>> digitsOfN = getDigitsOfInt(N);

    int pos(0);
    int RemainingSwaps(K);
    while (RemainingSwaps)
    {
        int lastHDPosition = getLastMaxDigitOfN(digitsOfN, pos);
        if (lastHDPosition != pos)
        {
            std::swap<int>(digitsOfN->at(lastHDPosition), digitsOfN->at(pos));

            ++pos;
            --RemainingSwaps;
        }
    }

    return getIntOfDigits(digitsOfN);
}

还有未经处理的角落案件,但我会把它留给你。

答案 6 :(得分:0)

我假设K = 2,但您可以更改值!

Java代码

public class Solution {
    public static void main (String args[]) {
        Solution d = new Solution();
        System.out.println(d.solve(1234));
        System.out.println(d.solve(9812));
        System.out.println(d.solve(9876));
    }
    public int solve(int number) {
        int[] array = intToArray(number);
        int[] result = solve(array, array.length-1, 2);
        return arrayToInt(result);
    }
    private int arrayToInt(int[] array) {
        String s = "";
        for (int i = array.length-1 ;i >= 0; i--) {
            s = s + array[i]+"";
        }
        return Integer.parseInt(s);
    }
    private int[] intToArray(int number){
        String s = number+"";
        int[] result = new int[s.length()];
        for(int i = 0 ;i < s.length() ;i++) {
            result[s.length()-1-i] = Integer.parseInt(s.charAt(i)+"");
        }
        return result;
    }
    private int[] solve(int[] array, int endIndex, int num) {
        if (endIndex == 0)
            return array;
        int size = num ;
        int firstIndex = endIndex - size;
        if (firstIndex < 0)
            firstIndex = 0;
        int biggest = findBiggestIndex(array, endIndex, firstIndex);
        if (biggest!= endIndex) {
            if (endIndex-biggest==num) {
                while(num!=0) {
                    int temp = array[biggest];
                    array[biggest] = array[biggest+1];
                    array[biggest+1] = temp;
                    biggest++;
                    num--;
                }
                return array;
            }else{
                int n = endIndex-biggest;
                for (int i = 0 ;i < n;i++) {
                    int temp = array[biggest];
                    array[biggest] = array[biggest+1];
                    array[biggest+1] = temp;
                    biggest++;
                }
                return solve(array, --biggest, firstIndex);
            }
        }else{
            return solve(array, --endIndex, num);
        }
    }
    private int findBiggestIndex(int[] array, int endIndex, int firstIndex) {
        int result = firstIndex;
        int max = array[firstIndex];
        for (int i = firstIndex; i <= endIndex; i++){
            if (array[i] > max){
                max = array[i];
                result = i;
            }
        }
        return result;
    }
}