【发布时间】:2011-01-31 19:28:22
【问题描述】:
输入:{5, 13, 6, 5, 13, 7, 8, 6, 5}
输出:{5, 5, 5, 13, 13, 6, 6, 7, 8}
问题是按照频率降序排列数组中的数字,保留它们出现的顺序。
如果存在平局,例如本例中的 13 和 6 之间,则输入数组中第一个出现的数字将首先出现在输出数组中。
【问题讨论】:
输入:{5, 13, 6, 5, 13, 7, 8, 6, 5}
输出:{5, 5, 5, 13, 13, 6, 6, 7, 8}
问题是按照频率降序排列数组中的数字,保留它们出现的顺序。
如果存在平局,例如本例中的 13 和 6 之间,则输入数组中第一个出现的数字将首先出现在输出数组中。
【问题讨论】:
我想我会这样做:
使用键值数据结构,其中数字本身是键,出现次数和首次出现索引是值。
现在遍历所有数字。如果数字未知(在数据结构中),添加它,记住当前索引以及 1 作为计数。否则,增加计数。
现在按照出现次数(递减)和出现索引(递增)对数据结构内容进行排序,并输出结果(使用出现次数重复次数)。
使用的处理空间
【讨论】:
C++ 中它会给出:std::map<Number, std::pair<Count, Index> >。你能提供一个代码示例吗? (例如在 Python 中,因为它通常是一个正式的算法)
我能想到两种解决方案:
这实现起来很简单,在最坏的情况下使用线性 (O(N)) 额外内存并且需要 O(N log N) 时间。
这一次完成所有事情,但我想不出比其他解决方案有什么优势。它仍然需要 O(N) 额外的内存,并且还需要 O(N log N) 时间。此外,它需要更复杂的数据结构。
【讨论】:
在 Python2.7 或 Python3.1
>>> from collections import Counter
>>> L=[5, 13, 6, 5, 13, 7, 8, 6, 5]
>>> c=Counter(L)
>>> def keyfunc(x):
... return (-c.get(x),L.index(x))
...
>>> sorted(L,key=keyfunc)
[5, 5, 5, 13, 13, 6, 6, 7, 8]
在Python2.6
中>>> from collections import defaultdict
>>> L=[5, 13, 6, 5, 13, 7, 8, 6, 5]
>>> c=defaultdict(int)
>>> for x in L:
... c[x]+=1
...
>>> def keyfunc(x):
... return (-c.get(x),L.index(x))
...
>>> sorted(L,key=keyfunc)
[5, 5, 5, 13, 13, 6, 6, 7, 8]
这是一个不使用任何库函数的版本(奇怪的约束)
>>> L=[5, 13, 6, 5, 13, 7, 8, 6, 5]
>>> c={}
>>> for x in L:
... c[x]=c.setdefault(x,0)+1
...
>>> def keyfunc(x):
... return (-c.get(x),L.index(x))
...
>>> sorted(L,key=keyfunc)
[5, 5, 5, 13, 13, 6, 6, 7, 8]
在每种情况下,keyfunc 都用于控制排序的顺序
keyfunc(5) returns (-3,0)
keyfunc(6) returns (-2,2)
keyfunc(7) returns (-1,5)
keyfunc(8) returns (-1,6)
keyfunc(13) returns (-2,1)
列表项按照keyfunc的返回值排序
【讨论】:
c.get)。
您可以使用冒泡排序类型的算法一次性完成,在该算法中记录前一个值的计数,并在找到更多其他数字时交换一串数字。
但是 - 作为第一步,您应该做一个 2pass 解决方案,使用 std::map/pair 来存储数字,或者如果您事先被告知值的范围,请使用一个简单的数组。
【讨论】:
C#。它采用最明显的方法:按频率和序数排序。
输出:5 5 5 13 13 6 6 7 8。
空间:O(n)。时间:O(n log n)。
class Program
{
class FreqAndOrdinal
{
public int Frequency;
public int Ordinal;
public FreqAndOrdinal(int freq, int ord)
{
this.Frequency = freq;
this.Ordinal = ord;
}
}
static int Compare(FreqAndOrdinal x, FreqAndOrdinal y)
{
int result = y.Frequency.CompareTo(x.Frequency);
return result == 0 ? x.Ordinal.CompareTo(y.Ordinal) : result;
}
static void Main(string[] args)
{
int[] nums = new int[] { 5, 13, 6, 5, 13, 7, 8, 6, 5 };
var freqLookup = new Dictionary<int, FreqAndOrdinal>(nums.Length);
for (int i = 0; i < nums.Length; i++)
{
FreqAndOrdinal tmp;
if (freqLookup.TryGetValue(nums[i], out tmp))
++tmp.Frequency;
else
freqLookup[nums[i]] = new FreqAndOrdinal(1, i);
}
Array.Sort(nums, (x,y) => Compare(freqLookup[x], freqLookup[y]));
for (int i = 0; i < nums.Length; i++)
{
Console.Write(" {0}", nums[i]);
}
Console.ReadKey();
}
}
【讨论】:
对数组进行排序,并在你的排序函数中为 x、y 排序:按 count(x) 与 count(y) 排序。如果它们相同,则按 index(x) 与 index(y) 排序
在python中:
输入 = [5, 13, 6, 5, 13, 7, 8, 6, 5] orig = 列表(输入) def cmp(x, y): 如果 (orig.count(y) - orig.count(x) != 0): 返回 orig.count(y) - orig.count(x) 返回 orig.index(x) - orig.index(y) 输入.排序(cmp) 打印输入为了提高效率,请在对数组进行排序之前预先计算计数和索引。
【讨论】:
private static void sortByFrequency(int[] a)
{
Map<Integer, Element> map = new HashMap<Integer, Element>();
for(int i=0; i<a.length; i++)
{
if(map.get(a[i]) == null)
{
map.put(a[i], new Element(i));
}
else
{
Element e = map.get(a[i]);
e.frequency++;
}
}
Set<Integer> set = map.keySet();
TreeSet<Element> treeSet = new TreeSet<Element>();
for(int i : set)
{
treeSet.add(map.get(i));
}
for(Element e : treeSet)
{
for(int i=0; i<e.frequency;i++)
{
System.out.println(a[e.index]);
}
}
}
private static class Element implements Comparable<Element>
{
private final int index;
private int frequency;
Element(int index)
{
this.index = index;
this.frequency = 1;
}
@Override
public int compareTo(Element o)
{
int k = o.frequency - this.frequency;
if(k != 0) return k;
else
{
return this.index - o.index;
}
}
}
public static void main(String[] args)
{
int[] a = {5, 13, 6, 5, 13, 7, 8, 6, 5};
sortByFrequency(a);
}
【讨论】: