數據結構概述(教材選用嚴蔚敏、吳偉民,該書程序是僞算法
具體的程序是高一凡,西電的,大牛,只有
程序。還有一本書,臺灣的黃國瑜自己寫的
只有思路,程序是另外一個合作的清華的寫
的,可惜很多錯的。)
學完數據結構之後會對面向過程的函數有一個更深的瞭解
定義
我們如何把現實中大量而複雜的問題以特定的數據類型(單
個數據怎樣存儲?)和特定的存儲結構(個體的關係)
保存到主存儲器(內存)中,以及在此基礎上爲實現某個功能
(比如查找某個元素,刪除某個元素,對所有元素進行排序)
而執行的相應操作,這個相應的操作也叫算法。(比如班裏有
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關係比較大)
再次討論什麼是數據結構:
數據結構研究是數據結構的存儲和數據的操作的一門學問
數據的存儲分爲兩部分:
個體的存儲
個體關係的存儲
從某個角度而言,數據的存儲最核心的就是個體關係
的存儲,個體的存儲可以忽略不計。
再次討論到底什麼是泛型:
同一種邏輯結構,無論該邏輯結構物理存儲是什麼樣子的
我們都可以對它執行相同的操作(例如都是線性結構或者
用數組實現的樹和用鏈表實現的樹。利用重載技術。)
數據結構_郝斌老師自學大綱
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.