1. 程式人生 > >集合、二叉樹

集合、二叉樹

工廠 尋路算法 整數 作用 lse == 取值 得到 eset

回顧:

/**
 * 
 */
package com.qfedu.Day17.HomeWork;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class HomeWork {
    @SuppressWarnings("rawtypes")
    public static void main(String[] args) {
      /*已知有一個工人Worker類 屬性:姓名 年齡 工資, 行為 void work()
    a.在集合中添加三個工人對象,信息如下:
        zhang3  18   3000
        li4      25  3500
        wang5    22  3200
    b. 在li4之前插入一個工人 信息為: zhao6  24  3300
    c. 刪除wang5的信息
    d. 利用for循環遍歷,打印幾個中所有工人的信息
    e. 利用叠代器遍歷,對集合中的所有工人調用work方法
      
*/ List list = new ArrayList(); //a. list中的元素存儲的是地址 // 集合中只能存儲對象-->引用的地址 list.add(new Worker("zhang3",18,3000)); list.add(new Worker("li4",25,3500)); list.add(new Worker("wang5",22,3200)); System.out.println(list); //b. 之所以集合可以存儲任何數據類型--> 因為是父類Object
// 向上轉型 子類的引用賦值給了父類的對象 --> 只能調用父類的屬性和方法,不能調用子類特有和方法 for(int i = 0; i<list.size();i++) { if(((Worker)(list.get(i))).getName().equals("li4")) { list.add(i, new Worker("zhao6",24,3000)); break; } } System.out.println(list);
//c. for(int i = 0;i<list.size();i++) { if(((Worker)(list.get(i))).getName().equals("wang5")) { list.remove(i); break; } } /* * ps不使用下標的形式: 傳入一個王五的對象 * 重寫equals 和 hashcode * list.remove(new Worker("wang5",22,3200)); */ //d. for(Object obj : list) { System.out.println(obj); } //e. //1.獲取叠代器對象 Iterator it = list.iterator(); //遍歷集合 //2.判斷叠代器中是否存在下一個元素 hasNext // 獲取當前叠代器中對象next-->集合中的對象 while(it.hasNext()) { // it.next(); // it.next(); // //一次循環移動兩次 --> 一共就三個元素 ((Worker)(it.next())).work(); } } } /** * */ package com.qfedu.Day17.HomeWork; public class Worker { //對當前類進行描述 --> 對象的描述 //類的作用 --> 用來存儲描述對象的信息 //姓名 年齡 工資, 行為 void work() //屬性的:定義成員變量 private String name; private int age; private int money; //構造方法時可以私有化 --> 單利 // --> 簡單工廠 工具類 //構造方法 --> 無參 -->有參 //提供了成員變量快捷的賦值方式 public Worker() { //沒有參數 //super(); 調用父類的構造方法(無參) //這裏還可以使用一些調用語句(方法的) 不這樣做 //初始化數組 ,集合初始化 //給成員變量一些初始值(固定的) } public Worker(String name,int age,int money) { this.name = name; this.age = age; this.money = money; } //getter和setter方法 --> 對外提供訪問和修改操作 public String getName() { //獲取當前成員變量 return name; } public void setName(String name) {//修改當前成員變量 this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } //描述行為 --> 方法 public void work() { System.out.println(name+"工人在工作拿到薪水:"+money); } /* * 打印自定義對象的信息 */ @Override public String toString() { return name; } }


List集合:
ArrayList集合 : 數組實現 查詢和修改速度快 增加和刪除速度慢
LinkedList集合 : 鏈表 --> 棧 隊列(單向和雙向) 增加和刪除速度快 ,查詢和修改速度慢

Vector集合: 不在使用了 線程安全的 但是效率低 數組
ArrayList集合 線程不安全 但是效率高
Collections工具類可以將當前集合修改為線程安全

4中遍歷集合的方式
普通for 增強for 叠代器(標準叠代器 iterator 增強叠代器 listIterator) 枚舉叠代器(使用的Vector不在使用)
List集合:有序存儲(下標) 可以存儲重復的值


Set集合
簡述Hash(哈希表)
在一般的數組中,元素在數據中的引用位置是隨機的,元素的取值和與元素的位置之間不存在確定關系
因此,在數組中查找特定的值,需要把查找值和數組中的元素進行一一比較

如果元素的值(value)和在數組中索引的位置(index)有一個確定的關系(Hash)
公式: index = hash(value);
那麽麽對於給定的值,只要調用上述Hash(value)方法,可以找到對應的index,能找到對應的元素Value
表推測:
index = 元素值/10-1;
如果數組中元素的遏制和索引的位置存在對應的關系,這樣的數組就稱為哈希表(桶結構)

一般情況下,我們是不會把哈希碼(hashCode)作為元素在數組中所有的位置,哈希碼和元素之間的某種映射關系(Map)
哈希表裝滿了,會進行擴容,提供了一個加載因子 --> 0.75
若創建一個hash表提供的空間是16,也就是當前到到16*0.75那麽就進行擴容

hash表中是不允許存在相同對象的(值(元素)),提供equals和hashCode相同 --> index相同,不存在當前元素

/**
 * 
 */
package com.qfedu.Day17.HashSet;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class HashSetDemo {

    public static void main(String[] args) {
        //1.HashSet是Set接口的實現類
        //set是不允許存儲重復值,排重 (排除重復)
        Set set = new  HashSet();
        // HashSet set = new Hash();
        //1.向集合中添加元素
        set.add("1");
        set.add("1");
        set.add("1");
        set.add("1");
        set.add("1");
        set.add("1");
        set.add("1");
        set.add("1");
        System.out.println(set);
        List list = new ArrayList();
        list.add("2");
        list.add("2");
        list.add("2");
        list.add("2");
        list.add("2");
        list.add("2");
        list.add("2");
        list.add("2");
        list.add("2");
        System.out.println(list);
        set.addAll(list);//存儲的不是集合 是集合中元素
        System.out.println(set);
        
        //清空集合  內容清空 集合存在
        set.clear();
        //判斷集合是否為空 -->空沒有元素 true  反之false
        System.out.println(set.isEmpty());
        /*
         *  boolean contains(Object o) 
                 判斷集合中是否存在指定元素 存在true 反之false
             boolean containsAll(Collection<?> c) 
              判讀集合中是否存在指定集合中的元素 存在true 反之false 

         */
        
        //set同樣支持Iterator叠代器
        //但是:它只支持標準的叠代器 不支持listIterator
        //千萬不要在叠代器中使用集合自身的增加和刪除方法
        //不然就會出現異常
        
        //1.獲取叠代器對象
        Iterator it =  set.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        
        //set中刪除只有一種直接刪除方式
        //參數必須是一個對象
        //這個方法是有返回值的  true證明刪除成功
        //                   false證明刪除失敗-->集合中沒有這個對象
        set.remove("2");
        
        /*
         * 參考List
         * boolean removeAll(Collection<?> c) 
                           刪除傳入參數集合中元素 ,保留剩余元素
           boolean retainAll(Collection<?> c) 
                      保留傳入參數集合中的元素,刪除剩余元素 

         */
        //提供一個方法可以獲得集合中所有元素的個數
        int size = set.size();
        //set集合除了Iterator叠代器方式可以遍歷
        //同樣支持for循環遍歷 -->只支持增強for循環
        for(Object obj :set) {
            System.out.println(obj);
        }
        //ps:set並不支持使用循環的方式賦值
        //set集合可以轉變為數組
          Object[] obj = set.toArray();
        //需求:如下set集合中存儲了字符串1-9
          //    將集合轉換為數組
          //    打印數組中值
        //    將數組中的值存到一個新的set集合中

    }

}


hashSet集合
hash表-->hash算法是看不到的
底層原碼使用native這個關鍵字,是使用本地類庫的關鍵字
操作系統,java有一個方法這個方法的實現不是java提供而是通過本地c或C++類庫來完成計算
hash表插入和查找是很優秀
可以當hash表接近裝滿的時候,向數組一樣進行擴容,會將小表轉換為大表
加載因子:0.75 -->hash表的大小是16 也就是說當前 到大12的時候 就會擴容
HashSet使用的就是hash表的存儲方式
不能存儲重復值(hash表中是不能存相同元素),存儲方式是無序(無序不等於隨機)

如何使用HashSet集合 API
HashSet是有hash表支持,但是底層實現是HashMap
看HashSet包

/**
 * 
 */
package com.qfedu.Day17.HashSet.SelfClass;

import java.util.HashSet;
import java.util.Set;

class Person{
    private String name;
    private int age;
    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    //認定兩個對象是同一個對象
    //只要屬性完全相同即可
    /* 
     */
    @Override
    public boolean equals(Object obj) {
        Person other = (Person)obj;
        return this.name.equals(other.name) && this.age == other.age;
    }
}

class A{
    
    @Override
    public boolean equals(Object obj) {
        
        return true;
    }
}
class B{
    
    @Override
    public int hashCode() {
    
        return 1;
    }
}
class C{
    
    @Override
    public boolean equals(Object obj) {
        
        return true;
    }
    @Override
    public int hashCode() {
    
        return 2;
    }
}





public class HashSetDemo2 {
    public static void main(String[] args) {
        Person p1 = new Person("小明", 18);
        Person p2 = new Person("小明", 18);
        System.out.println("若輸出true證明當前對象和傳入對象是同一個對象:"+p1.equals(p2));
        
        //創建一個Set集合
        Set set = new HashSet();
        set.add(new A());
        set.add(new A());
        set.add(new B());
        set.add(new B());
        set.add(new C());
        set.add(new C());
        set.add(p1);
        set.add(p2);
        //Set集合是自動排重 --> 排除重復
        System.out.println(set);

    }

}

自定義類在Set集合中如何排重
自定義類所創建的對象,認為是同一個對象標準是什麽?
對象中的屬性是完全一致的,只要是屬性一致我們就認為是同一個對象set就需要幫組我們排除重復
若在set集合中存儲自定義類所創建的對象,若需要排重操作,就需要提供equals和hashcode重寫

在hashSet中如何判斷兩個對象是是否相等
1.兩個對象的equal比較結果必須是true,這說明是同一個對象
2.兩個對象的hashcode必須也是相等的
在這裏Hashcode值決定了對象所在哈希表中的位置

規定:重寫equals和hashcode的原則,通過equals判斷為是同一個對象,那麽我們就認為當前兩個對象地址值也需要是相同
重寫equals就必須重寫hashcode這樣可以達到對象相等地址相等目的

當向hashSet中添加對象的手,先判斷該對象和集合中對象的hashcode;
不等,直接把該對象存到hashcode值對應的指定位置
相等,在繼續判斷對象和集合對象中的equals是否相等,會出現桶掛載情況

hashCoed相同 equals為true 視為同一個對象
hashCode相同 equals 為false 非常的麻煩 將當前對象向鏈表一樣串聯起來

需求 創建一個Student類,提供屬性 姓名和年齡 行為隨意 重寫toString
創建一個set集合,向set集合中存入Student類的對象
張三 18
李四 20
王五 25
張三 18
李四 20
打印集合查看效果
總結:
1.無論何時只要是比較兩個自定義對象是否相等,那麽久必須重寫equals和hashcode
ps:使用系統生成即可 alt+shif+s --> create hashcode和 equals
2.將自定義對象存儲到set集合中,需要排重時,當前類就必須提供equals和hashcode重寫
不然set集合不會幫組我們完成排重
看SelfClass包

HashSet類是有一個子類的LinkedHashSet
LinkedHashSet也是Set接口的實現類
使用了鏈表和hash表雙重的方式來實現的
即可以保證唯一性,也可以使用鏈表的形式來記錄先後順序
LinkedHashSet沒有任何自己的特有方法,所有的使用方式仿照hashSet即可
方法參照Set接口中提供的方法即可

TreeSet --> 如何自定義比較(排序)
TreeSet也是Set接口的實現類,具備排重,排序
向TreeSet中存儲數據,數據會按照從小到大的方式排序輸出(升序)
TreeSet中的方法不是我們的重點
重點在於如何實現連個比較(排序)接口

ps:
二叉樹 --> 紅黑二叉樹TreeSet實現方式
二叉樹是一種非常重要的數據結構,它同時具有數組和鏈表各自的特點:
它可以像數組一樣快速查找,也可以像鏈表一樣快速添加。
但是他也有自己的缺點:刪除操作復雜。

我們先介紹一些關於二叉樹的概念名詞。
二叉樹:是每個結點最多有[兩個子樹]的有序樹,在使用二叉樹的時候,數據並不是隨便插入到節點中的,
一個節點的左子節點的關鍵值必須小於此節點,右子節點的關鍵值必須大於或者是等於此節點,所以又稱二叉查找樹、二叉排序樹、二叉搜索樹。

ps:二叉樹可以做為搜索算法的 hash表也可以 --> 尋路算法 --> TOP N --> 好友推薦算法
今日頭條 --> 首席算法工程師 -->XXX -->中科院 --> 10次 --> java算法(有一定基礎不然會瘋 --> 數據結構)

/**
 * 
 */
package com.qfedu.Day17.TreeSet.Comparable;

public class Person implements Comparable{
       private int age;
       private int height;//

    public Person(int age,int height) {
        super();
        this.age = age;
        this.height = height;
    }

    public Person() {
        super();
        // TODO Auto-generated constructor stub
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        
        return "年齡"+age+","+"身高"+height;
    }
    /* 
     * 是對對象需要排序的屬性進行比較的方法
     */
    @Override
    public int compareTo(Object o) {
        Person other = (Person)o;
        
        //比較的是年齡 升序 必須滿足 整數 負數和 零的關系
//        if(this.age > other.age) {
//            return 1; //正數 就是升序  負數是降序
//        }else if(this.age == other.age){
//            //繼續書寫 年齡相等身高降序
//            if(this.height > other.height) {
//                return -1;
//            }else if(this.height == other.height) {
//                //身高相等 , 體重升序
//                return 0;
//            }else {
//                return 1;
//            }
//    
//        }else {
//            return -1;
//        }
        //String 引用類型若需要排序,都會提供比較方法
//        String str1 = "abc";
//        String str2 = "ABC";
//        str1.compareTo(str2);
//        
        
        
        //用什麽方式就可以得到正數,負數 或 零
        //需要升序就用當前對象的值 - 傳入對象的值 升序
        //需要降序就用當前傳入對象的值 - 當前對象的值 降序
        return this.age - other.age;
    
    }
 
}

/**
 * 
 */
package com.qfedu.Day17.TreeSet.Comparable;

import java.util.TreeSet;

public class PersonTest {
    public static void main(String[] args) {
        TreeSet set = new TreeSet();
        set.add(new Person(10,180));
        set.add(new Person(17,190));
        set.add(new Person(12,150));
        set.add(new Person(12,170));
        set.add(new Person(9,165));
        set.add(new Person(20,155));
        System.out.println(set);

    }

}

/**
 * 
 */
package com.qfedu.Day17.TreeSet.Comparable;

import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        //1.默認排序的 升序(從小到大)
        //2.排重 --> 排除重復
        //3.無序
        //4.TreeSet的底層實現是一個紅黑二叉樹(中序遍歷 左 根 右)
        //ps:數據若需要排序就一定要具備可比性整數和整數   小數和小數   
        // 整數和字符串,自定義類的對象進行比較 不具備就會出現異常
        // 使用TreeSet就參考set集合的使用方式
        TreeSet set  =  new TreeSet();
        set.add(1);
        set.add(20);
        set.add(8);
        set.add(9);
        set.add("100");
        set.add(11);
        set.add(55);
        set.add(32);
        set.add(44);
        System.out.println(set);

    }

}
/**
 * 
 */
package com.qfedu.Day17.TreeSet.Comparator;

import java.util.Comparator;

public class GZ implements Comparator{

    /* 
     * 制定規則
     * 第一個參數是當前對象 --> this
     * 第二個參數是傳入對象 --> other
     */
    @Override
    public int compare(Object o1, Object o2) {
        Person pthis = (Person)o1;
        Person pother = (Person)o2;
        
        return pthis.getAge() - pother.getAge();
    }

}
/**
 * 
 */
package com.qfedu.Day17.TreeSet.Comparator;

public class Person {
     private int age;

    public Person() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Person(int age) {
        super();
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
     
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        
        return "年齡:"+age;
    }
    

}

/**
 * 
 */
package com.qfedu.Day17.TreeSet.Comparator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.TreeSet;

public class TreeSetDemo {

    public static void main(String[] args) {
        //第一種方式
        //這才是使用自定義比較器(Comparator)的方式
          TreeSet set = new TreeSet(new GZ());
          set.add(new Person(18));
          set.add(new Person(11));
          set.add(new Person(14));
          set.add(new Person(20));
          set.add(new Person(16));
          set.add(new Person(19));
          System.out.println(set);
          
          
          //第二種方式 匿名內部類
          TreeSet set1 = new TreeSet(new Comparator() {

            @Override
            public int compare(Object o1, Object o2) {
                Person pthis = (Person)o1;
                Person pother = (Person)o2;
                
                return pthis.getAge() - pother.getAge();
            }
              
              
          });
          
          set1.add(new Person(18));
          set1.add(new Person(11));
          set1.add(new Person(14));
          set1.add(new Person(20));
          set1.add(new Person(16));
          set1.add(new Person(19));
          System.out.println(set1);
          
          
          //ps:
          Integer[] array = new Integer[10];
          for(int i = 0;i<array.length;i++) {
              array[i] = (int)(Math.random()*100);
          }
          //給當前數組排序 升序
          Arrays.sort(array);
          System.out.println(Arrays.toString(array));
          //需要降序 --> lambda表達式
          Arrays.sort(array, new Comparator() {

            @Override
            public int compare(Object o1, Object o2) {
                Integer ithis = (Integer)o1;
                Integer iother = (Integer)o2;
                return iother.compareTo(ithis);
            }
        });
          System.out.println(Arrays.toString(array));
    
    }

}




完全二叉樹:若設二叉樹的高度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,
第h層有葉子結點,並且葉子結點都是從左到右依次排布,這就是完全二叉樹。
滿二叉樹——除了葉結點外每一個結點都有左右子葉且葉子結點都處在最底層的二叉樹。
深度——二叉樹的層數,就是深度。

二叉樹的特點總結:
(1)樹執行查找、刪除、插入的時間復雜度都是O(logN) ---> 大o算法
(2)遍歷二叉樹的方法包括前序、中序、後序
(3)非平衡樹指的是根的左右兩邊的子節點的數量不一致
(4)在非空二叉樹中,第i層的結點總數不超過 , i>=1;
(5)深度為h的二叉樹最多有個結點(h>=1),最少有h個結點;
(6)對於任意一棵二叉樹,如果其葉結點數為N0,而度數為2的結點總數為N2,則N0=N2+1;

二叉樹遍歷分為三種
先序遍歷
首先訪問根,再先序遍歷左子樹,最後先序遍歷右子樹 根 --> 左 -- > 右 無 序
中序遍歷
首先中序遍歷左子樹,再訪問根,最後中序遍歷右子樹 左 --> 根 --.> 右 升 序
後序遍歷
首先後序遍歷左子樹,再後序遍歷右子樹,最後訪問根 左 --> 右 --> 跟

提供一個視頻:實現一個二叉樹,實現了中序遍歷 --> 遞歸

提供自定義類的比較方式
若自定義類在TreeSet中需要按照某種需求進行排序就需要實現一個接口Comparable
Comparable接口強行對實現它的每個類的對象進行整體排序。
這種排序被稱為類的[自然排序],類的 compareTo 方法被稱為它的自然比較方法。

就要實現下面這個方法 --> 指定排序的規則(升序,降序)
int compareTo(T o)
比較此對象與指定對象的順序。
比較此對象與指定對象的順序。如果該對象小於、等於或大於指定對象,則分別返回負整數、零或正整數
使用this 和 other 來表示當前對象和傳入對象
this > other 正數 1 (升序)
this == other 零 0 (不變)
this < other 負數 -1 (降序)
ps: Comparable接口是對應TreeSet的無參構造方法
數值型數據 ---> 數字的大小
字符 ASCII碼不夠,Unicode值進行比較
字符串 Unicode比較
ps:unicode是包含ASCII碼
看Comparable包

比較接口Comparator 自定義比較器
應用於 Collection.sort -->降序或升序排序
Arrays.sort -->降序或升序排序
int compare(T o1, T o2)
比較用來排序的兩個參數。
返回值是int,只要是返回 正數,負數,零 就可以區分升序還是降序
第一個參數是當前對象this
第二個參數是傳入對象other
使用this 和 other 來表示當前對象和傳入對象
this > other 正數 1 (升序)
this == other 零 0 (不變)
this < other 負數 -1 (降序)
ps:Comparator對應的是TreeSet有參構造方法-->作為參數傳遞到TreeSet才可以使用

需求使用Comparator實現自定義排序,,Person類型屬性age age從小大升序排序
看Comparator包

總結:
1.需要集合對我們自定義類進行排序操作,就必須實現Comparator/Comparable其中一個接口
千萬不要雙管齊下,沒有用,Comparable會高於Comparator
2.TreeSet自帶排序排重,存數據完成排序必須具備可比性
3.TreeSet的方法參考Set接口中方法即可

Set總結:
1.不允許重復值
2.都是線程不安全的
3.無序存儲(無序不等於隨機)
ps:LinkdeHashSet不是無序
HashSet是Set集合中最常用一個集合底層是哈希表實現查詢效率高,當前集合也有擴容
若在HashSet中存儲自定義類創建對象,需要排重的話重寫equals和HashCode
LinkedHashSet是HashSet的子類,本身沒有任何特殊的方法都是繼承而來
使用沒有HashSet頻繁底層有鏈表和哈希表共同實現
TreeSet一般使用在需要進行排序時:
TreeSet自帶排序升序,需要具備可比性
可以使用TreeSet對自定義類進行排序
需要實現以下兩個接口之一即可Comparator/Comparable
這兩個接口分別對應這不同的構造方法
Comparable --> TreeSet無參構造方法
CompareTo方法

Comparator --> TreeSet有參構造方法
Compare方法

Collection是List和Set父接口
Collection可以使用List或Set接口下的實現類來創建對象
Collection對我們的作用一般就是作為方法的參數而存在
若使用Collection作為方法的參數,此時即可以就說List集合 也可以接受 Set集合


泛型(genericity):
為什麽要使用泛型?
案例1:
集合可以存儲任何數據類型,必然集合中需要使用Object類型接受
1.若需要使用計算或是當前對象的特有屬性和方法強制類型轉換(向下轉型)
2.向集合中存儲元素,無法限制存儲元素的數據類型
我們可以使用泛型來約束集合中存儲的數據類型,並且使用了泛型後就不需要強制類型轉換了(向下轉型)
但是這樣一來會出現一個問題,不能再存儲任何數據類型,只能存儲限制的數據類型對應的值

案例2:
設計一個點(Ponit),來封裝坐標,要求坐標的數據類型可以支持String,int,double,float,
class Point{
private String x;
private String y;

}
class Point{
private int x;
private int y;

}
....
原則DRY 不要重復自己
可以通過創建對象時在決定Point的數據是什麽,這樣一來是不是就可以不用創建多個Point而只要建立一個類即可
class Point{
privare T x;
private T y;

}
new Point();
就可以使用泛型

什麽是泛型:
1.泛型的含義就是代表任何數據類型,但是本身沒有任何含義,就是一個站位符
2.在不能明確數據類型式,我們即可以使用這種占位符的形式來替代數據類型

集合、二叉樹