數據結構 - 排序總結

偶然發現菜鳥教程有着,整挺好存一下:https://www.runoob.com/w3cnote/radix-sort.html
快速排序:分治思想的冒泡排序
sf:https://segmentfault.com/a/1190000017314698 一篇文章讓你真正瞭解快速排序阮一峯


沒有編號的反人類目錄,不過可以看右方CSDN默認目錄。

數據結構複習

排序

名次排序/計數排序/原地重排

名次計算

名次是所有比它小的元素個數加上在它左邊出現的與它相同的元素個數。

對於每個i的值,比較次數爲i,因此總的比較次數爲1+2+3+...+n-1=(n-1)n/2.

template<class T>
void rank(T a[], int n, int r[]) {
	for(int i = 1; i < n; ++i) 
		r[i] = 0;
	for(int i = 1; i < n; ++i) 
		for(int j = 0; j < i; ++j)
			if(a[j] <= a[i]) 
				r[i] ++;
			else r[j] ++;
}

名次排序

利用附加數組的計數排序

2*n次移動+之前的(n-1)n/2次比較。

template<class T> 
void rearrange(T a[], int n, int r[]) {
	T *u = new T[n];
	for(int i = 0; i < n; ++i)
		u[r[i]] = a[i];
	for(int i = 0; i < n; ++i)
		a[i] = u[i];
	delete[] u;
}
原地重排

交換次數從最少0(初始即有序)到最多2(n-1).每次交換都可以將一個a[i]移動到正確位置。

比前者空間減少,時間複雜度增加一丟丟。

temlate<class T>
void rearrange(T a[], int n, int r[]) {
	for(int i = 0; i < n; ++i)
		while(r[i] != i) {
			int t = r[i];
			swap(a[i], a[t]);
			swap(r[i], r[t]);
		}
}

選擇排序

依次找剩下的元素中的最大值。

總的比較次數(n-1)n/2,元素移動次數3(n-1).

template<class T>
void selectionSort(T a[], int n) {
	for(int size = n; size > 1; size--) {
		int j = indexOfMax(a, size);
		swap(a[j], a[size-1]);
	}
}

及時終止的選擇排序

檢查如果已經有序,那麼就沒有必要繼續了。

template<class T>
void selectionSort(T a[], int n) {
	bool sorted = false;
	for(int size = n; !sorted && (size > 1); size--) {
		int indexOfMax = 0;
		sorted = true;
		for(int i = 1; i < size; i++)
			if(a[indexOfMax] <= a[i])
				indexOfMax = i;
			else sorted = false;
			swap(a[indexOfMax], a[size-1]);
	}
}

及時終止的冒泡排序

時間複雜度也爲(n-1)n/2,最好情況n-1

template<class T>;
bool bubble(T a[], int n) {
	bool swapped = false;
	for(int i = 0; i < n-1; i++)
		if(a[i] > a[i+1]) {
			swapped = true;
			swap(a[i], a[i+1]);
		}
	return swapped;
}

template<class T>;
void bubbleSort(T a[], int n) {
	for(int i = n; (i > 1) && bubble(a,i); i--);
}

插入排序

最好的比較次數是n-1,最壞的比較次數是(n-1)n/2.

template<class T>
void insert(T a[], int n, const T& x) {
	int i;
	for(i = n-1; i >= 0 && x < a[i]; i--)
		a[i+1] = a[i];
	a[i+1] = x;
}

template<class T>
void insertion(T a[], int n) {
	for(int i = 1; i < n; ++i) {
		T t = a[i];
		insert(a, i, t);
	}
}

版本2,其實沒啥不同

template<class T>
void insertion(T a[], int n) {
	for(int i = 1; i < n; ++i) {
		T t = a[i];
		int j;
		for(j = i-1; j >= 0 && t < a[j]; --j)
			a[j+1] = a[j];
		a[j+1] = t;
	}
}

堆 & 堆排序

通過初始化也可以排序,初始化好快啊,O(n)!(也就找個最大元素,其實理應這麼快)

void init() {
	for(int root = heapSize/2; root >= 1; --root) {
		int rootValue = heap[root];
		int child = 2*root;
		while(child <= heapSize) {
			if(child < heapSize && heap[child] < heap[child+1])
				child ++;
			if(rootValue >= heap[child])
				break;
			heap[child/2] = heap[child];
			child *= 2;
		}
		heap[child/2] = rootValue;
	}
}

通過插入刪除,都是O(logn)

void insert(int x) {
	int nw = ++heapSize;
	while(nw != 1 && heap[nw/2] < x) {//父節點更小 需要下移
		heap[nw] = heap[nw/2]; //下移
		nw /= 2;
	}
	heap[nw] = x;
}

void pop() {
	int last = heap[heapSize--]; //取出最後一個元素
	int nw = 1, child = 2;
	while (child <= heapSize) {
		if(child < heapSize && heap[child] < heap[child+1])
			child ++;
		if(last >= heap[child]) // 比孩子們都大
			break;
		heap[nw] = heap[child]; //孩子挪上來
		nw = child;
		child *= 2;
	}
	heap[nw] = last;
}

歸併排序 分治

書上代碼看不懂。

C語言版賊好用而且短好喜歡這個,思想是一樣的,考試考思路沒問題。

樹形 複雜度O(nlogn).

比較好的教程:常用12大排序算法之八:(遞歸和非遞歸)歸併排序

void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex) {
    int i = startIndex, j=midIndex+1, k = startIndex;
    while(i!=midIndex+1 && j!=endIndex+1) {
        if(sourceArr[i] > sourceArr[j])
            tempArr[k++] = sourceArr[j++];
        else
            tempArr[k++] = sourceArr[i++];
    }
    while(i != midIndex+1)
        tempArr[k++] = sourceArr[i++];
    while(j != endIndex+1)
        tempArr[k++] = sourceArr[j++];
    for(i=startIndex; i<=endIndex; i++)
        sourceArr[i] = tempArr[i];
}
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex) {
    int midIndex;
    if(startIndex < endIndex) {
        midIndex = (startIndex + endIndex) / 2;
        MergeSort(sourceArr, tempArr, startIndex, midIndex);
        MergeSort(sourceArr, tempArr, midIndex+1, endIndex);
        Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);
    }
}

快速排序 分治

思想,選取一個支點n,比n小的元素放在n的左邊作爲左段,比n大的放在n的右邊作爲右段,然後分別在左段和右段中進行快排,最後元素序列是左段+n+右段便是有序0。

見過的最通俗易懂的代碼。時間複雜度O(nlogn),最壞複雜度O(n^2).

void qsort(int a[], int left_index, int right_index)
{
    int left, right, pivot;
    if(left_index >= right_index) 
        return;
    left = left_index;
    right = right_index;
    pivot = a[(left_index + right_index) /2];
    while(left <= right) {
        while(a[left] < pivot) left++;
        while(a[right] > pivot) right--;
        if(left <= right) {
            swap(a[left],a[right]);
            left++; right--;
        }   
    }
    qsort(a,left_index,right);
    qsort(a,left,right_index);
}

箱子排序

即桶排。複雜度O(n).

基數排序

時間複雜度O(n),但是有個大常數c,所以可能不如快排快

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-E6bwuoF4-1593509465227)(C:\Users\wbxl2\AppData\Roaming\Typora\typora-user-images\image-20200104221341550.png)]

把數的每一位分離出來,從個位到高位依次桶排序一遍。

時間複雜度O(n),有個大常數。

排序總結

圖解十大排序算法

穩定排序

排序算法的穩定性

一個排序方法能夠保持同值元素之間的相對次序,那麼該方法稱爲穩定排序。

堆排序快速排序希爾排序直接選擇排序是不穩定的排序算法

基數排序冒泡排序直接插入排序折半插入排序歸併排序是穩定的排序算法。

穩定:插入排序、冒泡排序、歸併排序、計數排序、基數排序、桶排序

不穩定:快速排序、堆排序、希爾排序、選擇排序

冒泡排序 平均性能 最壞情況下性能 最好情況下性能 穩定性
冒泡排序 n^2 n^2 n 穩定
計數排序 n^2 n^2 n^2 穩定
插入排序 n^2 n^2 n 穩定
選擇排序 n^2 n^2 n^2 不穩定
堆排序 nlogn nlogn nlogn 不穩定
歸併排序 nlogn nlogn nlogn 穩定
快速排序 n^2 nlogn nlogn 不穩定

copy函數的三個參數

copy(a, a+n, tmp)表示從atmp.

漸進記法

沒太看懂。估計不考。

image-20200104191152884

散列表

線性探查

如果哈希的地方已經有元素,那麼找下一個可用桶。

要會畫圖模擬。

比較難的刪除操作代碼,一次檢查每個桶。

template<class T>
int hashTable<T>::erase(const T& theElement) {
	int root = search(theElement);
	delete table[root];
	table[root] = NULL;
	
	int cnt = 0;
	int i = (root+1)%divisor;
	int pre = (i-1+divisor)%divisor;
	while(table[i] != NULL && i != root) {
		int thisfather = (*table[i])%divisor;
		if((thisfather!= i)&&(((thisfather<=pre)&&(i>pre))||((thisfather>i)&&((pre<i)||(pre>=thisfather))))) {
			table[pre] = table[i];
			table[i] = NULL;
			pre = i;
			cnt++;
		}
		i = (i+1)%divisor;
	}
	
	return cnt;
}

鏈式散列

比較簡單。要會畫圖。

負載因子

α=n/(b=D)\alpha=n/(b=D)

對於線性探查:

一次成功搜索Un12(1+1(1α)2)U_n \approx \frac{1}{2}(1+\frac{1}{(1-\alpha)^2})

不成功搜索Sn12(1+1(1α))S_n \approx \frac{1}{2}(1+\frac{1}{(1-\alpha)})

一般情況下小於0.75.

性能

線性探查空間少,平均性能鏈式散列快。

樹的還原

前序遍歷:根左右

中序遍歷:左根右

後序遍歷:左右根

根據前中序列,求後序

根據前序遍歷劃分中序遍歷

中後求前也類似,要注意的是中後的時候會倒序看根節點會先右再左。

huffman樹

建立過程,每次取出兩棵權重最小的二叉樹。最後獲得編碼。

WEP是所有的路徑長*頻率再求和,也就是最終編碼的位數。

AVL樹

可視化 https://www.cs.usfca.edu/~galles/visualization/AVLtree.html

LL LR RR RL

B-樹

課本上的插入刪除很棒。

拓撲排序

有向無環圖(DAG)纔有拓撲排序。

  1. 從 DAG 圖中選擇一個 沒有前驅(即入度爲0)的頂點並輸出。

  2. 從圖中刪除該頂點和所有以它爲起點的有向邊。

  3. 重複 1 和 2 直到當前的 DAG 圖爲空或當前圖中不存在無前驅的頂點爲止。後一種情況說明有向圖中必然存在環。

Dijkstra

過程,要所有已知的最短邊去更新鄰接點。

正確性:當前節點能夠被取出來說明已經沒有其他邊可以更新此邊了。

Prim

記錄哪些點已經在生成樹中

當前生成樹的所有鄰接邊中選最小的

矩陣

特殊矩陣的壓縮

對角矩陣

三對角矩陣(分類討論就可)

三角矩陣(n(n+1)/2),下三角矩陣 map(i,j)=i(i1)/2+j1,ijmap(i,j)=i(i-1)/2+j-1, i \geq j

稀疏矩陣

線性儲存

相加

行主索引判斷一下前後次序,掃描一遍,小的加進去,相等就相加,直到其中一個到了末尾,最後剩下的元素推進去。

相乘

i,k中 尋找 k,j,進行累加。 相當於ans[i][j]要枚舉中間元素k

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章