【问题标题】:how to find complete sorting of elements by frequency?如何按频率找到元素的完整排序?
【发布时间】:2014-08-06 17:31:28
【问题描述】:

问题来了:

给定一个整数数组,根据元素的频率对数组进行排序。比如输入数组是{2, 3, 2, 4, 5, 12, 2, 3, 3, 3, 12},那么修改数组为{3, 3, 3, 3, 2, 2, 2、12、12、4、5}。如果 2 个数字具有相同的频率,则打印第一个出现的数字。

我知道如何做部分。这是我的方法。

我将创建一个结构,如下所示:

typedef struct node
{
  int index; // for storing the position of the number in the array.
  int count; // for storing the number of times the number appears
  int value; // for storing the actual value
} a[50];

我将创建一个由这些结构组成的数组,然后我将根据它们的计数通过排序算法对其进行排序。但是,我怎样才能确保如果两个元素的频率相同,那么应该出现具有较小索引值的数字?

【问题讨论】:

  • 您使用两个排序标准。首先应该检查的标准是出现的频率或数量。当频率相等时应检查的次要标准是index 数字。
  • 您需要为此编写自己的排序例程。自从我使用 C 语言以来已经有一段时间了——在 Java 中,我会编写一个函数来比较两个实例并报告更大的实例,然后我可以使用 Collections.sort。你可以在 C 中做类似的事情。
  • 您的问题已被提出并回答。 here is one way.
  • 我真的不明白你将如何使用这种方法来做到这一点,但如果你已经准备好了,可以考虑在结构中添加另一个整数变量,称之为@987654324 @ 与index 不同,它保持不变并保持初始索引的值。如果碰巧有平局,就打破平局。
  • @ryyker 这个答案并不能完全解决这个问题。虽然它可以,但经过一些体面的修改,它仍然缺少最重要部分的修复,即 3745736 要求他/她打破平局所需的部分。

标签: c arrays sorting


【解决方案1】:
#include <stdlib.h> // qsort, malloc, free
#include <stddef.h> // size_t
#include <stdio.h>  // printf

struct number
{
    const int * value;
    int         num_occurrences;
};

static void cmp_by_val(const struct number * a, const struct number * b)
{
    if (*a->value < *b->value)
        return -1;
    else if (*b->value < *a->value)
        return 1;
    else
        return 0;
}

static void cmp_by_occurrence_stable(const struct number * a, const struct number * b)
{
    if (a->num_occurrences < b->num_occurrences)
        return -1;
    else if (b->num_occurrences < a->num_occurrences)
        return 1;
    else if (a->value < b->value)
        return -1;
    else if (b->value < a->value)
        return 1;
    else
        return 0;
}

static struct number * sort_by_occurrence(const int * arr, size_t N)
{
    //
    // STEP 1: Convert the input
    //
    struct number * sort_arr = (struct number *)malloc(N * sizeof(struct number));
    if (! sort_arr) return NULL;
    for (int k = 0; k < N; ++k)
    {
        sort_arr[k].value = &arr[k];
        sort_arr[k].num_occurrences = 0;
    }
    //
    // STEP 2: Sort the input based on value
    //
    qsort(sort_arr, N, sizeof(struct number), cmp_by_val);
    //
    // STEP 3: Count occurrences
    //
    if (0 < N)
    {
        int cur_value = *sort_arr[0].value;
        int i = 0;
        for (j = 1; j < N; ++j)
        {
            if (*sort_arr[j].value != *sort_arr[i].value)
            {
                for (int k = i; k < j; ++k)
                    sort_arr[k].num_occurrences = j - i;
                i = j;
            }
        }
        for (; i < N; ++i)
            sort_arr[i].num_occurrences = N - i;
    }
    //
    // STEP 4: Sort based on occurrence count
    //
    qsort(sort_arr, N, sizeof(struct number), cmp_by_occurrence_stable);
    //
    // DONE
    //
    return sort_arr;
}

static void print_arr(const struct number * arr, size_t N)
{
    if (0 < N)
    {
        printf("%d", arr[0]->value);
        for (int k = 1; k < N; ++k)
            printf(", %d", arr[k]->value);
    }
    printf("\n");
}

int main(int argc, char ** argv)
{
    const int EXAMPLE_INPUT[11] = { 2, 3, 2, 4, 5, 12, 2, 3, 3, 3, 12 }; 
    struct number * sort_arr = sort_by_occurrence(EXAMPLE_INPUT, 11);
    if (sort_arr)
    {
        print_arr(sort_arr, 11);
        free(sort_arr);
    }
};

【讨论】:

  • qsort 不稳定,调用qsort(sort_arr, N, sizeof(struct number), cmp_by_val); 时如何保存索引?
  • 为什么有两种不同的功能?按出现排序和按值排序?
  • @AK_,注意原始数组中值的地址提供了一种推断它们原始顺序的方法。 struct number 使用指向值的指针,而不是值。因此,第二种排序产生的排序等同于由稳定排序确定的排序。
  • @user3745736,第一个排序用来统计元素的频率(num_occurrences)。然后第二个排序按频率排序。
  • @0xbe5077ed 我必须说这太棒了!您可以使用return a-&gt;value - b-&gt;value 减少行数吗?
【解决方案2】:

您可以创建一个数组来存储输入数组的频率(即 frequency[i] 是 input[i] 元素的频率)。之后,很容易对频率数组进行排序(使用稳定的算法)并对输入数组进行相同的更改(交换?)。

为了创建频率数组,您可以使用多种方法,一种简单且低效的方法是使用两个嵌套循环计算每个元素。我留下了更有效的替代方案供您想象。

注意:频率数组与结构节点中的计数字段具有相同的功能,但在单独的内存中。如果以后你不需要频率,我建议你使用分离的内存,因为你可以释放它。

【讨论】:

  • 非常昂贵的算法,您可以调整 qsort 以稳定查看@0xbe5077ed 代码..
  • 正如我所说,我创建频率阵列的方法效率低下,但可以改进。我的目的是给出对解决方案的理解,而不是给出代码。如果您使用 qsort(加上计数)来创建频率数组,并再次使用 qsort 进行频率排序,您将得到@0xbe5077ed 解决方案。
【解决方案3】:

看来问题在于对数组元素的频率使用了不稳定的排序算法。

  1. 根据频率对数组进行 qsort
  2. 再次根据仅具有相同频率的元素的索引对结果数组进行 qsort。

    • 这应该会给你一个 O(nLog) 的正确答案

我最小化了代码。明显的部分被省略了。

struct node
{
    int *val;
    int freq;
    // int index; <- we can do this by comparing &a->val with &b->val
};

int compare_byfreq(const int* a, const int* b)
{
    return a->freq - b->freq;
}
int compare_index(const int* a, const int* b)
{
    if( a->freq == b->freq)
    {
        return  a->val - b->val; //this can never be zero
    }
    //else we have different freq don't move elem
    return 0;
}

int main()
{
    int arr[] = {2, 3, 2, 4, 5, 12, 2, 3, 3, 3, 12};
    node *narray = (struct node*)malloc(sizeof(arr) * sizeof(node));

    // build the nodes-array
    for(int i =0; i < sizeof(arr); i++)
    {
        /* buid narray here, make sure you store the pointer to val and not the actual values */
    }

    qsort(narray, sizeof(arr), compare_byfreq);
    qsort(narray, sizeof(arr), compare_index);

    /*print narray*/

    return 0;
}

编辑:@0xbe5077ed 有一个有趣的想法。而不是比较索引比较您的值的地址! - 我刚刚重新编辑了代码

【讨论】:

    【解决方案4】:

    我现在正在尝试学习 Java,意识到这可能是一个很好的练习。在 Eclipse 中尝试并解决了这个问题。 Java 太可怕了,我又回到 C 来解决它,这里有一个解决方案,我会在展示后立即解释:

    #include <stdio.h>
    #include <malloc.h>
    
    typedef struct numbergroup {
        int firstencounteridx;
        int count;
        int thenumber;
    } Numbergroup;
    
    int firstoneissuperior( Numbergroup gr1, Numbergroup gr2 ) {
        return gr1.count > gr2.count ||   // don't mind the line-break, it's just to fit
        ( gr1.count == gr2.count && gr1.firstencounteridx < gr2.firstencounteridx );
    }
    
    void sortgroups( Numbergroup groups[], int amount ) {
        for ( int i = 1; i < amount; i++ ) {
            for ( int j = 0; j < amount - i; j++ ) {
                if ( firstoneissuperior( groups[j + 1], groups[j] ) ) {
                    Numbergroup temp = groups[j + 1];
                    groups[j + 1] = groups[j];
                    groups[j] = temp;
                }
            }
        }
    }
    
    int main( ) {
        int input[] = { 2, 3, 2, 4, 5, 12, 2, 3, 3, 3, 12 };
        Numbergroup * groups = NULL;
        int amountofgroups = 0;
    
        for ( int i = 0; i < ( sizeof input / sizeof * input ); i++ ) {
            int uniqueencounter = 1;
            for ( int j = 0; j < amountofgroups; j++ ) {
                if ( groups[j].thenumber == input[i] ) {
                    uniqueencounter = 0;
                    groups[j].count++;
                    break;
                }
            }
            if ( uniqueencounter ) {
                groups = realloc( groups, ( amountofgroups + 1 ) * sizeof * groups );
                groups[amountofgroups].firstencounteridx = i;
                groups[amountofgroups].count = 1;
                groups[amountofgroups].thenumber = input[i];
                amountofgroups++;
            }
        }
    
        sortgroups( groups, amountofgroups );
    
        for ( int i = 0; i < amountofgroups; i++ )
            for ( int j = 0; j < groups[i].count; j++ )
                printf( "%d ", groups[i].thenumber );
    
        free( groups );
    
        putchar( 10 );
        return 0;
    }
    

    让我先解释一下结构及其功能:它适用于每个唯一的数字。在您的示例中,它适用于2s、3s、4s、5s 和12s,各一个,总共 5 个。每一个都是存储:

    • 第一次遇到该号码的索引
    • 该号码的遭遇次数
    • 那个数字的值

    例如,对于12s,它应该存储:

    • firstencounteridx as 5,也就是前12的索引
    • count2
    • thenumber12

    第一个循环通常会这样做。每当遇到唯一数字时,它都会扩展 Numbergroups 组,并存储其索引;如果遇到已经有组的数字,则增加计数。

    然后发出排序,这只是冒泡排序。可能和传统的不一样,我没有记住。

    排序标准函数只是检查第一组的count 字段是否大于另一个;否则检查它们是否相同,并且第一组的 firstencounter 早于另一组;在这种情况下,它会将1 返回为真。这些是第一组被认为优于第二组的唯一可能方式。

    这是一种方法,还有其他方法。这只是一个建议,希望对您有所帮助,不仅仅是针对这种情况,而是总体而言。

    【讨论】:

      【解决方案5】:

      创建了一个地图并按值对地图进行排序。 O(nlogn) 时间和 O(n) 空间。

      import java.util.*;
      
      public class SortByFrequency {
          static void sortByFreq( int[] A ) {
      
              // 1. create map<number, its count>
              Map<Integer, Integer> map = new HashMap<>();
      
              for(int i = 0; i < A.length; i++) {
                  int key = A[i];
      
                  if( map.containsKey(key) ) {
                      Integer count = map.get(key);
                      count++;
                      map.put(key, count);
                  }
                  else {
                      map.put(key, 1);
                  }
              }
      
              // 2. sort map by value in desc. order 
              // used modified (for desc. order) MapUtil in http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java
              Map<Integer, Integer> map2= MapUtil.sortByValue(map);
      
      
              for(Map.Entry<Integer, Integer> entry : map2.entrySet() ) {
                  int num = entry.getKey();
                  int count = entry.getValue();
      
                  for(int i = 0; i < count; i++ ) {
                      System.out.print( num + " ");
                  }
              }
              System.out.println();
          }
      
          public static void main(String[] args ) {
              int[] A1 = {2, 3, 2, 4, 5, 12, 2, 3, 3, 3, 12};
              sortByFreq(A1);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-18
        • 2020-10-19
        • 2021-12-03
        • 1970-01-01
        • 2019-05-12
        • 1970-01-01
        • 1970-01-01
        • 2021-03-12
        相关资源
        最近更新 更多