按 C 中元素出现频率的降序对数组进行排序

Posted

技术标签:

【中文标题】按 C 中元素出现频率的降序对数组进行排序【英文标题】:Sort the array in decreasing order of frequency of occurrence of elements in C 【发布时间】:2013-10-08 10:13:44 【问题描述】:

问题是根据元素出现的频率对数组进行排序。例如,如果输入数组是

    2, 3, 2, 4, 5, 12, 2, 3, 3, 3, 12 

然后将数组修改为:

    3, 3, 3, 3, 2, 2, 2, 12, 12, 4, 5 

我为此编写了代码,它工作正常,但它占用了大量空间并且具有非常高的复杂性。

我对这个解决方案和我为此申请的逻辑不满意。任何人都可以帮助优化此代码或提供更好的逻辑吗?

我的代码是:

#define _CRT_SECURE_NO_WARNINGS // this line to work code in visual studio
#include <stdio.h>

int main() 
    /*
     * n = number of integer
     * i = loop variable
     * j = inner loop variable
     * c = number of distinct input
     * buf = temprary storage for input value
     * k = possibility of frequency of any no.
     */

    int n, i, j, c = 0, buf, k;
    int b; //act as flag
    int arr[100] =  0 ;
    int stack[200] =  0 ;
    int top = -1;
    printf("Enter the size of array(integer between 1-100):");
    scanf("%d", &n);
    n *= 2;

    printf("----------Enter the elements in the array----------\n\n");

    for (i = 0; i < n; i += 2) 
        b = 0;
        printf("Enter the element:");
        scanf("%d", &buf);
        for (j = 0; j <= i; j += 2) 
            if (arr[j] == buf) 
                arr[j + 1]++;
                b = 1;
                   
        
        if (b == 0) 
            c++;
            arr[c * 2 - 2] = buf;
            arr[c * 2 - 1]++;
        
    

    for (i = 0; i < c * 2; i++)
        printf("%d ", arr[i]);

    //input done in form of (element,times of occurence i.e. frequency),to print array, write this outside of comment: 
    //for (i = 0; i < c * 2; i++) printf("%d ", arr[i]);

    for (k = 1; k < n / 2; k++)    //checking for possible frequencies
        for (j = c * 2 - 1; j > 0; j -= 2) 
            //locations(index) to check in array for frequency
            //left to right, so with same frequency no.,which occurred first will push in last.
            if (arr[j] == k)
                stack[++top] = j; //pushing(index of frequency) into stack in increasing order of frequency     
        
    

    //to print stack, write this outside of comment:
    //printf("\nstack\n");
    //for (i = top; i > -1; i--) printf("%d ",stack[i]);

    //printing of elements in there decreasing order of frequency(pop from stack)
    //we have to print element, number of times of its frequency

    printf("\n\n----------Output array in sorted order of there frequency----------\n");
    for (top; top > -1; top--)         
        for (j = arr[stack[top]]; j > 0; j--)
            printf("%d ", arr[stack[top] - 1]);
    

【问题讨论】:

您是否仅限于C?如果C++是允许的,在哪里可以使用std::mapqsort,15行代码就可以搞定 阅读:Sort elements by frequency | Set 2 是的,因为我根本不懂 c++...但是您可以为其他人推荐 c++..bt 我肯定无法理解.. @Nitkt 您可以从此答案How to arrange an array in decreasing order of frequency of each number? 中选择一种技术 @mvp 我尝试使用 std::map 和 pair 并且不能在不到 17 行的时间内完成 :) 【参考方案1】:

按值对数组进行排序; RLE 结果,将 equals 的每个跨度转换为一对元素和跨度的长度(您可以使用辅助数组来支持第二个组件);按第二个组件降序对对进行排序;这就是你的结果。全部在 O(n log n) 时间和 O(n) 额外空间内。

【讨论】:

【参考方案2】:

我找到了一种优雅的方式来执行这种排序,如果 O(N2) 和平均复杂度 O(N .log(N)).

该方法使用以下步骤:

按值的递增顺序对数组进行排序。为此,我使用 qsort 和一个简单的比较功能。 扫描数组以查找最长的重复值序列。 如果此序列不在开头,请将值移动到位并在开头创建序列。 从上一步结束重复扫描过程,直到不再有任何重复序列。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int int_cmp(const void *p1, const void *p2) 
    int i1 = *(const int *)p1;
    int i2 = *(const int *)p2;
    return (i1 > i2) - (i1 < i2);


void print_array(const char *msg, const int *a, int n) 
    printf("%s: ", msg);
    for (int i = 0; i < n; i++)
        printf("%d%c", a[i], " \n"[i == n - 1]);


int main(int argc, char *argv[]) 
    int N = argc > 1 ? atoi(argv[1]) : 200;
    int *array;

    if (N <= 0 || (array = calloc(N, sizeof(*array))) == NULL)
        return 1;

    srand(N);
    for (int i = 0; i < N; i++) 
        unsigned int x = rand();
        array[i] = x * x % 10;
    

    print_array("unsorted", array, N);
    qsort(array, N, sizeof(int), int_cmp);
    print_array("  sorted", array, N);
    /* sort by decrasing frequency (assuming N > 0) */
    for (int i = 0;;) 
        /* find the most repeated sequence in [i..N-1] */
        int rep = array[i];
        int n = 1, j, k;
        for (j = k = i + 1; j < N; j++) 
            if (array[j] == array[j - n]) 
                rep = array[j];
                k = j + 1;
                n++;
            
        
        if (n == 1) 
            /* no more duplicates, f-sort completed */
            break;
        
        i += n;
        if (k > i) 
            /* shift the repeated sequence in place */
            while (k-- > i) 
                array[k] = array[k - n];
            
            while (n-- > 0) 
                array[k--] = rep;
            
        
    
    print_array("f-sorted", array, N);
    free(array);
    return 0;

【讨论】:

【参考方案3】:

这是一个使用qsort 对值进行排序以轻松计算频率并通过降低频率对结果频率表进行排序的实现。当两个值具有相同的频率时,我们按递增值排序。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int cmp_int(const void *p1, const void *p2) 
    return *(const int *)p1 - *(const int *)p2;


typedef struct 
    int val;
    int cnt;
 freq;

int cmp_freq(const void *p1, const void *p2) 
    const freq *pf1 = (const freq *)p1;
    const freq *pf2 = (const freq *)p2;
    if(pf1->cnt == pf2->cnt)
        return pf1->val - pf2->val;
    return pf2->cnt - pf1->cnt;


void frequencySort(int tbl[], int n) 
    // sort values in ascending order
    qsort(tbl, n, sizeof(int), cmp_int);

    // fill frequency table with frequencies
    int nFreq = 0;
    freq *freqTbl = malloc(n*sizeof(freq));
    int val = tbl[0];
    int cnt = 1;
    for(int i = 1; i < n; i++) 
        if(tbl[i] != val) 
            freqTbl[nFreq].cnt = cnt;
            freqTbl[nFreq].val = val;
            nFreq++;
            val = tbl[i];
            cnt = 1;
         else 
            cnt++;
        
    
    freqTbl[nFreq].cnt = cnt;
    freqTbl[nFreq].val = val;
    nFreq++;

    // sort by frequencies
    qsort(freqTbl, nFreq, sizeof(freq), cmp_freq);    

    // refill tbl by frequencies
    int m = 0;
    for(int i = 0; i < nFreq; i++)
        for(int j = 0; j < freqTbl[i].cnt; j++)
            tbl[m++] = freqTbl[i].val;

    free(freqTbl);


int main(int argc, char *argv[])

    int n = argc > 1 ? atoi(argv[1]) : 200;
    int *tbl;
    if (n <= 0 || (tbl = malloc(n * sizeof(int))) == NULL)
        return 1;
    srand(time(NULL));
    for (int i = 0; i < n; i++)
        tbl[i] = abs(rand()) % 10;

    printf("[%d", tbl[0]);
    for (int i = 1; i < n; i++)
        printf(",%d", tbl[i]);
    printf("]\n");

    frequencySort(tbl, n);

    printf("[%d", tbl[0]);
    for (int i = 1; i < n; i++)
        printf(",%d", tbl[i]);
    printf("]\n");

    free(tbl);
    return 0;

【讨论】:

请注意,两个整数的减法适用于小数字(代码仅测试 0..9 范围内的数字,即“小数字”的缩影),但会遇到如果数据中同时存在大的正数和大的负数,则会出现整数溢出问题。在cmp_freq() 中,一个选项可能是使用:return (pf1-&gt;val &gt; pf2-&gt;val) - (pf1-&gt;val &lt; pf2-&gt;val);cmp_freq() 中的另一个比较和cmp_int() 中的一个类似。【参考方案4】:

我用一些新的方法和逻辑非常简单有效地解决了这个问题。

def func(val):
    for key, value in dict1.items():
         if val == value:
             return key
res=[]        
for _ in range(int(input())):
    n=int(input())
    lst=list(map(int,input().split()))
    dict1=
    lst.sort()
    lst2=[]
    for i in lst:
        dict1[i]=lst.count(i)
        lst2.append(lst.count(i))
    lst2.sort()
    lst2.reverse()
    s=''
    for i in lst2:
        k=func(i)
        s=s+((str(k)+" ")*i)
        dict1[k]=0
    s1=s.replace('None',"")
    s2=s1.replace("  ","")
    res.append(s2)
for i in res:
    print(i)

【讨论】:

【参考方案5】:

您可以从 bucket sort 的修改版本开始,但在创建存储桶列表后中途停止。

我做了这个,灵感来自桶排序。它最薄弱的环节是快速排序,但我可以修改它以使用桶排序。我估计长度为 n 且最大值为 m 的数组 A 的复杂度为 O(m + n log n),如果使用桶排序而不是 qsort 进行修改,它将降至 O(m+n)

typedef struct 
    int bucket;
    int index;
 element;

int compare(const void *a, const void *b)

    element *A = (element *) a;
    element *B = (element *) b;
    return(A->bucket < B->bucket);


void sortByFreq(int * arr, int len)

    int arrMax=findMax(arr, len);  // O(len)
    element x[arrMax+1];
    for(int i=0; i<=arrMax; i++)    // O(arrMax)
        x[i].bucket=0;
        x[i].index=i;
    
    for(int i=0; i<len; i++)   // O(len)
       x[arr[i]].bucket++;
    qsort(x, arrMax+1, sizeof(element), compare);  //O(len*log(len))

    int k=0;
    for(int i=0; i<=arrMax; i++)  // O(arrMax + len)
        for(int j=0; j<x[i].bucket; j++)
            arr[k++]=x[i].index;

【讨论】:

我认为如果要排序的数组的所有元素都是负数(或者稍有不同,如果任何元素为负数),或者整数的大小是巨大(例如 1-20 亿),因为数组定义 element x[arrMax + 1]; @JonathanLeffler 是的,除非您有大量内存,否则它会遇到非常大的数字问题,这是正确的。但是,只需添加最小数字的偏移量,就可以很容易地对其进行修改以处理负数。【参考方案6】:
    创建一个二进制搜索树,并在创建 BST 时保持计数,即同一 BST 中每个到来的元素的频率。如果使用自平衡 BST,此步骤可能需要 O(nLogn) 时间。 执行 BST 的中序遍历并将每个元素和每个元素的计数存储在辅助数组中。让我们将辅助数组称为“count[]”。请注意,此数组的每个元素都是元素和频率对。这一步需要 O(n) 时间。 根据元素的频率对“count[]”进行排序。如果使用 O(nLogn) 排序算法,则此步骤需要 O(nLogn) 时间。 遍历排序数组“count[]”。对于每个元素 x,打印它的“freq”次,其中“freq”是 x 的频率。这一步需要 O(n) 时间。

如果我们使用 O(nLogn) 排序算法并使用带有 O(Logn) 插入操作的自平衡 BST,算法的整体时间复杂度可以最小 O(nLogn)。

Geeks for Geeks

【讨论】:

【参考方案7】:
#include<stdio.h>
#include<malloc.h>
int* freq_sort_array(int*,int);
int main()

  int a[10]=7,0,0,5,0,0,0,0,0,0;     /*input array*/
  int *b,i;
  printf("Input Array\n");
  for(i=0;i<10;i++)
  printf("%d ",a[i]);
  b=freq_sort_array(a,10);
  printf("\nOutput array\n");
  for(i=0;i<10;i++)
  printf("%d ",b[i]);


                                       /*Function for sorting array based on frequency*/
int* freq_sort_array(int *a,int len)

  int i,j,temp,count,k=0,s=0,t=0;
  int *b=(int*)malloc(len*sizeof(int));
  int *c=(int*)malloc(len*sizeof(int));
  for(i=0;i<len;i++)
  
      for(j=i+1;j<len;j++)
      
          if(a[j]==a[i])
          
            temp=a[j];
            for(j;j>i+1;j--)
            
             a[j]=a[j-1];
            
            a[++i]=temp;
          
      
  
  for(i=0;i<len;i++)
  
      a[j]=a[i];
      count=1;
      if(i!=len-1)
      
          while(a[++i]==a[j]&& i<len)
            count++;
          i=i-1;
      
      b[k]=a[j];
      c[k++]=count;
  
  for(i=1;i<k;i++)
  
      for(j=0;j<k-i;j++)
      
          if(c[j]<c[j+1])
          
              c[j]=c[j]+c[j+1]-(c[j+1]=c[j]);
              b[j]=b[j]+b[j+1]-(b[j+1]=b[j]);
          
      
  
  for(i=0;i<k;i++)
  
      for(j=0;j<c[i];j++)
        a[s++]=b[i];
  
 return a;

【讨论】:

使用&lt;stdlib.h&gt;(不是&lt;malloc.h&gt;)声明malloc()等,除非您明确使用&lt;malloc.h&gt;提供的额外功能(此代码没有)。 我的编译器反对 c[j]=c[j]+c[j+1]-(c[j+1]=c[j]);b[j]=b[j]+b[j+1]-(b[j+1]=b[j]); 行,因为(例如)代码分配给 c[j+1] 以及使用它,并且未定义处理顺序(没有序列点使其正常工作)。该代码还会泄漏内存。它同时分配bc(并且不检查分配是否成功)并且在返回之前不释放它们。我不清楚这些可疑的任务在做什么——我不能推荐这个代码。它可能适用于某些系统,但并不可靠。

以上是关于按 C 中元素出现频率的降序对数组进行排序的主要内容,如果未能解决你的问题,请参考以下文章

如何按第一位数字的降序对整数数组进行排序? [关闭]

按特定键的降序对字典列表进行排序[重复]

如何根据php中日期列的降序对mysql结果数组进行排序?

如何按降序对 JSON 数组元素进行排序? [复制]

按降序对数组进行排序的更好方法

按特定索引降序对数组进行排序[重复]