1. 程式人生 > >資料結構與演算法整理

資料結構與演算法整理

    基本上耳熟能詳的2個概念了,之前總是趕到招聘的時候,才會折騰翻一下,總覺得沒什麼用,但是隨著工作以及網上的閱讀,發現,其實,用處真的是太大了。

    好早之前就買了《演算法導論》這部鉅著,然而,現在依然束之高閣,最近想著從簡單點的看起,於是翻了翻《大話資料結構》《啊哈!演算法》,因為也還沒看完,下面就想到哪說到哪咯。

    佇列:先進先出的一種資料結構(FIFO),在樹的層序遍歷裡以及廣度搜索演算法中,都有用到,再簡單點,像陣列啦、vector啦,都是類似的東東,在這裡,有必要分享一個憂桑的故事:話說有一天,廢柴君興致勃勃的去參加面試,一路各種low爆,終於,面試官忍無可忍的問了廢柴君一個基礎問題:佇列說從頭進還是從尾進?那一刻,廢柴君內心是崩潰的,是迷茫的,只記得先進先出啊,頭進尾出或者是尾進頭處,不都一樣麼……好吧,後來記住了,原來佇列是尾進

!主要參考小時候排隊,都是從後面進到隊伍中,從前面一個個走的。

    棧:後進先出(LIFO),一般比較熟悉的應該就是壓棧出棧之類的操作,在函式呼叫之類的地方,總會被各種壓來出去的,所以這裡好像有個說法,太多的遞迴呼叫,是不好的。以前對棧什麼感覺,一直到前一陣做一個模仿資料夾“後退前進”操作的時候,當時是各種煎熬啊,生生了折騰了1天,各種陣列、各種flag,雖然當時是弄出來了,但是回頭再看的時候,連自己都蒙圈。直到前幾天翻到棧這裡,原來折騰半天,一個現成的資料結構擺在那裡,我卻沒有去珍惜。回去優化了一下,很好,自己看著真心舒服很多。

    陣列和連結串列:這兩種基本上就很常用了,資料呢,是一段連續的記憶體,隨機訪問速度很快,但是針對陣列中間資料進行操作的話,就會十分麻煩,而連結串列則不然,對連結串列的中間資料操作起來倒是比較方便,可是說到隨機訪問,就不行了,必須要一個個的遍歷過去。

    基本上典型點的代表,就是STL中的vector以及list了,而針對前面說過的佇列和棧,其實用陣列或連結串列,都可以分別實現的,再專業點的說法叫做:順序儲存結構和鏈式儲存結構。

    樹:樹就是不包含迴路的連通無向圖,二叉樹裡面最全的,叫滿二叉樹(一個點都不少呦),可以少幾個樹葉(從右往左)的,叫完全二叉樹。其他紅黑樹、B樹什麼的,真心沒看過,下次再說。樹這裡,暫時能想到的,也就2個:1是遍歷:前序、中序、後序,層序;2是堆:最小堆、最大堆。

    這裡說到的前序、中序、後序,主要是遍歷的時候,讀取節點的位置,讀取節點,之後左子節點,之後右子節點,這就是前序,其他類似。

    至於堆,主要就是用樹來進行排序。最小堆就是父節點的值小於所有子節點的值,反之,就是最大堆了。

    針對最小堆或者最大堆,主要有2種維護方式,分別是向下維護以及向上維護,程式碼像下面這樣:

// 向下維護最小堆
void siftDown(int i)
{
	// 假設heap已經是最小堆了
	int t = 0;
	while (i*2<n && !bFlag)
	{
		if (heap[i] < heap[i*2])
		{
			t = i;
		}
		else
		{
			t = i*2;
		}

		if (i*2+1<n)
		{
			if (head[t] > heap[i*2+1])
			{
				t = i*2+1;
			}
		}

		if (i == t)
		{
			bFlag = true;
		}
		else
		{
			swap(i, t);
			i = t;
		}
	}
}

// 向上維護最小堆
void siftUp(int i)
{
	// 假設heap已經是最小堆了
	if (i == 1)
	{
		return;
	}
	
	if (heap[i] > head[i/2])
	{
		return;
	}
	else
	{
		swap(i, i/2);
		siftUp(i/2);
	}
}

    至於堆的應用,比如想從小到大排序,可以先建立最小堆,然後每次把根節點拿出來,之後將最後的葉子節點放上去,維護一下最小堆,再次重複拿出根節點,就可以啦。

    堆排序還有一種更好的方法,從小到大排序的時候,不建立最小堆而建立最大堆,之後每次講根節點,與最後的節點互換,這樣還不多佔用空間了呦。

    堆還經常被用來求一個數列中第k大的數:只需要先將數列中前k個元素拿出來,組成一個最大堆,之後後面的元素依次比較就可以了。

    最後還有圖:好吧,這裡除了Folyd演算法,其他的真心沒看懂……

    以上部分內容節選自:《大話資料結構》《啊哈!演算法》感謝前人栽樹。