问题
I am trying to sort an array using the insertion sort algorithm. The array is filled with WordNode
elements that include a word
field (inputted from a text file) and a frequency
field (to measure the number of times the particular word appears in the text file). I have implemented the sort so that words are sorted by frequency (from lowest to highest), but I also want to sort alphabetically if frequencies are equal. How can I sort using two different criteria at the same time? Below is my sort code.
public static void sort(ArrayUnorderedList<WordNode> array) {
//create stacks for insertion sort
LinkedStack<WordNode> sorted = new LinkedStack<WordNode>();
LinkedStack<WordNode> temp = new LinkedStack<WordNode>();
//while the array has elements to be sorted
while(!array.isEmpty()) {
//remove current element from array
WordNode currentNode = array.removeFirst();
//while the sorted stack meets sorting criteria
while((!sorted.isEmpty()) && (sorted.peek().getFrequency() < currentNode.getFrequency())) {
//push elements to temp stack
temp.push(sorted.pop());
}
//push current element to sorted stack
sorted.push(currentNode);
//while the temp stack has elements to be replaced
while(!temp.isEmpty()) {
//push elements to sorted stack
sorted.push(temp.pop());
}
}
//replace sorted elements in array
while(!sorted.isEmpty()) {
array.addToRear(sorted.pop());
}
}
回答1:
AppClay's answer is absolutely correct, but if you are interested in "tidying it up", create a helper that implements Comparator.
class WordNodeComparator implements Comparator<WordNode> {
@Override
public int compare(WordNode lhs, WordNode rhs) {
int result = lhs.getFrequency() - rhs.getFrequency();
if (result == 0) {
return lhs.getWord().compareTo(rhs.getWord());
}
else {
return result;
}
}
}
Then you simply create an instance of it, and use it in your loop:
while((!sorted.isEmpty()) && (nodeComparator.compare(sorted.peek(), currentNode) < 0)
Not only does this make the code easier to read and test, it's now trivial to swap out different Comparator implementations as needed.
回答2:
Update this line:
while((!sorted.isEmpty()) && (sorted.peek().getFrequency() < currentNode.getFrequency())) {
to:
while((!sorted.isEmpty()) && (sorted.peek().getFrequency() < currentNode.getFrequency() || (sorted.peek().getFrequency() == currentNode.getFrequency() && sorted.peek().getWord().compareTo(currentNode.getWord()) < 0))) {
回答3:
Add this to your code:
int cmp = sorted.peek().getFrequency() - currentNode.getFrequency();
if (cmp == 0)
cmp = sorted.peek().getWord().compareTo(currentNode.getWord());
while((!sorted.isEmpty()) && cmp < 0) {
//push elements to temp stack
temp.push(sorted.pop());
cmp = sorted.peek().getFrequency() - currentNode.getFrequency();
if (cmp == 0)
cmp = sorted.peek().getWord().compareTo(currentNode.getWord());
}
I'm assuming that getFrequency()
returns an integer and that the actual word in a WordNode
is accessed by the getWord()
method. Using the above we compare first by frequency, and if both frequencies are equal then we compare alphabetically
EDIT :
A nicer solution, define this helper method:
private static boolean compare(LinkedStack<WordNode> sorted, WordNode current) {
int cmp = sorted.peek().getFrequency() - current.getFrequency();
if (cmp == 0)
cmp = sorted.peek().getWord().compareTo(current.getWord());
return cmp;
}
And then change the first inner loop in your code to this:
while (!sorted.isEmpty() && compare(sorted, currentNode) < 0) {
//push elements to temp stack
temp.push(sorted.pop());
}
回答4:
Use Guava lib:
public static List<WordNode> sort(List<WordNode> src){
List<WordNode> result = Lists.newArrayList(src);
Collections.sort(result, new Comparator<WordNode>(){
@Override public int compare(WordNode w1, WordNode w2) {
return ComparisonChain.start()
.compare(w1.frequency, w2.frequency)
.compare(w1.word , w2.word)
.result();
}});
return result;
}
来源:https://stackoverflow.com/questions/10134303/sorting-an-array-using-two-different-criteria