容器的一些基本操作:
import java.util.*;
class Person implements Comparable<Person>{
String name;
int age;
public Person (String name,int age) {
this.name = name;
this.age = age;
}
public Person(String name2, double d) {
// TODO Auto-generated constructor stub
}
//要通过容器对插入的对象实例进行排序,必须在类的定义中实现比较器,且实现对应的接口
public int compareTo(Person o) {
return this.age - o.age;
}
}
public class treeMapOrHashMap {
public static void main(String[] args) {
//https://www.cnblogs.com/yzssoft/p/7127894.html
TreeMap<String,Double> map11 = new TreeMap<>();
map11.put("ccc",89.0); //必须双引号,单引号不行
map11.put("kkk", 92.0);
map11.put("aaa",28.0);
System.out.println("map11"+map11);//容器是可以直接打印的,不用一个一个输出
TreeSet<String> map12 = new TreeSet<>();
map12.add("ccc"); //必须双引号,单引号不行
map12.add("kkk");
map12.add("aaa"); //上面是put
System.out.println("map12"+map12);//容器是可以直接打印的,不用一个一个输出
//说明treeMap是放键值对,treeSet是只能放一个数据对象(一般放字符串或者数字)
//treeMap是按照键值进行升序排序的,treeSet也是默认排序的
//如果要保存数据对象,则一般使用ArrayList,并且实现对应的接口,这样才知道怎么排序的
ArrayList<Person> map22 = new ArrayList<Person>();
//自己定义的数据对象,底层不知道怎么比,必须在定义的时候实现比较的借口
map22.add(new Person("ccc",89)); //必须双引号,单引号不行
map22.add(new Person("kkk", 92));
map22.add(new Person("aaa",28));
//还的调用下排序
Collections.sort(map22);
System.out.println("ArrayList排序后:");
for(Person ss : map22) {
System.out.println("name: "+ss.name+"age: "+ss.age);
}
}
}
寻找最小的k个数:
import java.util.*;
public class findKofMin {
public ArrayList<Integer> solve(int[] arr,int k){
ArrayList<Integer> list = new ArrayList<Integer>();
if (arr.length < k ) return list;
//使用大根堆来维护最小的k个数,通过TreeSet来代替大根堆,因为其是红黑树,
//默认按照升序排列。如果用优先队列,还要自己实现比较器,且相同的数会保留下来。
TreeSet<Integer> s = new TreeSet<>();
for(int i = 0; i < arr.length; i++) {
if(s.size()<k)
s.add(arr[i]);
else if(arr[i]<s.last()) {
s.remove(s.last());
s.add(arr[i]);
}
}
//现在把红黑树的数转存到ArrayList中
Iterator<Integer> it = s.iterator();
while(it.hasNext()) {
list.add(it.next());
}
return list;
}
public static void main(String[] args) {
findKofMin aa = new findKofMin();
int [] arr = {1,5,6,0,3,16,8,37};
System.out.println(aa.solve(arr,4));
}
}
寻找中位数:使用优先队列
import java.util.*;
public class findMidlleNum {
int count = 0; //用来判断数据流吐出来的数是奇数还是偶数
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
//PriorityQueue优先队列底层是二叉小根堆实现的,所以弹出队列头部的数据是队列中最小的数据
//上一道题寻找数组中的最小k个数,是用大根堆()通过treeSet实现(因为本身按照升序排列)
//这里的使用优先队列是因为这里的数可能重复,若用treeSet就不合适,所以用优先队列
//且本身优先队列就是通过小根堆实现的。此外,为了实现大小根堆,通过在其中加个比较器即可
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(15, //容量
new Comparator<Integer>() {
public int compare(Integer o1,Integer o2){
return o2 - o1;
//本来是o1过了o2,现在倒过来减,所以是优先弹出最大值,从而实现大根堆。
}
});
/**
* 剑指offer上的大体思路是:
* 插入数据流:
* 1.一个大根堆(堆顶元素比此堆中其他元素大),一个小根堆(堆顶元素比此堆中其他元素小)
* 2.中位数左边的数一定比它小,右边的数一定 比它大。所以左边用大根堆找最大值比中位数小,右边用小根堆找最小值比中位数大。
* 3.注意两个堆的大小不能超过1,所以只能一次放一个堆,且是交替放。
* 4.因为先有小的再有大的,所以设定偶数放小根堆,奇数放大根堆。就拿第一次放数来看谁放谁。
* 5.放的时候要保证小根堆的最小值比大根堆的最大值要大(即保证中位数的后半段比前半段都大),所以新进来的数放大根堆时
* 一定是先放到小根堆找到最小值,然后弹出放到大根堆。若新进来的数要放小根堆也是同样的道理,
* 先放到大根堆中,弹出堆顶再放到小根堆中。
*
* 6.取中位数:若总数是偶数一定是两个根堆的平均值,若是奇数一定是小根堆中的堆顶。
* */
public void Insert(int num) {
if(count % 2 == 0) {
//说明要放后半段了,即小根堆,先进大根堆再弹出进小根堆
maxHeap.add(num); //add 和offer方法都一样,只不过对容错的处理方式不同,一个是报错,一个返回false
minHeap.add(maxHeap.poll());//poll和remove的区别同上
}
else {
//说明进中位数的前半段(大根堆,因为找最大值),先进小根堆,弹出再进大根堆。
minHeap.add(num);
maxHeap.add(minHeap.poll());
}
count++;
}
public Double getMedian() {
if(count%2==0) return new Double(minHeap.peek()+maxHeap.peek())/2.0;
else return new Double(minHeap.peek());
}
public static void main(String[] args) {
findMidlleNum aa = new findMidlleNum();
aa.Insert(15);
aa.Insert(5);
aa.Insert(10);
aa.getMedian();
System.out.println(aa.getMedian());
}
}
红黑树(平衡性较高),AVL树(平衡性高度严格,最高),’SB’树(平衡性适中,现在用的最多)都是搜索二叉树,只是他们的平衡性不同。越平衡的树,其越接近二分查找,每次丢弃一个数,所以查找效率高。但是一味地追求高平衡性会导致很多额外的调整操作,反而效率降低。平衡树的容器代表是TreeMap(基于红黑树实现),TreeSet是基于TreeMap修改得来,两者都是默认按照升序排列,一个是按照键值,一个是按照value。此外,hash也有类似的操作且速度很快,为什么发明这个呢?因为在一些反复的查找中,如得到 .firstkey()等操作中tree树的操作是优于hash函数的。
HashMap<String,Integer> hash = new HashMap<>();
HashSet<Integer> hash1 = new HashSet<>();
//和treemap和treeSet一样,一个只能存键值对,一个只能存单一的数。