java集合的算法

本文介绍了Java集合框架中的一些核心算法,包括排序、乱序、常用数据操作、查找、构成及极值查找。排序算法使用优化的归并排序,保证了稳定性;乱序算法用于随机打乱列表;常用数据操作如反转、覆写、复制和交换等;查找算法通过二分查找实现高效搜索;构成算法统计元素频率和检查集合不相交;极值查找则能找出集合中的最小和最大元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

集合算法

java的Collections类以静态方法的形式提供了一些多态算法。
这些算法绝大多数处理List实例,也有个别能处理任意集合的实例。下面我们简要的介绍下。

排序

sort算法对列表的元素按升序进行排序。它有两种处理形式:

  • 一种是接收一个列表,并根据其元素的自然顺序进行排序。
  • 第二种是接收一个列表,以及额外的一个Comparator对象,用该对象对元素进行排序。

sort方法使用优化后的归并排序(merge sort)算法,不仅快,而且稳定:

  • 快:它能保证运行的时间复杂度是O(nlog(n)),几乎和快排(quicksort)一样快。但是后者不是稳定排序且不能保证O(nlog(n ))的时间复杂度。
  • 稳定:他不会重排相等的元素,当你对同一个列表根据不同的属性进行二次排序时,这一点很重要。比如,某用户对收件箱的消息先根据日期排序,然后再根据发件人排序。用户的正常预期是给定发件人的信息仍能保持日期排序。只有第二次排序是稳定的才能保证这一点。

示例1: 将程序的参数按照字母顺序排序:

import java.util.*;

public class Sort {
    public static void main(String[] args) {
        List<String> list = Arrays.asList(args);
        Collections.sort(list);
        System.out.println(list);
    }
}

示例2: 下面的代码找出所有符合要求的同字母异序词,并以列表的形式保存词组,然后对列表进行排序,按照词组长度倒序输出。这里使用Comparator参数自定义排序规则来实现这个效果。它接收两个参数,一是词典文件路径,二是最小词组大小

词典文件:https://docs.oracle.com/javase/tutorial/collections/interfaces/examples/dictionary.txt

import java.util.*;
import java.io.*;

public class Anagrams {
    public static void main(String[] args) {
        int minGroupSize = Integer.parseInt(args[1]);

        Map<String, List<String>> m = new HashMap<>();

        try{
            Scanner s = new Scanner(new File(args[0]));
            while (s.hasNext()) {
                String word = s.next();
                String alpha = alphabetize(word);
                // 类似python字典的setdefault方法
                m.computeIfAbsent(alpha, k -> new ArrayList<>()).add(word);
            }
        } catch (IOException e) {
            System.out.println(e);
            System.exit(1);
        }

        // 找出词组长度满足要求的
        List<List<String>> candidates = new ArrayList<>();
        for(List<String> l : m.values()) {
            if (l.size() >= minGroupSize)
                candidates.add(l);
        }

        // 根据词组长度倒序排序
        Collections.sort(candidates, new Comparator<List<String>>() {
            public int compare(List<String> o1, List<String> o2) {
                return o2.size() - o1.size();
            }
        });

        for(List<String> l : candidates) {
            System.out.println(l.size() + ": " + l);
        }
    }

    private static String alphabetize(String s) {
        char[] letters = s.toCharArray();
        Arrays.sort(letters);
        return new String(letters);
    }
}

输出:

12: [apers, apres, asper, pares, parse, pears, prase, presa, rapes, reaps, spare, spear]
11: [alerts, alters, artels, estral, laster, ratels, salter, slater, staler, stelar, talers]
10: [least, setal, slate, stale, steal, stela, taels, tales, teals, tesla]
9: [estrin, inerts, insert, inters, niters, nitres, sinter, triens, trines]
9: [capers, crapes, escarp, pacers, parsec, recaps, scrape, secpar, spacer]
9: [palest, palets, pastel, petals, plates, pleats, septal, staple, tepals]
9: [anestri, antsier, nastier, ratines, retains, retinas, retsina, stainer, stearin]

乱序

schuffle算法和sort相反,它随机打乱列表。利用它可以实现洗牌,生成测试用例等。此操作有两种形式:

  • 一种是接收一个列表并使用默认等随机源
  • 另一种是调用时额外提供一个Random对象作为随机源。

以下示例打乱程序的参数:

import java.util.*;

public class Shuffle {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (String a : args)
            list.add(a);
        Collections.shuffle(list, new Random());
        System.out.println(list);
    }
}

以上也可以简化为:

import java.util.*;

public class Shuffle {
    public static void main(String[] args) {
        List<String> list = Arrays.asList(args);
        Collections.shuffle(list);  // //使用默认的随机源
        System.out.println(list);
        System.out.println(Arrays.toString(args));
    }
}

注意,这里利用Arrays类的静态工厂方法asList将数组处理为List视图。这个方法不会拷贝数组,对List视图的修改会写入数组,反之亦然。List视图不是通用的List实现,因为它没有实现addremove方法:数组不可调整大小。

常用数据操作

Collections类提供了5个常用的算法来处理列表对象的常用数据操作:

  • reverse 反转列表中元素的顺序
  • fill 用指定的值覆写列表的每个元素,可用户重新初始化一个列表
  • copy 接收两个参数,一个目标列表,一个源列表,将源列表的元素拷贝到目标列表并进行覆盖。目标列表长度至少要和源列表一样,如果目标列表更长,多余的元素不会受到影响。
  • swap 交换列表中指定位置的元素
  • addAll 将所有指定元素拷贝到集合中,要添加的元素可以单独指定,也可以作为一个数组

示例:

List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3,4));
Collections.reverse(list);
System.out.println(list); // [4, 3, 2, 1]

Collections.fill(list, 0);
System.out.println(list);  // [0, 0, 0, 0]

Collections.copy(list, new ArrayList<>(Arrays.asList(8, 9)));
System.out.println(list);  // [8, 9, 0, 0]

Collections.swap(list, 0, list.size() -1);
System.out.println(list);  // [0, 9, 0, 8]

Collections.addAll(list, 10, 11);
System.out.println(list);  // [0, 9, 0, 8, 10, 11]

查找

binarySearch二分查找算法从一个有序的列表中查找指定的元素。这个算法有两种形式:

  • 一种是接收一个列表和要搜索的元素。这种形式假定列表是有序的(按其元素的自然顺序升序排序)。
  • 第二种形式接收一个额外Comparator对象,并假定列表是有序的(根据指定的Comparator对象按升序排序)。注意,这里是告诉binarySearch,要查找的列表是根据指定的Comparator对象排序的,而不是让binarySearch先对列表作排序。

可以在调用binarySearch前,先调用sort对列表进行排序。
二分查找法的返回值是int类型,如果返回值>=0,说明找到了,且返回值是元素对应的索引。
如果小于0,说明没找到,返回值是(-(insertion_index) - 1),其中insertion_index是元素的预期插入位置的索引,采用这种形式是为了确保未找到时的返回值<0

下面演示二分查找的使用,并在查找不存在的情况下将元素插入列表中合适的位置:

List<Integer> list = new ArrayList<>(Arrays.asList(3,6,2,9,5,8,1,7));
Collections.sort(list);
int pos = Collections.binarySearch(list, 4); // -4
if (pos < 0)
    list.add(-pos-1, 4);

为了演示第二种形式,下面故意写啰嗦一点,和上面是等价的:

List<Integer> list = new ArrayList<>(Arrays.asList(3,6,2,9,5,8,1,7));

Comparator c = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1.compareTo(o2);
    }
};

Collections.sort(list, c); 
int pos = Collections.binarySearch(list, 4, c);
if (pos < 0)
    list.add(-pos-1, 4);

构成

frequency频率和disjoint不相交算法:

  • frequency 统计指定元素在集合中出现的次数
  • disjoint 判断两个集合是否不包含共同的元素
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,3));
int freq = Collections.frequency(list, 3);
System.out.println(freq); // 2

boolean isDisjoint1 = Collections.disjoint(list, new ArrayList<>(Arrays.asList(2, 3)));
boolean isDisjoint2 = Collections.disjoint(list, new ArrayList<>(Arrays.asList(4, 5)));
System.out.println(isDisjoint1);  // false
System.out.println(isDisjoint2);  // true

极值查找

  • min 查找集合中的最小元素
  • max 查找集合中的最大元素

以上两种方法都有两种形式,一种是只接收一个集合,默认按照元素的自然排序查找。第二种是额外接收一个Comparator对象,按照指定排序查找。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值