資料結構與演算法-優先順序佇列
優先順序佇列
概念
搜尋樹結構和詞典結構,都支援覆蓋資料全集的訪問和操作。也就是說,其中儲存的每一資料物件都可作為查詢和訪問目標
優先順序佇列,較之此前的資料結構反而有所削弱。具體地,這類結構將操作物件限定於當前的全域性極值者。而維護一個偏序關係
- 介面
template <typename T> struct PQ { //優先順序佇列PQ模板類 virtual void insert ( T ) = 0; //按照比較器確定的優先順序次序插入詞條 virtual T getMax() = 0; //叏出優先順序最高的詞條 virtual T delMax() = 0; //刪除除優先順序最高的詞條 };
- 應用
/****************************************************************************************** 2 * Huffman樹極造演算法:對傳入癿Huffman森枃forest逐步合幵,直刡成為一棵樹 3 ****************************************************************************************** 4 * forest基亍優先順序佇列實現,此演算法適用亍符合PQ介面癿仸何實現斱式 5 * 為Huffman_PQ_List、Huffman_PQ_ComplHeap和Huffman_PQ_LeftHeap共用 6 * 編譯前對應工程叧需謳置相應標誌:DSA_PQ_List、DSA_PQ_ComplHeap戒DSA_PQ_LeftHeap 7 ******************************************************************************************/ HuffTree* generateTree ( HuffForest* forest ) { while ( 1 < forest->size() ) { HuffTree* s1 = forest->delMax(); HuffTree* s2 = forest->delMax(); HuffTree* s = new HuffTree(); s->insertAsRoot ( Huffchar ( '^', s1->root()->data.weight + s2->root()->data.weight ) ); s->attachAsLC ( s->root(), s1 ); s->attachAsRC ( s->root(), s2 ); forest->insert ( s ); //將合幵後癿Huffman樹揑回Huffman森枃 } HuffTree* tree = forest->delMax(); //至此,森枃中癿最後一棵樹 return tree; //即全尿Huffman編碼樹 }
堆
完全二叉堆
- 什麼是堆
-
它是一個完全二叉樹,即除了最後一層節點不是滿的,其他層節點都是滿的,即左右節點都有。
-
它不是二叉搜尋樹,即左節點的值都比父節點值小,右節點的值都不比父節點值小,這樣查詢的時候,就可以通過二分的方式,效率是(log N)。
-
它是特殊的二叉樹,它要求父節點的值不能小於子節點的值。這樣保證大的值在上面,小的值在下面。所以堆遍歷和查詢都是低效的,因為我們只知道從根節點到子葉節點的每條路徑都是降序的,但是各個路徑之間都是沒有聯絡的,查詢一個值時,你不知道應該從左節點查詢還是從右節點開始查詢。
-
它可以實現快速的插入和刪除,效率都在(log N)左右。所以它可以實現優先順序佇列。
-
大頂堆與小頂堆
堆序性也可對稱地約定為“堆頂以外的每個節點都不低(小)於其父節點”,此時同理,優先順序最低的詞條,必然始終處於堆頂位置。為以示區別,通常稱前(後)者為大(小)頂堆 -
模板類與一些常用方法
//必要的方法
int Parent(int i){
//PQ[i]的父節點(floor((i-1)/2),i無論正負)
return ((i - 1) >> 1);
}
int LChild(int i){
//PQ[i]的左孩子
return (1 + (i << 1));
}
int RChild(int i){
//PQ[i]的右孩子
return ((i + 1) << 1);
}
Boolean InHeap(int n,int i){
//判斷PQ[i]是否合法
return ((-1 < i) && (i < n));
}
Boolean ParentValid(int i){
return (Parent(i) >= 0);
}
Boolean LChildValid(int n,int i){
//判斷PQ[i]是否有一個(左)孩子
return InHeap(n,LChild(i));
}
Boolean RChildValid(int n,int i){
//判斷PQ[i]是否有兩個孩子
return InHeap(n,RChild(i));
}
int Bigger(Vector<T> PQ,int i,int j){
//取大者(等時前者優先)
return (lt(PQ.elementAt(i),PQ.elementAt(j)) ? j : i);
}
int ProperParent(Vector<T> PQ,int n, int i){
/*父子(至多)三者中的大者*/
return (RChildValid(n,i) ? Bigger(PQ,Bigger(PQ,i,LChild(i)),RChild(i)) :
(LChildValid(n,i) ? Bigger(PQ,i,LChild(i)) : i));
}
//相等時父節點優先,如此可避免不必要的交換
Boolean lt(T a,T b){
return ((int)a > (int)b);
}
void swap(T a,T b){
T c = a;
a = b;
b = c;
}
void copyFrom(T[] A,int lo,int hi){
int i = 0;
while (lo < hi){
this.set(i++, A[lo++]);
}
}
int LastInternal(int n){
return ((n + 1) / 2 - 1);
}
元素插入
-
原理
-
實現
@Override
public void insert(T e) {
//插入詞條
this.addElement(e);
percolateUp(elementCount - 1);
}
protected int percolateUp(int i){
//上濾
while (ParentValid(i)){
int j = Parent(i);
if (lt(elementAt(i),elementAt(j))) break;
swap(elementAt(i),elementAt(j));
i = j;
}
return i;
}
- 例項
元素刪除
-
原理
-
實現
@Override
public T delMax() {
//刪除詞條
T maxElem = elementAt(0);
this.setElementAt(elementAt(elementCount - 1),0);
this.remove(--elementCount);
percolateDown(elementCount,0);
return maxElem;
}
protected int percolateDown(int n,int i){
//下濾
int j = 0;
while (i != (j = ProperParent(this,n,j))){
swap(elementAt(i),elementAt(j));
i = j;
}
return i;
}
- 例項
批量建堆
-
原理
-
實現
public PQ_CompHeap(T[] A, int n){
//批量構造
copyFrom(A,0,n);
heapify(n);
}
protected void heapify(int n){
//Floyd建堆演算法
for (int i = LastInternal(n);i >= 0;i--){
//自下而上,依次
percolateDown(n,i);
//下濾各內部節點
}
}
protected int percolateDown(int n,int i){
//下濾
int j = 0;
while (i != (j = ProperParent(this,n,j))){
swap(elementAt(i),elementAt(j));
i = j;
}
return i;
}
- 例項
就地堆排序
-
原理
-
實現
//堆排序
public void heapSort(T[] A,int lo,int hi){
copyFrom(A,lo,hi);
int n = hi - lo;
heapify(n);
while (0 < --n){
swap(A[0],A[n]);
percolateDown(n,0);
}
}
- 例項
左視堆
概念
對於堆的合併來說,可以想到的有兩種方法:
-
簡單的取出並插入
-
將兩個堆中詞條視為無順序的,用堆合併
-
堆不需要與二叉樹一樣保持平衡
左視堆:左式堆是優先順序佇列的另一實現方式,可高效地支援堆合併操作。其基本思路是:在保持堆序性的前提下附加新的條件,使得在堆的合併過程中,只需調整很少量的節點。具體地,需參與調整的節點不超過O(logn)個,故可達到極高的效率。
模板類
使用的是二叉樹結構
空節點路徑長度
左傾性
最右側通路
package com.atguigu.domin;
import com.atguigu.self.BinNode;
import com.atguigu.self.BinTree;
/**
* @anthor shkstart
* @create 2020-08-13 15:07
*/
public class PQ_LeftHeap<T> extends BinTree<T> implements PQ<T> {
@Override
public void insert(T e) {
//插入元素
BinNode<T> v = new BinNode<T>(e);
_root = merge(_root,v);
_root.parent = null;
_size++;
}
@Override
public T getMax() {
//取出優先順序最高的元素
return _root.data;
}
@Override
public T delMax() {
//刪除優先順序最高的元素
BinNode<T> lHeap = _root.lc;
//左子堆
BinNode<T> rHeap = _root.rc;
//右子堆
T e = _root.data;
//備份堆頂處的最大元素
_size--;
//刪除一個節點
_root = merge(lHeap,rHeap);
//原左右堆合併
if (_root != null){
//更新父子連結
_root.parent = null;
}
return e;
//返回源節點處資料
}
public BinNode<T> merge(BinNode<T> a, BinNode<T> b){
if (a ==null) return b;
if (b == null) return a;
if (lt(a.data,b.data)) swap(b,a);
a.rc = merge(a.rc,b);
a.rc.parent = a;
if (a.rc == null || a.rc.npl < a.rc.npl){
swap(a.lc,a.rc);
}
a.npl = (a.rc != null) ? a.rc.npl + 1:1;
return a;
}
//一些必要的方法
void swap(BinNode<T> a,BinNode<T> b){
BinNode<T> c = a;
a = b;
b = c;
}
Boolean lt(T a,T b){
return ((int)a < (int)b);
}
}
合併
- 原理
- 實現
public BinNode<T> merge(BinNode<T> a, BinNode<T> b){
if (a ==null) return b;
if (b == null) return a;
if (lt(a.data,b.data)) swap(b,a);
a.rc = merge(a.rc,b);
a.rc.parent = a;
if (a.rc == null || a.rc.npl < a.rc.npl){
swap(a.lc,a.rc);
}
a.npl = (a.rc != null) ? a.rc.npl + 1:1;
return a;
}
//一些必要的方法
void swap(BinNode<T> a,BinNode<T> b){
BinNode<T> c = a;
a = b;
b = c;
}
Boolean lt(T a,T b){
return ((int)a < (int)b);
}
- 例項
基於合併的插入和刪除
- 原理
- 實現
@Override
public void insert(T e) {
//插入元素
BinNode<T> v = new BinNode<T>(e);
_root = merge(_root,v);
_root.parent = null;
_size++;
}
@Override
public T getMax() {
//取出優先順序最高的元素
return _root.data;
}
@Override
public T delMax() {
//刪除優先順序最高的元素
BinNode<T> lHeap = _root.lc;
//左子堆
BinNode<T> rHeap = _root.rc;
//右子堆
T e = _root.data;
//備份堆頂處的最大元素
_size--;
//刪除一個節點
_root = merge(lHeap,rHeap);
//原左右堆合併
if (_root != null){
//更新父子連結
_root.parent = null;
}
return e;
//返回源節點處資料
}
- 例項
錦標賽排序
原始演算法
-
原理
-
實現
相關連結
https://www.cnblogs.com/binarylei/p/10115921.html#24-堆排序-vs-快速排序
package com.atguigu.domin;
/**
* @anthor shkstart
* @create 2020-08-13 16:31
*/
public class TourSort extends PQ_CompHeap{
public static final int MAX = 10000;
private class Node//用node來儲存競賽排序過程中的節點,包括裡面的資料和資料在陣列中的ID
{
public int data;
public int id;
public Node()
{
}
public Node(int data, int id) {
this.data = data;
this.id = id;
}
}
public void Adjust(Node[] B, int idx){
//當去除最小元素以後,我們要調整陣列
while (idx != 0){
if (idx / 2 == 0){
if (B[idx].id == -1){
B[Parent(idx)] = B[idx + 1];
} else {
B[Parent(idx)] = B[idx].data > B[idx+1].data ? B[idx+1] : B[idx];
}
} else {
if (B[idx].id == -1){
B[Parent(idx)] = B[idx - 1];
} else {
B[Parent(idx)] = B[idx].data > B[idx-1].data ? B[idx-1] : B[idx];
}
}
}
}
public void creat(int[] A) {
int n = 1;
while (n < A.length){
n *= 2;
}
int s = n - 1;
Node[] B = new Node[2*n - 1];
for (int i = 0;i < n;i++){
if (i < A.length){
B[n - 1 + i].data = A[i];
B[n - 1 + i].id =n - 1 + i;
} else {
B[n - 1 + i].data = MAX;
B[n - 1 + i].id = -1;
}
}
while (--s >= 0){
if (B[LChild(s)].id == (-1)) {
B[s] = B[RChild(s)];
} else if (B[RChild(s)].id == (-1)) {
B[s] = B[LChild(s)];
} else {
B[s] = (B[LChild(s)].data < B[RChild(s)].data) ? B[LChild(s)] : B[RChild(s)];
}
}
for ( int i = 0; i < A.length; i++)//實際排序的過程
{
A[i] = B[0].data;
//取出最小的
B[B[0].id].id = -1;
Adjust(B, B[0].id);
}
}
}
改進演算法
-
原理
-
實現(還未寫)
多叉堆
-
原理
-
效率
- 實現(還未寫)
相關連結
HashTable解析(先寫到這裡,等hashmap看了整理到一起)
package java.util;
import java.io.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.BiFunction;
/*
和HashMap一樣,Hashtable 也是一個散列表,它儲存的內容是鍵值對(key-value)對映。
Hashtable 繼承於Dictionary,實現了Map、Cloneable、java.io.Serializable介面。
Hashtable 的函式都是同步的,這意味著它是執行緒安全的。它的key、value都不可以為null。
此外,Hashtable中的對映不是有序的。
*/
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
/**
* hashtable中的資料
*/
private transient Entry<?,?>[] table;
/**
* 表中的元素的實際數量
*/
private transient int count;
/**
* 閾值,判斷是否需要調整容量
*/
private int threshold;
/**
* 載入因子,一般0.75
*/
private float loadFactor;
/**
*對於表的操作次數,可以判斷
*是否有併發執行緒對其進行改動,並返回錯誤
*/
private transient int modCount = 0;
/**
*版本號
*/
private static final long serialVersionUID = 1421746759512286392L;
/**
* 構造器:有初始容量和載入因子
*/
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
if (initialCapacity==0)
initialCapacity = 1;//以上是對極端情況的調整判斷
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//閾值大小
}
/**
* 構造器:載入因子為0.75預設,有初始容量
*/
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/**
* 構造器:載入因子0.75,初始容量11
*/
public Hashtable() {
this(11, 0.75f);
}
/**
* 構造器:有Map對映,構造器初始容量大於對映的兩倍
*/
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
/**
* 最大容量為int最大值-8
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 動態改變容量大小
*/
@SuppressWarnings("unchecked")
protected void rehash() {
int oldCapacity = table.length;//記錄表的老容量
Entry<?,?>[] oldMap = table;//記錄表的資料
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;//判斷新的容量與最大值的關係
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)//判斷老的容量與最大值的關係
return;//相等就結束
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;//操作次數增加
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//設定閾值
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {//每一個位置的每一條鏈
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;//一條鏈的下一個連結
int index = (e.hash & 0x7FFFFFFF) % newCapacity;//從新計算引用位置
e.next = (Entry<K,V>)newMap[index];//將這個與上一個連結
newMap[index] = e;//賦值
}
}
}
/**
* 刪除某個詞條
*/
public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];//通過key計算hash值並進一步確定位置
for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
modCount++;
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;//將key對應的詞條的上和下連結起來
}
count--;//數量減1
V oldValue = e.value;
e.value = null;//對應處的數值改為Null
return oldValue;
}
}
return null;
}
/**
* 複製某一Map的所有詞條至表中
*/
public synchronized void putAll(Map<? extends K, ? extends V> t) {
for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
put(e.getKey(), e.getValue());
}
// Views
/**
* 所有的key,entry,values都要加一個鎖,保證不發生併發改變的問題
*/
private transient volatile Set<K> keySet;
private transient volatile Set<Map.Entry<K,V>> entrySet;
private transient volatile Collection<V> values;
/**
*
*/
public Set<K> keySet() {
if (keySet == null)
keySet = Collections.synchronizedSet(new KeySet(), this);
return keySet;
}
private class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return getIterator(KEYS);
}
public int size() {
return count;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return Hashtable.this.remove(o) != null;
}
public void clear() {
Hashtable.this.clear();
}
}
/**
*
*/
public Set<Map.Entry<K,V>> entrySet() {
if (entrySet==null)
entrySet = Collections.synchronizedSet(new EntrySet(), this);
return entrySet;
}
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return getIterator(ENTRIES);
}
public boolean add(Map.Entry<K,V> o) {
return super.add(o);
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
Object key = entry.getKey();
Entry<?,?>[] tab = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index]; e != null; e = e.next)
if (e.hash==hash && e.equals(entry))
return true;
return false;
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
Object key = entry.getKey();
Entry<?,?>[] tab = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
if (e.hash==hash && e.equals(entry)) {
modCount++;
if (prev != null)
prev.next = e.next;
else
tab[index] = e.next;
count--;
e.value = null;
return true;
}
}
return false;
}
public int size() {
return count;
}
public void clear() {
Hashtable.this.clear();
}
}
/**
*
*/
public Collection<V> values() {
if (values==null)
values = Collections.synchronizedCollection(new ValueCollection(),
this);
return values;
}
private class ValueCollection extends AbstractCollection<V> {
public Iterator<V> iterator() {
return getIterator(VALUES);
}
public int size() {
return count;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
Hashtable.this.clear();
}
}
/**
* 判等
*/
public synchronized boolean equals(Object o) {
if (o == this)//正好相等
return true;
if (!(o instanceof Map))//型別不一致
return false;
Map<?,?> t = (Map<?,?>) o;
if (t.size() != size())
return false;
try {//判斷不相等
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Map.Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(t.get(key)==null && t.containsKey(key)))//不存在相同key值
return false;
} else {
if (!value.equals(t.get(key)))//key對應的位置存在但值不相等
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
/**
* 返回hashcode值
*/
public synchronized int hashCode() {
int h = 0;
if (count == 0 || loadFactor < 0)
return h; // 空
loadFactor = -loadFactor; // 載入因子改為負,作標記,不能被其他程序更改
Entry<?,?>[] tab = table;
for (Entry<?,?> entry : tab) {
while (entry != null) {
h += entry.hashCode();//hashcode的計算方式:相加該位置所有連結的hash值
entry = entry.next;
}
}
loadFactor = -loadFactor;
return h;
}
/*
*按照key,value刪除
*/
@Override
public synchronized boolean remove(Object key, Object value) {
Objects.requireNonNull(value);
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
modCount++;
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
count--;
e.value = null;
return true;
}
}
return false;
}
/**
* 通過流儲存檔案
*/
private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
Entry<Object, Object> entryStack = null;
synchronized (this) {
s.defaultWriteObject();
s.writeInt(table.length);
s.writeInt(count);
for (int index = 0; index < table.length; index++) {
Entry<?,?> entry = table[index];
while (entry != null) {
entryStack =
new Entry<>(0, entry.key, entry.value, entryStack);
entry = entry.next;
}
}
}
while (entryStack != null) {
s.writeObject(entryStack.key);
s.writeObject(entryStack.value);
entryStack = entryStack.next;
}
}
/**
* 詞條
*/
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
protected Entry(int hash, K key, V value, Entry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
@SuppressWarnings("unchecked")
protected Object clone() {
return new Entry<>(hash, key, value,
(next==null ? null : (Entry<K,V>) next.clone()));
}
// Map.Entry Ops
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
if (value == null)
throw new NullPointerException();
V oldValue = this.value;
this.value = value;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
(value==null ? e.getValue()==null : value.equals(e.getValue()));
}
public int hashCode() {
return hash ^ Objects.hashCode(value);
}
public String toString() {
return key.toString()+"="+value.toString();
}
}
}