返回

Java选择排序:Comparator实现字符串末字符排序

java

Java 中使用 Comparator 实现选择排序(Selection Sort)并解决特定问题

最近在写一个程序,需要对字符串列表进行选择排序。程序接收 6 个字符串作为输入,然后按照字符串的最后一个字符对它们进行排序。但我写的代码出了点问题, 这篇文章就来讲讲我是怎么解决这个问题的。

问题

我的程序接收 6 个字符串输入, 应该输出按最后一个字符排序的字符串列表. 可是实际运行结果和期望的不一样。 下面是我尝试的代码和对应的输出:

代码:

import java.util.Arrays;
import java.util.Scanner;
import java.util.List;
import java.util.Comparator;

public class Exercise_20_21 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter 6 strings: ");
        // Step 1: proccess strings from input
        String data = input.next();
        String[] list = data.split(" ");
        for (int i = 0; i < list.length; i++) {
            System.out.print(list[i] + " ");
        }
        selectionSort(list, new Comparator<String>() {
            @Override
            public int compare(String w1, String w2) {
                if (w1.charAt(0) > w2.charAt(0)) {
                    return -1;
                } else if (w1.charAt(0) < w2.charAt(0)) {
                    return 1;
                } else {
                    return 0;
                }
            }
        });
        for (int i = 0; i < list.length; i++) {
            System.out.print(list[i] + " ");
        }
    }

    public static <E> void selectionSort(E[] list, Comparator<? super E> comparator) {
        Arrays.sort(list, comparator);
    }
}

期望输出:

Enter 6 strings: red blue green yellow orange pink
red blue orange pink green yellow

实际输出:

Enter 6 strings: red blue green yellow orange pink
red red

问题原因分析

看这输出结果,我发现问题可能出在这几个地方:

  1. 输入处理: input.next() 只读取了第一个字符串。遇到空格就停了, 后面的字符串根本没读进去。
  2. Comparator 的实现: Comparator 比较的是字符串的第一个字符,而不是最后一个字符。
  3. 排序算法: 虽然代码里写的是selectionSort, 但实际上调用的是 Arrays.sort(),这并不是选择排序,而是 Java 内置的排序算法(可能是快速排序或者归并排序)。

解决方案

针对上面分析的原因, 我逐个解决。

1. 修复输入处理

要读取所有输入的字符串,不能只用 input.next()。 应该用循环读取, 或者先用nextLine()读取整行,再进行分割。

修改后的代码:

Scanner input = new Scanner(System.in);
System.out.print("Enter 6 strings: ");
String[] list = new String[6];
for (int i = 0; i < 6; i++) {
    list[i] = input.next();
}

// 或者用 nextLine()

//Scanner input = new Scanner(System.in);
//System.out.print("Enter 6 strings: ");
//String data = input.nextLine();
//String[] list = data.split(" ");

这样就能保证读取到所有的输入字符串。

2. 修正 Comparator

要按最后一个字符排序,compare 方法里就要比较字符串的最后一个字符。

修改后的代码:

new Comparator<String>() {
    @Override
    public int compare(String w1, String w2) {
        char lastChar1 = w1.charAt(w1.length() - 1);
        char lastChar2 = w2.charAt(w2.length() - 1);
        if (lastChar1 > lastChar2) {
            return 1;
        } else if (lastChar1 < lastChar2) {
            return -1;
        } else {
            return 0;
        }
    }
}

这样, Comparator就能按字符串的最后一个字符进行比较。

3. 实现选择排序 (Selection Sort)

题目要求是实现选择排序, 所以得把Arrays.sort()换成自己实现的选择排序算法。

选择排序的原理:

选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

代码实现:

public static <E> void selectionSort(E[] list, Comparator<? super E> comparator) {
    int n = list.length;
    for (int i = 0; i < n - 1; i++) {
        // 找到未排序部分最小元素的索引
        int minIndex = i;
        for (int j = i + 1; j < n; j++) {
            if (comparator.compare(list[j], list[minIndex]) < 0) {
                minIndex = j;
            }
        }

        // 将最小元素与未排序部分的第一个元素交换
        if (minIndex != i) {
            E temp = list[i];
            list[i] = list[minIndex];
            list[minIndex] = temp;
        }
    }
}

这个 selectionSort 方法就实现了选择排序。每次循环都从未排序的部分里找出最小的元素, 然后和未排序部分的第一个元素交换。

4. 完整的修改后代码

把上面的修改都整合起来,完整的代码是这样的:

import java.util.Comparator;
import java.util.Scanner;

public class Exercise_20_21 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter 6 strings: ");
        String[] list = new String[6];
        for (int i = 0; i < 6; i++) {
            list[i] = input.next();
        }
        
        // 或者
        //String data = input.nextLine();
        //String[] list = data.split(" ");
        
        System.out.print("Input: ");
         for (int i = 0; i < list.length; i++) {
             System.out.print(list[i] + " ");
        }
        System.out.println();
        selectionSort(list, new Comparator<String>() {
            @Override
            public int compare(String w1, String w2) {
                char lastChar1 = w1.charAt(w1.length() - 1);
                char lastChar2 = w2.charAt(w2.length() - 1);
                if (lastChar1 > lastChar2) {
                    return 1;
                } else if (lastChar1 < lastChar2) {
                    return -1;
                } else {
                    return 0;
                }
            }
        });
        System.out.print("Output: ");
        for (int i = 0; i < list.length; i++) {
            System.out.print(list[i] + " ");
        }
    }

    public static <E> void selectionSort(E[] list, Comparator<? super E> comparator) {
        int n = list.length;
        for (int i = 0; i < n - 1; i++) {
            // 找到最小元素的索引
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                if (comparator.compare(list[j], list[minIndex]) < 0) {
                    minIndex = j;
                }
            }

            // 将最小元素与未排序部分的第一个元素交换
            if (minIndex != i) {
                E temp = list[i];
                list[i] = list[minIndex];
                list[minIndex] = temp;
            }
        }
    }
}

运行结果:

Enter 6 strings: red blue green yellow orange pink
Input: red blue green yellow orange pink 
Output: red blue orange pink green yellow 

这下,输入、排序逻辑都对了,结果也符合预期。

5. 代码优化和进阶技巧

虽然问题解决了,但还可以做些优化,提升代码质量。

  • Comparator 简化: 可以用 Comparator.comparingInt 来简化 Comparator 的创建。

    selectionSort(list, Comparator.comparingInt(s -> s.charAt(s.length() - 1)));
    

    更简洁, 也更容易理解。

  • 处理空字符串: 如果输入的字符串可能是空的, charAt 会出错. 可以加个判断.

    selectionSort(list, (s1, s2) -> {
      if (s1.isEmpty() && s2.isEmpty()) {
          return 0;
      }
      if (s1.isEmpty()) {
        return -1; // 空字符串排前面
      }
      if (s2.isEmpty()) {
        return 1; // 空字符串排前面
      }
    
     return Integer.compare(s1.charAt(s1.length() - 1), s2.charAt(s2.length()-1));
    
    });
    
    
  • 更灵活的排序规则: 如果除了按最后一个字符排序, 还想在最后一个字符相同的情况下按其他规则排序 (比如按字符串长度, 或者按字典序), 可以进一步扩展 Comparator.

       selectionSort(list, Comparator.comparingInt((String s) -> s.charAt(s.length() - 1))
               .thenComparing(String::length) // 如果最后一个字符一样,按长度排序
               .thenComparing(Comparator.naturalOrder())); // 如果长度也一样,按字典序
    

这样一来代码变得更灵活。
以上就是我对这个问题的解决过程和一些思考, 把一个一开始跑不通的小程序,逐步分析、调试,最终解决了问题。也让我对Java 里的 Comparator 和排序算法有了更深的理解。