如何按频率查找元素的完整排序?

时间:2014-06-16 20:06:05

标签: c arrays sorting

问题在于:

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

我知道如何部分地做到这一点。这是我的approcach。

我将创建一个类似于:

的结构
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];

我将创建这些结构的数组,然后我将根据它们的计数通过排序算法对其进行排序。但是,我如何确保如果两个元素的频率相同,那么该数字应该出现哪个指数值较小?

5 个答案:

答案 0 :(得分: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);
    }
};

答案 1 :(得分:0)

似乎问题是在数组元素的频率上使用不稳定排序算法。

  1. 根据freq
  2. 在数组上执行qsort
  3. 再次根据具有相同频率的元素的索引对结果数组执行qsort。

    • 这应该在O(nLog)
    • 中给出正确的答案
  4. 我最小化了代码。遗漏了明显的部分。

    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有一个有趣的想法。而不是比较索引比较您的值的地址! - 我刚刚重新编辑了

    的代码

答案 2 :(得分:0)

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

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

注意:频率数组与struct节点中的count字段具有相同的功能,但是在单独的内存中。如果您将来不需要频率,我建议您使用分开的内存,因为您可以释放它。

答案 3 :(得分:0)

我现在正在努力学习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;
}

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

  • 该号码第一次遭遇的索引
  • 该号码遭遇的数量
  • 该数字的值

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

  • firstencounteridx5,即前12
  • 的索引
  • count2
  • thenumber12

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

然后发出排序,这只是一个冒泡排序。可能与传统的不同,我没有任何记忆。

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

这是一种方法,可以有其他方法。这只是一个建议,我希望它可以帮助你,不仅仅是为了这个案例,而是一般。

答案 4 :(得分:0)

创建地图并按值对地图进行排序。 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);
    }
}