容器的一些基本操作:
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一樣,一個只能存鍵值對,一個只能存單一的數。