【问题标题】:Minimum number of swaps to sort an array对数组进行排序的最小交换次数
【发布时间】:2019-08-08 03:50:34
【问题描述】:

我需要做这样的事情:假设我有一个数组:

 [3, 4, 1, 2]

我需要交换 3 和 4,以及 1 和 2,所以我的数组看起来像 [4, 3, 2, 1]。现在,我可以做sort()。在这里,我需要计算需要多少次迭代,才能将初始数组更改为最终输出。示例:

// I can sort one pair per iteration
let array = [3, 4, 1, 2, 5]
let counter = 0;

//swap 3 and 4
counter++;
// swap 1 and 2
counter++;
// 5 goes to first place
counter++

// now counter = 3 <-- what I need

编辑:这是我尝试过的。并不总是有效......它来自这个问题:Bubble sort algorithm JavaScript

let counter = 0;
    let swapped;
    do {
        swapped = false;
        for (var i = 0; i < array.length - 1; i++) {
            if (array[i] < array[i + 1]) {
                const temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
                swapped = true;
                counter++;
            }
        }
    } while (swapped);

编辑:这并不总是正确的,因为例如,我可以将位置从最后一个交换到第一个。看上面的示例代码,现在已经编辑好了。

【问题讨论】:

  • 为什么不试试呢?
  • 您需要使用.sort 还是不使用.sort
  • 这个问题可以澄清一下。有许多排序例程——当您说“我需要”时,这表明您正在寻找对数组进行排序所需的最少排序步骤。如果您不关心最小数量,.sort 的自定义比较器函数内的计数器将为您提供引擎的内置排序功能(通常是快速排序 + 插入排序),但您需要跟踪只有在比较导致交换的情况下。
  • @nemanja 其实是依赖于浏览器,原生代码中数组的排序方法是用什么算法实现的。
  • 感谢更新,但还不是很清楚。听起来您要求对数组进行排序所需的绝对最小步骤数(冒泡排序不能保证这一点,但这是一个很好的尝试)。是这样吗?如果是这样,我会更新标题和正文以明确要求。

标签: javascript arrays


【解决方案1】:

这是迄今为止我尝试过的最佳代码,该代码也被认为是最佳代码 由hackerrank 回答:

function minimumSwaps(arr) {
    var arrLength = arr.length;

    // create two new Arrays 
    // one record value and key separately
    // second to keep visited node count (default set false to all)

    var newArr = [];
    var newArrVisited = [];
    for (let i = 0; i < arrLength; i++) {
        newArr[i]= [];
        newArr[i].value = arr[i];
        newArr[i].key = i;
        newArrVisited[i] = false;
    }

    // sort new array by value

    newArr.sort(function (a, b) {
        return a.value - b.value;
    })

    var swp = 0;
    for (let i = 0; i < arrLength; i++) {

        // check if already visited or swapped
        if (newArr[i].key == i || newArrVisited[i]) {
            continue;
        }

        var cycle = 0;
        var j = i;
        while (!newArrVisited[j]) {

            // mark as visited
            newArrVisited[j] = true;
            j = newArr[j].key; //assign next key
            cycle++;
        }
        if (cycle > 0) {
            swp += (cycle > 1) ? cycle - 1 : cycle;
        } 

    }
    return swp;
}

reference

【讨论】:

    【解决方案2】:
    //You are given an unordered array consisting of consecutive integers  [1, 2, 3, ..., n] without any duplicates.
    //still not the best
    
    function minimumSwaps(arr) {
         let count = 0;
        for(let i =0; i< arr.length; i++){
            if(arr[i]!=i+1){
                let temp = arr[i];
                arr[arr.indexOf(i+1)] =temp;
                arr[i] = i+1;
                count =count+1;
            }
        }
        return count;
    }
    

    【讨论】:

      【解决方案3】:

      我认为您想要衡量一个排序需要多少次迭代有两个原因。所以我会给你一些理论(如果数学太密集,别担心),然后是一些实际应用。

      有许多排序算法,其中一些算法根据您要排序的项目数量具有可预测的迭代次数,其中一些只是基于要排序的项目的顺序和哪个项目的抽签运气如何选择所谓的pivot。因此,如果优化对您非常重要,那么您将需要为排序算法选择正确的算法。否则,请使用通用算法。

      以下是用于学习的最流行的排序算法,每种算法都有最少、最差和平均的运行情况。如果这不仅仅是一个理论/学习练习,那么堆排序、基数和二元排序值得一看。

      快速排序

      最坏情况:Θ(n 2)

      最佳情况:Θ(n lg n)

      平均情况:Θ(n lg n)

      Here is a Quicksort implementation by Charles Stover

      归并排序

      最坏情况:Θ(n lg n)

      最佳情况:Θ(n lg n)

      平均情况:Θ(n lg n)

      (注意它们都是一样的)

      Here is a merge sort implementation by Alex Kondov

      插入排序

      最坏情况:Θ(n2)

      最佳情况:Θ(n)

      平均情况:Θ(n2)

      (请注意,它的最坏情况和平均情况是相同的,但最好的情况是所有算法中最好的)

      Here is an insertion sort implementation by Kyle Jensen

      选择排序

      最坏情况:Θ(n2)

      最佳情况:Θ(n2)

      平均情况:Θ(n2)

      (注意它们都是一样的,就像归并排序一样)。

      Here is a selection sort algorithm written by @dbdavid updated by myself for ES6

      您可以很容易地向这些示例中的任何一个添加一个迭代器变量,以计算它们进行的交换次数,并使用它们来查看哪种算法在哪种情况下效果最好。

      如果项目已经很好地排序,那么插入排序是您的最佳选择。如果您完全不知道,四种基本排序算法中的快速排序是您的最佳选择。

      【讨论】:

      • 这不能回答问题。 OP 询问交换次数。
      • @NinoFiliu 如果你看到最后,你会看到我告诉他他可以很容易地添加一个迭代器变量来计算每次交换。
      【解决方案4】:
      function minimumSwaps(arr) {
      
         var counter = 0;
      
         for (var i = arr.length; i > 0; i--) {
            var minval = Math.min(...arr); console.log("before", arr);
            var minIndex = arr.indexOf(minval);
            if (minval != = arr[0]) {
               var temp = arr[0];
               arr[0] = arr[minIndex];
               arr[minIndex] = temp; console.log("after", arr);
               arr.splice(0, 1);
               counter++;
            }
            else {
               arr.splice(0, 1); console.log("in else case")
            }
      
         } return counter;
      }
      

      这就是我调用交换函数的方式:

      minimumSwaps([3, 7, 6, 9, 1, 8, 4, 10, 2, 5]);

      它适用于选择排序。逻辑如下:

      1. 遍历数组长度
      2. 如果第 0 个索引没有找到最小值,则查找数组中的最小元素,然后与数组中的第一个元素交换。
      3. 现在删除第一个元素。
      4. 如果第 2 步不存在,则删除第一个元素(这是已经存在的最小值)
      5. 当我们交换值时增加计数器。
      6. 在 for 循环之后返回计数器值。

      它适用于所有值。

      但是,由于大约 50,000 的值超时,它会失败。

      【讨论】:

      • 非常聪明的解决方案
      • 这就是选择排序的工作原理。但没有给出最优解
      【解决方案5】:

      除非您已经对计算机科学或真正的数学天才有所了解,否则该问题的解决方案不是很直观,但这一切都归结为反转次数和产生的循环

      如果您不熟悉计算机科学,我建议您使用以下资源来补充此解决方案:

      如果我们将反转定义为:

      arr[i]>arr[j]

      其中 i 是当前索引,j 是下一个索引 --

      如果没有反转,则数组已经有序并且不需要排序。

      例如:

      [1,2,3,4,5]

      所以交换次数与反转次数有关,但不是直接相关,因为每次反转都可能导致一系列交换(与单个交换 EX:[3,1,2] 相对)。

      所以如果考虑以下数组:

      [4,5,2,1,3,6,10,9,7,8]

      这个数组由三个循环组成。

      循环一 - 4,1,3(两次交换)

      循环二- 5,2(一次交换)

      周期三- 6(0 次交换)

      第四周期 - 10,9,7,8(3 次交换)

      现在是 CS 和数学魔法真正发挥作用的地方:每个循环只需要一次通过即可对其进行正确排序,这始终是正确的。

      所以另一种说法是——对任何循环进行排序的最小交换次数是该循环中的元素数减一,或更明确地说:

      最小交换 =(周期长度 - 1)

      因此,如果我们将每个周期的最小交换次数相加,则该总和将等于原始数组的最小交换次数。

      这是我试图解释为什么这个算法有效:

      如果我们认为任何连续的数字集只是数轴的一部分,那么任何从零开始的集合都应该等于它自己的索引,如果该集合表示为 Javascript 数组。这个想法成为以编程方式确定元素是否已经基于其自身值处于正确位置的标准。

      如果当前值不等于它自己的索引,那么程序应该检测一个循环开始并记录它的长度。一旦while循环达到循环中的原始值,它将循环中的最小交换次数添加到计数器变量中。

      无论如何,这是我的代码——它非常冗长但应该可以工作:

      export const minimumSwaps = (arr) => {
      
        //This function returns the lowest value
        //from the provided array.
        //If one subtracts this value the from
        //any value in the array it should equal
        //that value's index.
        const shift = (function findLowest(arr){
            let lowest=arr[0];
            arr.forEach((val,i)=>{
                if(val<lowest){
                    lowest=val;
                }
            })
            return lowest;
        })(arr);
      
        //Declare a counter variable
        //to keep track of the swaps.
        let swaps = 0;
      
        //This function returns an array equal
        //in size to the original array provided. 
        //However, this array is composed of 
        //boolean values with a value of false.
        const visited = (function boolArray(n){
            const arr=[];
            for(let i = 0; i<n;i++){
                arr.push(false);
            }
            return arr;
          })(arr.length);
      
        //Iterate through each element of the
        //of the provided array.
        arr.forEach((val, i) => {
      
          //If the current value being assessed minus
          //the lowest value in the original array
          //is not equal to the current loop index,
          //or, if the corresponding index in
          //the visited array is equal to true,
          //then the value is already sorted 
          if (val - shift === i || visited[i]) return;
      
          //Declare a counter variable to record
          //cycle length.
          let cycleLength = 0;
      
          //Declare a variable for to use for the 
          //while loop below, one should start with
          //the current loop index
          let x = i;
      
          //While the corresponding value in the
          //corresponding index in the visited array
          //is equal to false, then we 
          while (!visited[x]) {
      
            //Set the value of the current
            //corresponding index to true
            visited[x] = true;
      
            //Reset the x iteration variable to
            //the next potential value in the cycle  
            x = arr[x] - shift;
      
            //Add one to the cycle length variable
            cycleLength++;
          };
      
          //Add the minimum number of swaps to
          //the swaps counter variable, which
          //is equal to the cycle length minus one
          swaps += cycleLength - 1;
      
        });
      
        return swaps
      
      }
      

      【讨论】:

        【解决方案6】:

        此解决方案简单快捷。

        function minimumSwaps(arr) {
        
            let minSwaps = 0;
        
            for (let i = 0; i < arr.length; i++) {
                // at this position what is the right number to be here
                // for example at position 0 should be 1
                // add 1 to i if array starts with 1 (1->n)
                const right = i+1;
                // is current position does not have the right number
                if (arr[i] !== right) {
                    // find the index of the right number in the array
                    // only look from the current position up passing i to indexOf
                    const rightIdx = arr.indexOf(right, i);
                    // replace the other position with this position value
                    arr[rightIdx] = arr[i];
                    // replace this position with the right number
                    arr[i] = right;
                    // increment the swap count since a swap was done
                    ++minSwaps;
                }
            }
        
            return minSwaps;
        }
        

        【讨论】:

          【解决方案7】:

          这是我的解决方案,但它会超时 3 个输入非常大的测试用例。使用较小的输入,它可以工作并且不会因超时而终止。

          function minimumSwaps(arr) {
            let swaps = 0;
            for (let i = 0; i < arr.length; i++) {
              if (arr[i] === i + 1) continue;
              arr.splice(i, 1, arr.splice(arr.indexOf(i + 1), 1, arr[i])[0]); //swap 
              swaps++;
            }
            return swaps;
          }
          
          

          我正在学习如何提高性能,欢迎任何帮助。

          【讨论】:

            【解决方案8】:

            这是我对 JavaScript 中 Main Swaps 2 问题的解决方案。它通过了所有的测试用例。我希望有人觉得它有用。

            //this function calls the mainSwaps function..
            function minimumSwaps(arr){
                   let swaps = 0;
            
                    for (var i = 0; i < arr.length; i++){
                        var current = arr[i];
                        var targetIndex = i + 1;
                        if (current != targetIndex){
                            swaps += mainSwaps(arr, i);
                        }
                    }
            
                    return swaps;
            
            }
            
            //this function is called by the minimumSwaps function
            function mainSwaps(arr, index){
                let swapCount = 0;
                let currentElement = arr[index];
                let targetIndex = currentElement - 1;
                let targetElement = arr[currentElement - 1];
            
                while (currentElement != targetElement){
                    //swap the elements
                    arr[index] = targetElement;
                    arr[currentElement - 1] = currentElement;
            
                    //increase the swapcount 
                    swapCount++;
            
                    //store the currentElement, targetElement with their new values..
                    currentElement = arr[index];
                    targetElement = arr[currentElement - 1];
                }
            
                return swapCount;
            }
            
            var myarray = [2,3,4,1,5];
            var result = console.log(minimumSwaps(myarray));
            

            【讨论】:

              猜你喜欢
              • 2015-01-06
              • 1970-01-01
              • 2013-02-15
              • 2017-01-09
              • 2015-02-18
              • 2018-07-08
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多