1. 程式人生 > 其它 >TP框架下,利用模板引擎控制 某些便籤不顯示或者操作其屬性

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( ), 使得每個關鍵碼與結構中的唯一的儲存位置相對應:

Address=Hash( )

需要解決兩個問題:

  • 找到一個合適的雜湊函式,避免或儘量減少衝突
  • 擬定解決衝突的方案

雜湊函式

取餘法

\[\rm hash(key) = key\%p, p\leq m \]

散列表中地址數位m, p為不大於m但最接近m的質數.

取最大質數是為了減少衝突.

平方取中法

hash(key) = key^2的中間部分

長度取決於表的大小. 如表長 = \(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.尋找下一個桶的公式:

\[H_{i+1} = (H_i+1)\%m, i=1,2,...,m-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}\):

\[\begin{aligned} H_{i} = (H_{i_1} + 2*i-1)\% m, odd=0\\ H_{i} = (H_{i_1} - 2*i+1)\% m, odd=1\\ \end{aligned} \]

每次查詢完後, 將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....

處理衝突的開雜湊(鏈地址)方法

將同義詞放入同一個桶. 各個桶中的元素分別用單鏈表連線起來, 各個連結串列的表頭結點組成一個向量.