【问题标题】:How to arrange an array in decreasing order of frequency of each number?如何按每个数字的频率降序排列数组?
【发布时间】: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 之间,则输入数组中第一个出现的数字将首先出现在输出数组中。

【问题讨论】:

    标签: algorithm arrays


    【解决方案1】:

    我想我会这样做:

    使用键值数据结构,其中数字本身是键,出现次数和首次出现索引是值。

    现在遍历所有数字。如果数字未知(在数据结构中),添加它,记住当前索引以及 1 作为计数。否则,增加计数。

    现在按照出现次数(递减)和出现索引(递增)对数据结构内容进行排序,并输出结果(使用出现次数重复次数)。

    使用的处理空间

    【讨论】:

    • +1: 在C++ 中它会给出:std::map<Number, std::pair<Count, Index> >。你能提供一个代码示例吗? (例如在 Python 中,因为它通常是一个正式的算法)
    • 我想出了与此相同的答案,并在我的帖子中用 C# 实现:stackoverflow.com/questions/2523883/…
    【解决方案2】:

    我能想到两种解决方案:

    首先计算频率并存储第一个索引,然后根据该索引进行排序

    这实现起来很简单,在最坏的情况下使用线性 (O(N)) 额外内存并且需要 O(N log N) 时间。

    使用某种priority queue 一次性完成所有操作,其中“优先级”是频率计数(在您通过输入时发生变化),以及第一次出现以打破关系的索引

    这一次完成所有事情,但我想不出比其他解决方案有什么优势。它仍然需要 O(N) 额外的内存,并且还需要 O(N log N) 时间。此外,它需要更复杂的数据结构。

    【讨论】:

    • 我的原始答案使用了稳定的排序,这就是为什么我有不正确的模式 ...13,6,13,6... 而不是 ...13,13,6,6。 .. 需要的是按频率排序,然后按列表中第一次出现的索引。我的回答是通过将两个部分都放入一个元组中,然后元组的自然顺序处理其余部分。
    • @gnibbler, andras:很好地指出。现在我可以想到另一种可以消除这种需求的方法:计数排序的简单变体:en.wikipedia.org/wiki/Counting_sort
    • @andras:这个保证足以保证订单。看看堆排序算法 (en.wikipedia.org/wiki/Heapsort)。它通过将所有内容放在堆中进行排序,然后反复删除堆的根(即最小值)。
    • 谢谢 - 但我认为这里有一个误解:我的意思是你声称在 single pass 中这样做是错误的。您必须进行第二次通过才能对优先级队列中的项目进行实际排序 - 就像您在第一次通过 heapify 后在 heapsort 中执行的操作一样。
    【解决方案3】:

    Python2.7Python3.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的返回值排序

    【讨论】:

    • 在 management1.0 中说(“实习生:将此数字列表按频率降序排序并放在我的桌子上”)
    • Rajendra,这是 Martinho 回答中第一种方法的实现
    • @Rajendra:你的算法就在那里。只要仔细看代码:先计数,然后排序,排序键为计数(c.get)。
    • @gnibbler: 我没有运行代码(这里没有 python :( )但是你自己的结果是错误的...... [..., 13, 6, 13, ...]?是这是一个错字,还是真正的结果?
    • @Lucero,Martinho,错过了关于关系的部分。现已修复
    【解决方案4】:

    您可以使用冒泡排序类型的算法一次性完成,在该算法中记录前一个值的计数,并在找到更多其他数字时交换一串数字。

    但是 - 作为第一步,您应该做一个 2pass 解决方案,使用 std::map/pair 来存储数字,或者如果您事先被告知值的范围,请使用一个简单的数组。

    【讨论】:

      【解决方案5】:

      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();
          }
      }
      

      【讨论】:

        【解决方案6】:

        对数组进行排序,并在你的排序函数中为 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) 打印输入

        为了提高效率,请在对数组进行排序之前预先计算计数和索引。

        【讨论】:

        • 为了更有效地使用 key 函数而不是 cmp。 cmp 在 Python3 中消失了
        【解决方案7】:
        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);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-06-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多