这是一个面试问题。 swap
表示从数组中删除任何元素并将其附加到同一数组的后面。给定一个整数数组,找到排序数组所需的最小swaps
个数。你能比O(n^2)
做得更好吗?
例如:输入数组:[3124]。 swaps
的数量:2([3124] - > [1243] - > [1234])。
答案 0 :(得分:22)
问题归结为找到排序数组的最长前缀,该数组在输入数组中显示为子序列。这确定了 not 需要排序的元素。其余的元素需要逐个删除,从最小到最大,并附加在后面。
在您的示例[3, 1, 2, 4]
中,已经排序的子序列为[1, 2]
。最佳解决方案是删除重新包含的两个元素3
和4
,并将它们添加到后面。因此,最佳解决方案是两次“交换”。
使用O(n logn)
额外内存可以在O(n)
时间内查找子序列。以下伪代码将执行此操作(代码也恰好是有效的Python):
l = [1, 2, 4, 3, 99, 98, 7]
s = sorted(l)
si = 0
for item in l:
if item == s[si]:
si += 1
print len(l) - si
如果在您的示例中,数组包含从1
到n
的整数排列,则可以使用O(n)
内存在O(1)
时间内解决问题:
l = [1, 2, 3, 5, 4, 6]
s = 1
for item in l:
if item == s:
s += 1
print len(l) - s + 1
更一般地说,只要我们先验地知道输出数组就可以使用第二种方法,因此不需要通过排序找到它。
答案 1 :(得分:9)
即使我们不假设连续值数组,这也可能在O(nlogn)
中有效
如果我们这样做 - 可以在O(n)
中完成。
一种方法是使用O(n)
空格和O(nlogn)
时间
给定数组A
将其(O(nlogn)
)排序为第二个数组B
现在......(数组从1索引)
swaps = 0
b = 1
for a = 1 to len(A)
if A[a] == B[b]
b = b + 1
else
swaps = swaps + 1
答案 2 :(得分:4)
观察:如果元素交换到后面,它的先前位置无关紧要。任何元素都不需要多次交换。
观察:最后一次交换(如果有的话)必须移动最大的元素。
观察:在交换之前,必须对数组(不包括最后一个元素)进行排序(通过前交换,或最初)
排序算法,假设值为conecutive:找到从1开始的连续(按值)元素的最长排序子序列:
3 1 5 2 4
依次交换所有较高元素:
1 5 2 4 3
1 5 2 3 4
1 2 3 4 5
要在O(n)中查找交换次数,请查找从1开始的连续元素的最长排序子序列的长度:
然后交换次数=输入的长度 - 它最长的排序子序列。
另一种解决方案(O(n ^ 2))如果输入不是1..n的排列:
又一个解决方案(O(n log n)),假设有独特的元素:
如果您不想复制输入数组,请在最后一步之前按oldPos排序。
答案 3 :(得分:2)
这可以在O(n log n)中完成。
首先找到数组中的最小元素。现在,找到此元素之前出现的最大元素。请拨打此max_left
。您必须为数组的min元素之前的所有元素调用swap()
。
现在,找到min元素右侧最长的增加子序列,以及应跳过值大于max_left的元素的约束。
所需的互换数量为size(array) - size(LIS)
。
例如,考虑数组
7 8 9 1 2 5 11 18
数组中的最小元素是1.因此我们在最小元素之前找到最大值。
7 8 9 | 1 2 5 11 18
max_left = 9
现在,使用元素<找到min右侧的LIS。 9 LIS = 1,2,5
交换次数= 8 - 3 = 5
在max element为null的情况下,即。,min是第一个元素,找到数组的LIS,所需的答案是size(array)-size(LIS)
例如
2 5 4 3
max_left为null。 LIS是2 3
swaps = size(array) - size(LIS)= 4 - 2 = 2
答案 4 :(得分:2)
这是python中最小交换次数的代码
def find_cycles(array):
cycles = []
remaining = set(array)
while remaining:
j = i = remaining.pop()
cycle = [i]
while True:
j = array[j]
if j == i:
break
array.append(j)
remaining.remove(j)
cycles.append(cycle)
return cycles
def minimum_swaps(seq):
return sum(len(cycle) - 1 for cycle in find_cycles(seq))
答案 5 :(得分:1)
@all,由@Itay karo和@NPE提供的公认解决方案完全错误因为它没有考虑交换元素的未来排序......
许多测试用例失败,例如:
3 1 2 5 4
正确输出: 4
但他们的代码输出为 3 ...
说明:3 1 2 5 4 ---> 1 2 5 4 3 ---> 1 2 4 3 5 ---> 1 2 3 5 4 ---> 1 2 3 4 5
PS:我因为声誉低而无法在那里发表评论答案 6 :(得分:1)
int numSwaps(int arr[], int length) {
bool sorted = false;
int swaps = 0;
while(!sorted) {
int inversions = 0;
int t1pos,t2pos,t3pos,t4pos = 0;
for (int i = 1;i < length; ++i)
{
if(arr[i] < arr[i-1]){
if(inversions){
tie(t3pos,t4pos) = make_tuple(i-1, i);
}
else tie(t1pos, t2pos) = make_tuple(i-1, i);
inversions++;
}
if(inversions == 2)
break;
}
if(!inversions){
sorted = true;
}
else if(inversions == 1) {
swaps++;
int temp = arr[t2pos];
arr[t2pos] = arr[t1pos];
arr[t1pos] = temp;
}
else{
swaps++;
if(arr[t4pos] < arr[t2pos]){
int temp = arr[t1pos];
arr[t1pos] = arr[t4pos];
arr[t4pos] = temp;
}
else{
int temp = arr[t2pos];
arr[t2pos] = arr[t1pos];
arr[t1pos] = temp;
}
}
}
return swaps;
}
此代码返回对数组进行排序所需的最小交换次数。
例如,A [] = [7,3,4,1]通过交换1和7,得到[1,3,4,7]。 类似地B [] = [1,2,6,4,8,7,9]。我们先用4交换6,所以,B [] - &gt; [1,2,4,6,8,7,9]。然后是7和8.所以 - &gt; [1,2,4,6,7,8,9]
算法在O中运行(其中索引i处的值&lt;索引i-1处的值)~O(N)的对数。
答案 7 :(得分:1)
编写一个非常简单的JavaScript程序对数组进行排序并查找交换次数:
function findSwaps(){
let arr = [4, 3, 1, 2];
let swap = 0
var n = arr.length
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
if (arr[i] > arr[j]) {
arr[i] = arr[i] + arr[j];
arr[j] = arr[i] - arr[j];
arr[i] = arr[i] - arr[j]
swap = swap + 1
}
}
}
console.log(arr);
console.log(swap)
}
答案 8 :(得分:1)
在C#中,Hear是我的解决方案,用于解决使数组变短所需的最小交换次数 当时我们只能交换2个元素(在任何索引位置)。
public class MinimumSwaps2
{
public static void minimumSwapsMain(int[] arr)
{
Dictionary<int, int> dic = new Dictionary<int, int>();
Dictionary<int, int> reverseDIc = new Dictionary<int, int>();
int temp = 0;
int indx = 0;
//find the maximum number from the array
int maxno = FindMaxNo(arr);
if (maxno == arr.Length)
{
for (int i = 1; i <= arr.Length; i++)
{
dic[i] = arr[indx];
reverseDIc.Add(arr[indx], i);
indx++;
}
}
else
{
for (int i = 1; i <= arr.Length; i++)
{
if (arr.Contains(i))
{
dic[i] = arr[indx];
reverseDIc.Add(arr[indx], i);
indx++;
}
}
}
int counter = FindMinSwaps(dic, reverseDIc, maxno);
}
static int FindMaxNo(int[] arr)
{
int maxNO = 0;
for (int i = 0; i < arr.Length; i++)
{
if (maxNO < arr[i])
{
maxNO = arr[i];
}
}
return maxNO;
}
static int FindMinSwaps(Dictionary<int, int> dic, Dictionary<int, int> reverseDIc, int maxno)
{
int counter = 0;
int temp = 0;
for (int i = 1; i <= maxno; i++)
{
if (dic.ContainsKey(i))
{
if (dic[i] != i)
{
counter++;
var myKey1 = reverseDIc[i];
temp = dic[i];
dic[i] = dic[myKey1];
dic[myKey1] = temp;
reverseDIc[temp] = reverseDIc[i];
reverseDIc[i] = i;
}
}
}
return counter;
}
}
答案 9 :(得分:1)
for(int count = 1; count<=length; count++)
{
tempSwap=0; //it will count swaps per iteration
for(int i=0; i<length-1; i++)
if(a[i]>a[i+1])
{
swap(a[i],a[i+1]);
tempSwap++;
}
if(tempSwap!=0) //check if array is already sorted!
swap += tempSwap;
else
break;
}
System.out.println(swaps);
答案 10 :(得分:1)
这是一个O(n)解决方案,适用于所有输入:
static int minimumSwaps(int[] arr) {
int swap=0;
boolean visited[]=new boolean[arr.length];
for(int i=0;i<arr.length;i++){
int j=i,cycle=0;
while(!visited[j]){
visited[j]=true;
j=arr[j]-1;
cycle++;
}
if(cycle!=0)
swap+=cycle-1;
}
return swap;
}
}
答案 11 :(得分:1)
def minimumSwaps(arr):
swaps = 0
'''
first sort the given array to determine the correct indexes
of its elements
'''
temp = sorted(arr)
# compare unsorted array with the sorted one
for i in range(len(arr)):
'''
if ith element in the given array is not at the correct index
then swap it with the correct index, since we know the correct
index because of sorting.
'''
if arr[i] != temp[i]:
swaps += 1
a = arr[i]
arr[arr.index(temp[i])] = a
arr[i] = temp[i]
return swaps
答案 12 :(得分:1)
如果您发现以下情况需要删除并附加数组中的元素,我认为可以在O(N)中解决此问题:
然后,这仅是确定需要删除和附加的元素。这是代码:
static int minMoves(int arr[], int n) {
if (arr.length == 0) return 0;
boolean[] willBeMoved = new boolean[n]; // keep track of elements to be removed and appended
int min = arr[n - 1]; // keep track of the minimum
for (int i = n - 1; i >= 0; i--) { // traverse the array from the right
if (arr[i] < min) min = arr[i]; // found a new min
else if (arr[i] > min) { // arr[i] has a smaller element to the right, so it will need to be moved at some point
willBeMoved[i] = true;
}
}
int minToBeMoved = -1; // keep track of the minimum element to be removed and appended
int result = 0; // the answer
for (int i = 0; i < n; i++) { // traverse the array from the left
if (minToBeMoved == -1 && !willBeMoved[i]) continue; // find the first element to be moved
if (minToBeMoved == -1) minToBeMoved = i;
if (arr[i] > arr[minToBeMoved]) { // because a smaller value will be moved to the end, arr[i] will also have to be moved at some point
willBeMoved[i] = true;
} else if (arr[i] < arr[minToBeMoved] && willBeMoved[i]) { // keep track of the min value to be moved
minToBeMoved = i;
}
if (willBeMoved[i]) result++; // increment
}
return result;
}
它使用O(N)空间。
答案 13 :(得分:0)
int temp = 0, swaps = 0;
for (int i = 0; i < arr.length;) {
if (arr[i] != i + 1){
// System.out.println("Swapping --"+arr[arr[i] - 1] +" AND -- "+arr[i]);
temp = arr[arr[i] - 1];
arr[arr[i] - 1] = arr[i];
arr[i] = temp;
++swaps;
} else
++i;
// System.out.println("value at position -- "+ i +" is set to -- "+ arr[i]);
}
return swaps;
这是我找到的最优化的答案。很简单。您可能会通过循环看一下。感谢黑客级别的达里尔。
答案 14 :(得分:0)
O(1)空间和O(N)(〜2 * N)个解决方案,假设min元素为1,并且数组包含从1到N-1的所有数字,且没有重复值。其中N是数组长度。
int minimumSwaps(int[] a) {
int swaps = 0;
int i = 0;
while(i < a.length) {
int position = a[i] - 1;
if(position != i) {
int temp = a[position];
a[position] = a[i];
a[i] = temp;
swaps++;
} else {
i++;
}
}
return swaps;
}