數據結構自學筆記(郝斌)

數據結構概述(教材選用嚴蔚敏、吳偉民,該書程序是僞算法
具體的程序是高一凡,西電的,大牛,只有
程序。還有一本書,臺灣的黃國瑜自己寫的
只有思路,程序是另外一個合作的清華的寫
的,可惜很多錯的。)
學完數據結構之後會對面向過程的函數有一個更深的瞭解

定義
我們如何把現實中大量而複雜的問題以特定的數據類型(單
個數據怎樣存儲?)和特定的存儲結構(個體的關係)
保存到主存儲器(內存)中,以及在此基礎上爲實現某個功能
(比如查找某個元素,刪除某個元素,對所有元素進行排序)
而執行的相應操作,這個相應的操作也叫算法。(比如班裏有
15個人,其信息量也許一個數組就搞定了,但是假如10000個,
怎麼辦?內存也許沒有這麼多連續的空間,所以我們改用鏈表,
you see這就是與存儲有關係。又比如,人事管理系統的信息存儲,
因爲存在着上下級的關係,所以數組和鏈表就無能爲力了,
這時候我們用樹,再比如我們做的是交通圖,站和站之間肯定要連通,這
時候以上的存儲方式又無能爲力了,所以我們又有了圖。圖
就是每個結點都可以和其他結點產生聯繫。所以當我們要解決
問題時,首先要解決的是如何把這些問題轉換成數據,先保存
到我們的主存中,)

   數據結構 = 個體的存儲 + 個體的關係的存儲
   算法 = 對存儲數據的操作

算法
解題的方法和步驟

	衡量算法的標準
		1、時間複雜度
			大概程序要執行的次數,而非執行的時間。
			
		2、空間複雜度
			算法執行過程中大概所佔用的最大內存
			
		3、難易程度(主要是應用方面看重)
		
		4、健壯性(不能別人給一個非法的輸入就掛掉)
		
數據結構的地位
	數據結構是軟件中最核心的課程
	
	程序 = 數據的存儲+數據的操作+可以被計算機執行的語言(已經提供)

(學完數據結構,想用一種語言去實現它,必須有指針,數據結構java
版,就胡扯,變味,因爲我們要講鏈表,就是通過指針鏈在一起的。比如
在java中A aa = new A();本質上,aa是個地址)
預備知識
指針
指針的重要性:(內存是可以被CPU直接訪問的,硬盤不行
主要靠地址總線,數據總線,控制總線。)
指針是C語言的靈魂
定義
地址
地址就是內存單元的編號
從0開始的非負整數
範圍:0–FFFFFFFF[0-4G-1](地址線是32位,剛好控制2的32次)
指針:
指針就是地址 地址就是指針
指針變量是存放內存單元地址的變量
指針的本質是一個操作受限的非負整數(不能加乘除,只能減)
分類:
1、基本類型的指針

	    2、指針和數組的關係

結構體(C++中用類也能實現)
	爲什麼會出現結構體
		爲了表示一些複雜的數據,而普通的基本類型變量無法滿足要求
	
	什麼叫結構體
		結構體是用戶根據實際需要自己定義的複合數據類型
	
	如何使用結構體
		兩種方式:
			struct Student st = {1000, "zhangsan", 20}
			struct Student * pst = &st;
			
			1.
				st.sid
			2.
			 	pst->sid
			 	pst所指向的結構體變量中的sid這個成員
	
	注意事項:
		結構體變量不能加減乘除,但可以相互賦值
		普通結構體變量和結構體指針變量作爲函數參數的傳遞

(病毒就是靠訪問正在運行的那些程序所佔用的內存。Java中規定局部
變量必須初始化,因爲這些變量一開始都是垃圾值,但是屬性不是必須
初始化的,因爲已經默認初始化爲0)
動態內存分配和釋放(動態分配的內存一定要手動釋放,否則造成內存
泄露。)
(java中A aa = new A();其實就是 A *p = (A *)malloc(sizeof(A)))

模塊一:線性結構【把所有的結點用一根直線穿起來】
連續存儲【數組】
1、什麼叫做數組
元素類型相同,大小相等(數組傳參,只要傳進去首地址和長度就行)
2、數組的優缺點:
優點:
存取速度快
缺點:
事先必須知道數組的長度
插入刪除元素很慢
空間通常是有限制的
需要大塊連續的內存塊
插入刪除元素的效率很低

離散存儲【鏈表】(我們搞底層的開發,類似於SUN公司的類)
	定義:
		n個節點離散分配
		彼此通過指針相連
		每個節點只有一個前驅節點,每個節點只有一個後續節點
		首節點沒有前驅節點,尾節點沒有後續節點。
		
		專業術語:
				首節點:
						第一個有效節點
				尾節點:
						最後一個有效節點
				頭節點:
						頭結點的數據類型和首節點的類型一樣
						沒有存放有效數據,最最前面的,是在
						首節點之前的,主要是爲了方便對鏈表
						的操作。
				頭指針:(指向頭)
						指向頭節點的指針變量
				尾指針:
						指向尾節點的指針

(頭結點有可能很大,佔的內存可能大,假設我想造一個函數
輸出所有鏈表的值,那你如果不用頭指針類型做形參,那由於
不同鏈表的頭節點不一樣大小,這樣就沒辦法找出形參)

	確定一個鏈表需要幾個參數:(或者說如果期望一個函數對鏈表進行操作
								我們至少需要接收鏈表的那些信息???)
		只需要一個參數:頭指針,因爲通過它我們可以推出
		鏈表的所有信息。

(鏈表的程序最好一定要自己敲出來)
分類:
單鏈表
雙鏈表:
每一個節點有兩個指針域

		循環鏈表
				能通過任何一個節點找到其他所有的節點
		非循環鏈表

(java中變成垃圾內存則會自動釋放,但是C和C++則不會,所以要
手動釋放,否則會引起內存泄露。delete等於free)
算法:
遍歷
查找
清空
銷燬
求長度
排序
刪除節點
插入節點
算法:狹義的算法是與數據的存儲方式密切相關
廣義的算法是與數據的存儲方式無關
泛型:(給你一種假象,只不過牛人從內部都弄好了)
利用某種技術達到的效果就是:不同的存儲方式,執行的操作是一樣的

算法的真正學法:很多算法你根本解決不了!!!!!!因爲很多都屬於
數學上的東西,所以我們把答案找出來,如果能看懂就
行,但是大部分人又看不懂,分三步,按照流程,語句,
試數。這個過程肯定會不斷地出錯,所以不斷出錯,不斷
改錯,這樣反覆敲很多次,纔能有個提高。實在看不懂
就先背會。

	鏈表的優缺點:
			優點:
				空間沒有限制
				插入刪除元素很快
			缺點:
				存取速度很慢。

線性結構的兩種常見應用之一   棧   (存儲數據的結構)
	定義
		一種可以實現“先進後出” 的存儲結構
		棧類似於箱子
	
	分類
		靜態棧 (類似於用數組實現)
		動態棧 (類似於用鏈表實現)
	
	算法(往裏放,從裏取)
		出棧
		壓棧(參看Java中線程的例子,成產消費的例子)
	
	應用
		函數調用
		中斷
		表達式求值(用兩個棧,一個存放數字,一個存放符號)
		內存分配
		緩衝處理
		迷宮
	
線性結構的兩種常見應用之二   隊列
	定義:
		一種可是實現“先進先出”的存儲結構
	分類:
		鏈式隊列:用鏈表實現
		
		靜態隊列:用數組實現
			靜態對流通常都必須是循環隊列,爲了減少
			內存浪費。
			
			循環隊列的講解:
				1、 靜態隊列爲什麼必須是循環隊列
				2、	循環隊列需要幾個參數來確定 及其含義
					需要2個參數來確定
						front
						 
						rear
						  					    					
					
				3、 循環隊列各個參數的含義

						2個參數不同場合不同的含義?    
			            建議初學者先記住,然後慢慢體會

				         1)隊列初始化
							front和rear的值都是零
						 2)隊列非空
							front代表隊列的第一個元素
							rear代表了最後一個有效元素的下一個元素
						 3)隊列空
							front和rear的值相等,但是不一定是零
			  4、	循環隊列入隊僞算法講解
			    	兩步完成:
			    	1)將值存入r所代表的位置
			    	2)將r後移,正確寫法是 rear = (rear+1)%數組長度
			    	錯誤寫法:rear=rear+1;
			    	
				5、 循環隊列出隊僞算法講解
					front = (front+1) % 數組長度
				
				6、 如何判斷循環隊列是否爲空
					如果front與rear的值相等,
					則隊列一定爲空
				
				7、 如何判斷循環隊列是否已滿
					預備知識:
						front的值和rear的值沒有規律,
						即可以大,小,等。
				
					兩種方式:
						1、多增加一個表標識的參數
						2、少用一個隊列中的元素(才一個,不影響的)
						通常使用第二種方法
						如果r和f的值緊挨着,則隊列已滿
						用C語言僞算法表示就是:
							if( (r+1)%數組長度 == f )
								已滿
							else
								不滿
		
	隊列算法:
						入隊
						出隊
				隊列的具體應用:
						所有和事件有關的操作都有隊列的影子。
						(例如操作系統認爲先進來的先處理)

專題:遞歸【這對你的編碼能力是個質的飛躍,如果你想成爲一個很厲害的
程序員,數據結構是必須要掌握的,因爲計算機專業的本科生也達不到這水
平!計算機特別適合用遞歸的思想來解決問題,但是我們人類用遞歸的思想
來考慮問題就會感到十分困擾,這也是很多學過遞歸的人一直都搞不明白的
地方!那是不是遞歸可以隨便寫,當然不是,有些同學一用遞歸程序就死翹
翹了。遞歸的思想是軟件思想的基本思想之一,在樹和圖論上面,幾乎全是
用遞歸來實現的,最簡單,像求階乘這種沒有明確執行次數的問題,都是用
遞歸來解決】
	定義:
		一個函數自己直接或間接調用自己(一個函數調用另外
		一個函數和他調用自己是一模一樣的,都是那三步,
		只不過在人看來有點詭異。)
		
	遞歸滿足的三個條件:
		1、遞歸必須得有一個明確的終止條件
		2、該函數處理的數據規模必須在遞減
		3、這個轉化必須是可解的。
	
	循環和遞歸:
			理論上循環能解決的,肯定可以轉化爲遞歸,但是這個
			過程是複雜的數學轉化過程,遞歸能解決不一定能轉化
			爲循環,我們初學者只要把經典的遞歸算法看懂就行,
			至於有沒有能力運用看個人。		
			
			遞歸:
				易於理解
				速度慢
				存儲空間大
			循環
				不易於理解
				速度快
				存儲空間小
			
	舉例:    
		1.求階乘
		2.1+2+3+4+。。。+100的和
		3.漢諾塔
		【漢諾塔】這不是線性遞歸,這是非線性遞歸!
		n=1      1
		n=2      3
		n=3      7
		.........
		.........
		n=64     2的64次方減1【這是個天文數字,就算世界上最快的計算機
		也解決不了,漢諾塔的負責度是2的n次方減1】問題很複雜,但真正解決
		問題的編碼只有三句。
		4.走迷宮(CS的實現)
		
		遞歸的運用:
			樹和森林就是以遞歸的方式定義的
			樹和圖的很多算法都是以遞歸來實現的
			很多數學公式就是以遞歸的方式定義的
				斐波拉契序列
					1 2 3 5 8 13 21 34。。。

爲何數據結構難學:因爲計算機內存是線性一維的,而我們要處理的數據
都是比較複雜的,那麼怎麼把這麼多複雜的數據保存在計算機中來保存本
身就是一個難題,而計算機在保存線性結構的時候比較好理解,尤其是數
組和鏈表只不過是連續和離散的問題,線性結構是我們學習的重點,因爲
線性算法比較成熟,無論C++還是Java中都有相關的工具例如Arraylist.
Linkedlist,但是在Java中沒有樹和圖,因爲非線性結構太複雜了,他的
操作遠遠大於線性結構的操作。即使SUN公司也沒造出來。
小複習一下:_
邏輯結構:(就是在你大腦裏面能產生的,不考慮在計算機中存儲)
線性(用一根直線穿)
在計算機中存儲用:
數組
鏈表
棧和隊列是一種特殊的線性結構,是具體應用。
(操作受限的線性結構,不受限的應該是在任何地方可以增刪改查
可以用數組和鏈表實現。只要把鏈表學會,棧和隊列都能搞定,數
組稍微複雜一些。)
非線性:


物理結構:
數組
鏈表

模塊二:非線性結構(現在人類還沒有造出一個容器,能把樹和圖
都裝進去的,因爲他們確實是太複雜了)
(都要靠鏈表去實現)

樹定義
專業定義:
1、有且只有一個稱爲根的節點
2、有若干個互不相交的子樹,這些子樹本身也是一棵樹

				通俗定義:
					1、樹是由節點和邊組成
					2、每個節點只有一個父節點但可以有多個子節點
					3、但有一個節點例外,該節點沒有根節點,此節點稱爲根節點
			
				專業術語
					節點    父節點      子節點
					子孫    堂兄弟      
					深度:
						從根節點到最底層節點的層數稱之爲深度
						根節點是第一層
					葉子節點;(葉子就不能劈叉了)
						沒有子節點的節點
					非終端節點:
						實際就是非葉子節點。
					根節點既可以是葉子也可以是非葉子節點
					度:
						子節點的個數稱爲度。(一棵樹看最大的)			
		樹分類:
			一般樹
				任意一個節點的子節點的個數都不受限制
			二叉樹(有序樹)
				任意一個節點的子節點的個數最多兩個,且子節點
				的位置不可更改。
				
				分類:
					一般二叉樹
					滿二叉樹
						在不增加樹的層數的前提下。無法再多
						添加一個節點的二叉樹就是滿二叉樹。
					完全二叉樹
						如果只是刪除了滿二叉樹最底層最右邊的
						連續若干個節點,這樣形成的二叉樹就是
						完全二叉樹。
					
			森林
				n個互不相交的樹的集合

一般的二叉樹要以數組的方式存儲,要先轉化成完全二叉樹,因爲如果你
只存有效節點(無論先序,中序,後序),則無法知道這個樹的組成方式
是什麼樣子的。

		樹的存儲(都是轉化成二叉樹來存儲)
			二叉樹的存儲
				連續存儲【完全二叉樹】
					優點:
						查找某個節點的父節點和子節點(也包括判斷有咩有)速度很快
					缺點:
						耗用內存空間過大
				
				鏈式存儲
				
			一般樹的存儲
				雙親表示法
					求父節點方便
				孩子表示法
					求子節點方便
				雙親孩子表示法
					求父節點和子節點都很方便
				二叉樹表示法
					把一個普通樹轉化成二叉樹來存儲
					具體轉換方法:
						設法保證任意一個節點的
							左指針域指向它的第一個孩子
							有指針域指向它的下一個兄弟
						只要能滿足此條件,就可以把一個普通樹轉化成二叉樹
						一個普通樹轉化成的二叉樹一定沒有右子樹
					
			
			森林的存儲
			    先把森林轉化爲二叉樹,再存儲二叉樹,具體方式爲:根節點
			    之間可以當成是兄弟來看待
			
		二叉樹操作
			遍歷
                  
                  先序遍歷【先訪問根節點】
                  		先訪問根節點
                  		再先序訪問左子樹
                  		再先序訪問右子樹
                  
                  中序遍歷【中間訪問根節點】
                  		中序遍歷左子樹
                  		再訪問根節點
                  		再中序遍歷右子樹
                  		
                  後序遍歷【最後訪問根節點】
                  		先後序遍歷左子樹
                  		再後序遍歷右子樹
                  		再訪問根節點
                  
              已知兩種遍歷序列求原始二叉樹 
              		通過先序和中序 或者 中序和後續我們可以
              		還原出原始的二叉樹
              		但是通過先序和後續是無法還原出原始的二叉樹的
              		
              		換種說法:
              			只有通過先序和中序, 或通過中序和後序
              			我們纔可以唯一的確定一個二叉樹				  
			  
			應用
				樹是數據庫中數據組織的一種重要形式(例如圖書館
				的圖書分類一層一層往下分。)
				操作系統子父進程的關係本身就是一棵樹
				面嚮對象語言中類的繼承關係本身就是一棵樹
				赫夫曼樹(樹的一個特例)


圖

模塊三:查找和排序
折半查找

	排序:
			冒泡
			插入
			選擇
			快速排序
			歸併排序
	
	排序和查找的關係
		排序是查找的前提
		排序是重點

Java中容器和數據結構相關知識
Iterator接口
Map
哈希表(與Java關係比較大)

再次討論什麼是數據結構:
數據結構研究是數據結構的存儲和數據的操作的一門學問
數據的存儲分爲兩部分:
個體的存儲
個體關係的存儲
從某個角度而言,數據的存儲最核心的就是個體關係
的存儲,個體的存儲可以忽略不計。

再次討論到底什麼是泛型:
同一種邏輯結構,無論該邏輯結構物理存儲是什麼樣子的
我們都可以對它執行相同的操作(例如都是線性結構或者
用數組實現的樹和用鏈表實現的樹。利用重載技術。)

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