我已经编写了一个代码,可以根据Java中数组元素的频率对数组进行排序。我需要更好的代码或伪代码(没有收集框架)。请提供链接或代码帮助。
public class SortByFreq1 {
public static void main(String[] args) {
int arr[] = { 2, 5, 2, 8, 5, 6, 8, 8, 0, -8 };
int nArr[] = new int[arr.length];
Map<Integer,Integer> map = new HashMap<Integer, Integer>();
Map<Integer,Integer> sortmap = new HashMap<Integer, Integer>();
ArrayList<Integer> arrList = new ArrayList<Integer>();
for (int i = 0; i < arr.length; i++) {
arrList.add(arr[i]);
}
Set<Integer> set = new HashSet<Integer>(arrList);
for (Integer i : set) {
map.put(i, Collections.frequency(arrList, i));
}
// System.out.println(map.keySet());
// sort map by value
Set<Entry<Integer,Integer>> valList=map.entrySet();
ArrayList<Entry<Integer, Integer>> tempLst = new ArrayList<Map.Entry<Integer, Integer>>(valList);
Collections.sort(tempLst, new Comparator<Entry<Integer, Integer>>() {
@Override
public int compare(Entry<Integer, Integer> o1, Entry<Integer, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
int k = 0;
for (Entry<Integer, Integer> entry : tempLst) {
int no = entry.getKey();
int noOfTimes = entry.getValue();
int i = 0;
while (i < noOfTimes) {
nArr[k++] = no;
i++;
}
}
for (int i = 0; i < nArr.length; i++)
System.out.print(nArr[i] + " ");
}
}
答案 0 :(得分:1)
其背后的逻辑与Counting Sort十分相似。
注意:我们不来修改传入的数组。
有两种不同的方法,它们的时间和空间复杂度几乎相同。
上面提到的k是数组中不同数字的数量。
使用Stream
也许可以使过程更加简洁,尽管OP并没有要求这样做:
/**
* 1. count the frequency and sort the entry based on the frequency while using LinkedHashMap to retain the order;
* 2. fill up the new array based on the frequency while traversing the LinkedHashMap;
* @param arr
* @return
*/
private static int[] sortByCounting(int[] arr) {
Map<Integer, Long> countMap = Arrays.stream(arr).boxed()
.collect(Collectors.groupingBy(Integer::intValue, Collectors.counting()))
.entrySet().stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldV, newV) -> oldV, LinkedHashMap::new));
int[] newArr = new int[arr.length];
int i = 0;
for (Map.Entry<Integer, Long> entry : countMap.entrySet()) {
Arrays.fill(newArr, i, i += entry.getValue().intValue(), entry.getKey());
}
return newArr;
}
由于我们无法使用内置收集方法,因此我们必须记录该数字的计数。
本能地,我们可以引入一个自定义对,以将number
及其相关的frequency
(或我们可以说的count
)记录为我们的自定义方法。
private static int[] sortByPlainCounting(int[] arr) {
if (arr.length < 1) throw new IllegalArgumentException("Array cannot be empty");
MyPair[] pairs = prepareMyPairs(arr);
Arrays.sort(pairs, Comparator.comparing(MyPair::getCount).reversed());
int[] newArr = new int[arr.length];
int i = 0;
for (MyPair pair : pairs) {
Arrays.fill(newArr, i, i += pair.count, pair.key);
}
return newArr;
}
static class MyPair {
int key;
int count;
public MyPair(int theKey) {
this.key = theKey;
this.count = 1;
}
public void inc() {
this.count++;
}
public int getCount() {
return this.count;
}
}
static MyPair[] prepareMyPairs(int[] arr) {
Integer[] tmpArr = Arrays.stream(arr).boxed().toArray(Integer[]::new);
Arrays.sort(tmpArr, Comparator.reverseOrder());
int count = 1;
int prev = tmpArr[0];
for (int i = 1; i < tmpArr.length; i++) {
if (tmpArr[i] != prev) {
prev = tmpArr[i];
count++;
}
}
MyPair[] pairs = new MyPair[count];
int k = 0;
for (int i = 0; i < tmpArr.length; i++) {
if (pairs[k] == null) {
pairs[k] = new MyPair(tmpArr[i]);
} else {
if (pairs[k].key == tmpArr[i]) {
pairs[k].inc();
} else {
k++; i--;
}
}
}
return pairs;
}
进行最终比较,我们可以证明:
public static void main(String[] args) {
int N = 10_000 + new Random().nextInt(100);
Long start;
List<Long> list0 = new ArrayList<>();
List<Long> list1 = new ArrayList<>();
for (int i = 0; i < 100; ++i) {
int[] arr = RandomGenerator.generateArrays(N, N, N / 10, N / 5, false);
start = System.nanoTime();
int[] arr0 = sortByCounting(arr);
list0.add(System.nanoTime() - start);
start = System.nanoTime();
int[] arr1 = sortByPlainCounting(arr);
list1.add(System.nanoTime() - start);
System.out.println(isFrequencyEqual(arr0, arr1));
}
System.out.println("Collection time cost: " + list0.stream().collect(Collectors.summarizingLong(Long::valueOf)));
System.out.println("Custom time cost: " + list1.stream().collect(Collectors.summarizingLong(Long::valueOf)));
}
private static boolean isFrequencyEqual(int[] arr0, int[] arr1) {
Map<Integer, Long> countMap0 = getCountMap(arr0);
Map<Integer, Long> countMap1 = getCountMap(arr1);
boolean isEqual = countMap0.entrySet().size() == countMap1.entrySet().size();
if (!isEqual) return false;
isEqual = countMap0.values().containsAll(countMap1.values()) &&
countMap1.values().containsAll(countMap0.values());
if (!isEqual) return false;
List<Long> countList0 = countMap0.values().stream().collect(Collectors.toList());
List<Long> countList1 = countMap1.values().stream().collect(Collectors.toList());
for (int i = 0; i < countList0.size(); i++) {
if (countList1.get(i) != countList0.get(i)) return false;
}
return true;
}
private static Map<Integer, Long> getCountMap(int[] arr) {
return Arrays.stream(arr).boxed()
.collect(Collectors.groupingBy(Integer::intValue, Collectors.counting()))
.entrySet().stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldV, newV) -> oldV, LinkedHashMap::new));
}
helper util方法:
public static int[] generateArrays(int minSize, int maxSize, int low, int high, boolean isUnique) {
Random random = new Random(System.currentTimeMillis());
int N = random.nextInt(maxSize - minSize + 1) + minSize;
if (isUnique) {
Set<Integer> intSet = new HashSet<>();
while (intSet.size() < N) {
intSet.add(random.nextInt(high - low) + low);
}
return intSet.stream().mapToInt(Integer::intValue).toArray();
} else {
int[] arr = new int[N];
for (int i = 0; i < N; ++i) {
arr[i] = random.nextInt(high - low) + low;
}
return arr;
}
}
测试输出:
Sorted by frequency: true
// ... another 98 same output
Sorted by frequency: true
Collection time cost: LongSummaryStatistics{count=100, sum=273531781, min=466684, average=2735317.810000, max=131741520}
Custom time cost: LongSummaryStatistics{count=100, sum=366417748, min=1733417, average=3664177.480000, max=27617114}
答案 1 :(得分:1)
可以使用信鸽排序在O(n)中完成。伪代码:
counts = new HashMap<Item, int>(),;
foreach item in elements:
counts[item] += 1;
buckets = new List<Item>[elements.length+1];
foreach item in counts:
buckets[counts[item]].Append(item)
for i from 1 to elements.length:
bucket = buckets[i]; /* edit: looping over i instead over bucket */
for item in bucket:
/* edit: if the out has to preserve the original number of elements
such as [1,5,5,0,1,9,1] will print
9,0,5,5,1,1,1 instead of 9,0,5,1, then the next line
has to be repeated i times*/
System.out.println(item)
edit:通过实现哈希表和链接列表,无需收集框架即可编写相同的内容:
class Node {
public Node next;
public int value;
};
log2count = Math.ceil(Math.log(elements.length) / Math.log(2));
hashSize = (int) Math.Round(Math.Pow(2, log2count) * 2);
/* countsQuadraticProbing[i] is 0 if the hash entry is empty,
otherwise it contains the frequency of the element in
elementsQuadraticProbing[i].
Note that quadratic probing over a hash table of size 2**k,
and probing of (0.5 i + 0.5 i**2) is guaranteed to find an empty
entry if the hash table is not full.
*/
countsQuadraticProbing = new int[hashSize];
elementsQuadraticProbing = new int[hashSize];
foreach item in elements:
for i from 0 to hashSize-1:
index = (item + (i * (i + 1) / 2)) % hashSize;
if countsQuadraticProbing[index] == 0:
countsQuadraticProbing[index] = 1;
elementsQuadraticProbing[index] = item;
break;
if elementsQuadraticProbing[index] == item:
countsQuadraticProbing[index]++;
break;
buckets = new Node[elements.length+1];
for i from 0 to hashSize-1:
count = countsQuadraticProbing[index];
if count != 0:
Node topNode = new Node();
topNode.next = buckets[count];
topNode.value = elementsQuadraticProbing[i];
buckets[count] = topNode;
/* there are O(N) buckets, total of elements in all buckets O(N),
overall complexity of the nested loop O(N)
*/
for i from 1 to elements.length:
node = buckets[i] /* edit: using i for iteration */
while node != null:
/* edit: if the out has to preserve the original number of elements
such as [1,5,5,0,1,9,1] will print
9,0,5,5,1,1,1 instead of 9,0,5,1, then the next line
has to be repeated i times*/
System.out.println(node.value);
node = node.next;
答案 2 :(得分:0)
您的解决方案更好,但是由于不使用任何集合,因此可能会很大。
1。对列表进行排序 2.获取每个元素的频率 3.创建一个新的arraylist /数组并存储频率较高的元素到频率较低的元素。
获取频率
class CountFrequencies
{
// Function to find counts of all elements present in
// arr[0..n-1]. The array elements must be range from
// 1 to n
void findCounts(int arr[], int n)
{
// Traverse all array elements
int i = 0;
while (i < n)
{
// If this element is already processed,
// then nothing to do
if (arr[i] <= 0)
{
i++;
continue;
}
// Find index corresponding to this element
// For example, index for 5 is 4
int elementIndex = arr[i] - 1;
// If the elementIndex has an element that is not
// processed yet, then first store that element
// to arr[i] so that we don't loose anything.
if (arr[elementIndex] > 0)
{
arr[i] = arr[elementIndex];
// After storing arr[elementIndex], change it
// to store initial count of 'arr[i]'
arr[elementIndex] = -1;
}
else
{
// If this is NOT first occurrence of arr[i],
// then increment its count.
arr[elementIndex]--;
// And initialize arr[i] as 0 means the element
// 'i+1' is not seen so far
arr[i] = 0;
i++;
}
}
System.out.println("Below are counts of all elements");
for (int j = 0; j < n; j++)
System.out.println(j+1 + "->" + Math.abs(arr[j]));
}
以上代码应为您提供输出:
1 -> 3
2 -> 0
3 -> 2
4 -> 0
5 -> 2
6 -> 0
7 -> 2
8 -> 0
9 -> 2
10 -> 0
11 -> 0
答案 3 :(得分:0)
我很好奇为什么您不能使用旧的冒泡排序,而只是自定义 Bubble ?在最坏的情况下,时间复杂度将为O(n * n),空间复杂度将为O(3n):)
纯数组的实现将类似于:
private static void bubbleSortByOccurrences(int[] arr) {
int[][] counter = new int[2][arr.length];
int counterIndex = -1;
for (int value : arr) {
int idx = 0;
for (; idx <= counterIndex; idx++) {
if (counter[0][idx] == value) {
counter[1][idx]++;
while (idx > 0 && counter[1][idx] > counter[1][idx-1]) {
int temp = counter[1][idx];
counter[0][idx] = counter[0][idx-1];
counter[1][idx] = counter[1][idx-1];
counter[0][idx-1] = value;
counter[1][idx-1] = temp;
idx--;
}
break;
}
}
if (idx > counterIndex) {
counter[0][idx] = value;
counter[1][idx]++;
counterIndex = idx;
}
}
fillArrayBackwards(arr, counter, counterIndex);
}
private static void fillArrayBackwards(int[] buf, int[][] counter, int counterIndex) {
for (int i = counterIndex, j = buf.length - 1; i >=0; i--) {
for (int k = 0; k < counter[1][i]; k++) {
buf[j--] = counter[0][i];
}
}
}
使用Bubble类实现的相同算法如下所示:
private static void bubbleSortByOccurrences(int[] arr) {
Bubble head = null;
for (int value : arr) {
if (head == null) {
head = new Bubble(value);
} else {
Bubble currentHead = null;
Bubble current = head;
for (; current != null && !(current.getValue() == value); current = current.getTail()) {
currentHead = current;
}
if (current == null) {
current = new Bubble(value);
current.setTail(head);
head = current;
} else {
current.incrementOccurrences();
while (current.getTail() != null && current.getOccurrences() > current.getTail().getOccurrences()) {
Bubble currentTail = current.getTail();
current.setTail(currentTail.getTail());
currentTail.setTail(current);
if (currentHead != null) {
currentHead.setTail(currentTail);
currentHead = currentTail;
} else {
head = currentTail;
}
}
}
}
}
fillArrayBackwards(arr, head);
}
private static void fillArrayBackwards(int[] buf, Bubble head) {
int i = buf.length - 1;
for (Bubble current = head; current != null; current = current.getTail()) {
for (int j = 0; j < current.getOccurrences(); j++) {
buf[i--] = current.getValue();
}
}
}
自定义气泡如下:
class Bubble {
private int value;
private int occurrences;
private Bubble tail;
public Bubble(int value) {
this.value = value;
this.occurrences = 1;
}
public int getValue() {
return value;
}
public int getOccurrences() {
return occurrences;
}
public void incrementOccurrences() {
this.occurrences++;
}
public Bubble getTail() {
return tail;
}
public void setTail(Bubble tail) {
this.tail = tail;
}
}