2.3.15 #
解答 #
事实上只需要修改快速排序的切分方法,分两次进行切分。
首先选第一个螺母作为枢轴,找到对应的螺丝($O(n)$)放到第一位,对螺丝数组进行切分。
然后再用找到的螺丝对螺母数组进行切分。
螺母类,实现了对螺丝类的 IComparable 接口
public class Nut<T> : IComparable<Bolt<T>> where T : IComparable<T>
{
/// <summary>
/// 螺母的值。
/// </summary>
public T Value { get; set; }
/// <summary>
/// 螺母的构造函数。
/// </summary>
/// <param name="value">螺母的值。</param>
public Nut(T value) => Value = value;
/// <summary>
/// 比较方法,螺母只能和螺丝比较。
/// </summary>
/// <param name="other">需要比较的螺丝。</param>
/// <returns></returns>
public int CompareTo(Bolt<T> other)
{
return Value.CompareTo(other.Value);
}
}
类似的有螺丝类。
public class Bolt<T> : IComparable<Nut<T>> where T : IComparable<T>
{
/// <summary>
/// 螺丝的值。
/// </summary>
public T Value { get; set; }
/// <summary>
/// 螺丝的默认构造函数。
/// </summary>
/// <param name="value">螺丝的值。</param>
public Bolt(T value) => Value = value;
/// <summary>
/// 比较方法,螺丝只能和螺母比较。
/// </summary>
/// <param name="other">需要比较的螺母。</param>
/// <returns></returns>
public int CompareTo(Nut<T> other)
{
return Value.CompareTo(other.Value);
}
}
代码 #
修改后的排序方法。
public class BoltsAndNuts
{
private readonly Random _random = new();
/// <summary>
/// 对螺丝和螺母排序。
/// </summary>
/// <typeparam name="T">需要排序的元素类型。</typeparam>
/// <param name="bolts">螺母数组。</param>
/// <param name="nuts">螺丝数组。</param>
public void Sort<T>(Bolt<T>[] bolts, Nut<T>[] nuts) where T : IComparable<T>
{
if (bolts.Length != nuts.Length)
throw new ArgumentException("数组长度必须一致");
Shuffle(bolts);
Shuffle(nuts);
Sort(bolts, nuts, 0, bolts.Length - 1);
}
/// <summary>
/// 对螺丝和螺母排序。
/// </summary>
/// <typeparam name="T">需要排序的元素类型。</typeparam>
/// <param name="bolts">螺母数组。</param>
/// <param name="nuts">螺丝数组。</param>
/// <param name="lo">起始下标。</param>
/// <param name="hi">终止下标。</param>
public void Sort<T>(Bolt<T>[] bolts, Nut<T>[] nuts, int lo, int hi) where T : IComparable<T>
{
if (hi <= lo)
return;
var j = Partition(bolts, nuts, lo, hi);
Sort(bolts, nuts, lo, j - 1);
Sort(bolts, nuts, j + 1, hi);
}
/// <summary>
/// 对数组进行切分。
/// </summary>
/// <typeparam name="T">需要排序的数组类型。</typeparam>
/// <param name="bolts">螺母数组。</param>
/// <param name="nuts">螺丝数组。</param>
/// <param name="lo">起始下标。</param>
/// <param name="hi">终止下标。</param>
/// <returns>切分位置。</returns>
private int Partition<T>(Bolt<T>[] bolts, Nut<T>[] nuts, int lo, int hi) where T : IComparable<T>
{
int i = lo, j = hi + 1;
var pivotB = bolts[lo];
// 找到对应螺丝
for (var k = lo; k <= hi; k++)
{
if (nuts[k].CompareTo(pivotB) == 0)
{
Exch(nuts, k, lo);
break;
}
}
// 先用螺母去套螺丝
while (true)
{
while (nuts[++i].CompareTo(pivotB) < 0)
if (i == hi)
break;
while (pivotB.CompareTo(nuts[--j]) < 0)
if (j == lo)
break;
if (i >= j)
break;
Exch(nuts, i, j);
}
Exch(nuts, lo, j);
// 再用螺丝去比较螺母
var pivotN = nuts[j];
i = lo;
j = hi + 1;
while (true)
{
while (bolts[++i].CompareTo(pivotN) < 0)
if (i == hi)
break;
while (pivotN.CompareTo(bolts[--j]) < 0)
if (j == lo)
break;
if (i >= j)
break;
Exch(bolts, i, j);
}
Exch(bolts, lo, j);
return j;
}
/// <summary>
/// 打乱数组。
/// </summary>
/// <typeparam name="T">需要打乱的数组类型。</typeparam>
/// <param name="a">需要打乱的数组。</param>
private void Shuffle<T>(T[] a)
{
for (var i = 0; i < a.Length; i++)
{
var r = i + _random.Next(a.Length - i);
var temp = a[i];
a[i] = a[r];
a[r] = temp;
}
}
/// <summary>
/// 交换两个元素。
/// </summary>
/// <typeparam name="T">元素类型。</typeparam>
/// <param name="a">需要交换的数组。</param>
/// <param name="lo">需要交换的第一个元素。</param>
/// <param name="hi">需要交换的第二个元素。</param>
private void Exch<T>(T[] a, int lo, int hi)
{
var t = a[lo];
a[lo] = a[hi];
a[hi] = t;
}
}
另请参阅 #
下面这个网站给出了这道题的解法,还给出了另一种确定性算法(非随机的算法)的论文链接。 Matching Nuts and Bolts - Solution