TP框架下,利用模板引擎控制 某些便籤不顯示或者操作其屬性
排序
氣泡排序
template <typename T> void BubbleSort(vector<T>& list) requires Comparable<T> { const int n = list.size(); for (int i = 0; i < n - 1; i++) { for (int j = n - 1; j > i; j--) if (list[j] < list[j - 1]) swap(list[j], list[j - 1]); } }
插入排序
1. 直接插入排序
直接插入排序對本來已經大致有序的序列效率較高
template<typename T> void InsertSort(vector<T>& list,int sizeOfSorted=1) requires Comparable<T> { const int n = list.size(); for (int i = sizeOfSorted; i <= n-1; i++) { T temp = list[i]; int j = i - 1; for ( j = i-1; j >= 0 && temp < list[j]; j--) { list[j+1] =list[j]; } list[j+1] = temp; } }
1.折半插入排序
注意:
- while 迴圈的條件包括 =, 和
high = mid-1
以及low = mid+1
可以保證當迴圈結束時, 要插入的位置剛好是當前 low 的前一位. - 隨著不斷插入, low 和 high 每次迴圈時都要重新賦值.
template<typename T> void BinaryInsertSort(vector<T>& list,const int sizeOfSorted=1) { const int n = list.size(); int low = 0; int high = sizeOfSorted - 1; int mid = 0; for (int i = sizeOfSorted; i < n; i++) { T temp = list[i]; high = i - 1; low = 0; while (low<=high) { mid = (low + high) / 2; if (list[mid] > list[i]) high = mid-1; else low = mid+1; } for (int j = i; j > low; j--) list[j] = list[j - 1]; list[low] = temp; } }
3. 希爾排序法
template<typename T>
void ShellInsertSort(vector<T>& list) requires Comparable<T>
{
int gap = 0;
const int n = list.size();
for (gap = list.size() / 3; gap > 0; gap = gap / 3 + 1)
{
//各個子列輪流做直接插入, 將每個子列第零個元素設為基準
for (int i = gap; i < n; i++)
{
T temp = list[i];
int j = 0;
for ( j = i - gap; j >= 0 && temp < list[j] ; j -= gap)
{
list[j + gap] = list[j];
}
list[j + gap] = temp;
}
if (gap == 1) break;
}
}
快速排序法
通過一趟排序將序列分為左右兩個子序列, 其中左序列所有的資料都小於右序列, 然後對這兩個子序列進行快速排序,整個排序過程遞迴進行.
快速排序
//雙指標
template <typename T>
int FindPos(vector<T>& list,const int left,const int right) requires Comparable<T>
{
T base = list[left];
int smallerPos = left;
int greaterPos = right;
while ( smallerPos< greaterPos) {
while (list[greaterPos] >= base && smallerPos < greaterPos)
{
greaterPos--;
}
list[smallerPos] = list[greaterPos];
while (list[smallerPos] <= base && smallerPos < greaterPos)
{
smallerPos++;
}
list[greaterPos] = list[smallerPos];
}
list[smallerPos] = base;
return smallerPos;
}
//普通
template <typename T>
int FindPos(vector<T>& list, int left, int right) requires Comparable<T>
{
T base = list[left];
int smallerPos =left;
for(int i=left+1;i<=right;i++)
{
if(list[i]<base)
{
smallerPos++;
if(i!=smallerPos)
swap(list[smallerPos],list[i]);
}
}
swap(list[left],list[smallerPos]);
return smallerPos;
}
template <typename T>
void QuickSort(vector<T>& list,int left,int right) requires Comparable<T>
{
if (left < right) {
int pos = FindPos(list, left, right);
QuickSort(list, left, pos - 1);
QuickSort(list, pos + 1, right);
}
}
2.三路劃分快速排序
template <typename T>
void QuickSort(vector<T>& list, int left, int right)
{
if (left >= right) return;
T base = list[left];
int lessPos = left;
int greaterPos = right + 1;
for (int i = left + 1; i < greaterPos; i++)
{
if (list[i] < base)
swap(list[++lessPos], list[i]);
else if (list[i] > base)
{
swap(list[i], list[--greaterPos]);
i--;
}
}
swap(list[left], list[lessPos]);
QuickSort(list, left, lessPos - 1);
QuickSort(list, greaterPos, right);
}
選擇排序
1. 直接選擇
template<typename T>
void selectSort(vector<T>& list)
{
for (int i = 0; i < list.size(); i++)
{
int indexOfmin = i;
int j;
for (j = i + 1; j < list.size(); j++)
{
if (list[j] < list[indexOfmin]) indexOfmin = j;
}
swap(list[i], list[indexOfmin]);
}
}
2.堆排序
基數排序
void radixSort(vector<int>& array)
{
vector<vector<int>> bucket(10);
int max=array[0];
for (int i = 1; i < array.size(); i++)
{
if (max < array[i]) max = array[i];
}
for (int round = 1; round <=max; round *= 10)
{
//進桶
for (int i = 0; i < array.size(); i++)
{
bucket[((array[i]) / round) % 10].push_back(array[i]);
}
int count = 0;
int i = 0;
while (count != array.size())
{
while (!bucket[i].empty())
{
array[count++] = bucket[i].front();
bucket[i].erase(bucket[i].begin());
}
i++;
}
}
}
雜湊 (雜湊)
複雜度分析:
- 順序查詢: O(n)
- 二分查詢: O(\(\log_2n\))
- 雜湊方法: O(C)
散列表與雜湊方法
將一個元素的關鍵碼和儲存位置之間建立對應的函式關係 Hash( ), 使得每個關鍵碼與結構中的唯一的儲存位置相對應:
需要解決兩個問題:
- 找到一個合適的雜湊函式,避免或儘量減少衝突
- 擬定解決衝突的方案
雜湊函式
取餘法
\[\rm hash(key) = key\%p, p\leq m \]散列表中地址數位m, p為不大於m但最接近m的質數.
取最大質數是為了減少衝突.
平方取中法
長度取決於表的大小. 如表長 = \(2^9\) =\((512)_{10}\) , 地址 \(000\sim 777\),
key | 平方 | 雜湊地址 |
---|---|---|
\((2061)_8\) | \(4\underline{310}541\) | \(310\) |
\((1100)_8\) | \(1\underline{210}000\) | 210 |
乘法雜湊函式
\[\rm hash(key) = M\times((\phi \times key)\% 1)_{10} \]將結果化成八進位制
處理衝突的閉雜湊(開地址)方法
產生衝突元素的關鍵碼互為同義詞.
閉雜湊又叫開地址法. 所有的桶都直接放在散列表陣列中,並且把該陣列組織成環形結構. 每個桶只有一個元素. 當發生衝突時, 把這個元素存放進表中"下一個"空桶中.尋找空桶的方法有很多.
線性探查法
若hash(key)=d
並且這個桶已經被佔用, 那麼檢查陣列中連續的桶:d+1,d+2...m-1,0,...d-1
.尋找下一個桶的公式:
每次發生衝突就探查下一個桶, 當迴圈 m-1 次後就會回到開始探查時的位置,說明待查關鍵碼不在表內且表已滿,不能再插入新的關鍵碼.
\(\rm ASL_{succ}\) : 搜尋成功的平均搜尋次數, 搜尋成功時, 把找到的每個元素的比較次數比上元素個數得到\(\rm ASL_{succ}\)
\(\rm ASL_{unsucc}\): 搜尋失敗時平均探查次數, 指在表中沒有找到與待插入元素關鍵碼相同的元素, 但找到空桶(即最終插入位置)時平均探查次數. 它是對於散列表中每個地址而言的, 其實就是從每個桶到下一個空桶需要探查的次數的平均值.
散列表儲存的是元素集合, 不允許關鍵碼相同的元素存在.
注意:閉雜湊情況下不能真正地將已有的元素刪去, 因為中間的元素被刪掉後會影響到之後元素的探查. 所以用一個狀態陣列來標識雜湊表中每個元素的狀態.
二次探查法
若用hash函式算得的桶 \(H_0\) 已經被佔用,那麼下 \(i\) 個桶號 \(H_{i}\):
\[\begin{aligned} H_{i}=(H_0+i^2)\%m,i = 1,3,5...\\ H_{i}=(H_0-i^2)\%m,i = 2,4,6...\\ \end{aligned} \]假設上一個桶號為 \(H_{i-1}\),用一個標識 odd
控制是加還是減, 可得 \(H_{i}\):
每次查詢完後, 將odd
取反.
更淺顯的
bool QuadraticProbing(key)
{
int h0 = key%divisor;
if(info[h0]==empty||info[h0]==deleted||table[h0]==key)
return h0;
int i = 0;
int iSqure = 0;
int odd = 1;
while(1)
{
if(odd == 1)
{
iSqure = iSqure+2*i+1;
}
h0 = (h0 + odd * iSqure)%divisor;
if(info[h0]==empty||info[h0]==deleted||table[h0]==key)
return h0;
if(odd==1) odd=-1;
else {i++;odd=1;}
}
}
雙雜湊
如果hash(key)
計算得到的桶號d已經被佔用, 那麼用再雜湊函式rehash(key)
計算得到 c, 則依次探查 d+c,d+2c,d+3c....
處理衝突的開雜湊(鏈地址)方法
將同義詞放入同一個桶. 各個桶中的元素分別用單鏈表連線起來, 各個連結串列的表頭結點組成一個向量.