內容

數據結構概述

定義:我們如何把現實中大量而復雜的問題以特定的數據類型和特定的存儲結構保存到主存儲器(內存)中,以及在此基礎上為實現某個功能(比如查找某個元素,刪除某個元素、對所有元素進行排序)而執行的相應操作,這個相應的操作也叫算法。

數據結構=個體+個體的關系

算法=對存儲數據的操作(依附于存儲的,存儲方式不一樣,算法也不一樣)

算法:

解題的方法和步驟

衡量算法的標準:1.時間復雜度 ?(大概程序要執行的次數,而非執行的時間)2.空間復雜度(算法執行過程中大概所占用的最大內存)3.難易程度 ? 4.健壯性

數據結構的地位:數據結構是軟件中最核心的課程

程序=數據的存儲+數據的操作+可以被計算機執行的語言

預備知識

指針:指針是C語言的靈魂,

CPU訪問內存的3條線:地址線、控制線、數據線

? ? ? 定義:地址,內存單元的編號,從0開始的非負整數,范圍0-FFFFFFFF(4G-1)

指針:指針就是地址,地址就是指針

指針變量是存放內存單元地址的變量

指針的本質是一個操作受限的非負整數

*p是不確定的內存存儲地址,垃圾數據,不允許賦值

一個軟件所分配到的空間中極可能存在著以前其他軟件使用過后的殘留數據,這些數據被稱之為垃圾數據。所以通常情況下我們為一個變量,為一個數組,分配好存儲空間之后都要對該內存空間初始化。

注意:

指針變量也是變量,只不過它存放的不能是內存單元的內容,只能存放內存單元的地址

普通變量前不能加*

常量和表達式前不能加&

指針變量也是變量,只不過它存放的不能是內存單元的內存

基本概念

int ? i=10;

int ? *P=&i; //等價于 ?int*p; ?P=&i:

詳解這兩步操作:

1.p存放了i的地址,所以我們說p指向了i

2.p和i是完全不同的兩個變量,修改其中的任意一個變量的值不影響另一個;

3.p指向i,*p就是i變量本身,更形象地說所有出現*p的地方都可以換乘i,i的地方都可以換成*p

如何通過被調函數修改主調函數中普通變量的值:

? ? ?1.實參為相關變量的地址;2.形參為以該變量的類型為類型的指針變量;3.在被調函數中通過“*形參變量名”的方式就可以修改主函數


int ?*p ? ? ? ? ? ?//p是個指針變量,int*表示該p變量只能存儲int類型變量的地址。

指針和數組:

指針和一維數組

數組名:1.一維數組名是個指針常量;2.它存放的是一維數組第一個元素的地址;3.它的值不能被改變;4.一維數組名指向的是數組的第一個元素

下標和指針的關系:a[i]等價于*(a+i)

假設指針變量的名字為p,則p+i的值是p+i*(p所指向的變量所占的字節數)

指針變量的運算: 1.指針變量不能相加,不能相乘,不能相除;2.如果兩指針變量屬于同一數組,則可以相減;3.指針變量可以加減一整數,前提是最終結果不能超過指針 ?a [3]==*(3+a)

舉例:

如何通過被調函數修改主調函數中一維數組的內容:

兩個參數:1.存放數組首元素的指針變量;2.存放數組元素長度的整型變量;

一個字節8位,1個字節一個地址

所有的指針變量只占4個字節,用第一個字節的地址表示整個變量的地址

第7課:如何通過函數修改實參的值?

要想改寫值,只需改寫指針的變量


結構體

1.為什么會出現結構體:為了表示一些復雜的數據,而普通的基本類型變量無法滿足要求。

struct Studnet

{

int sid;

String name;

int sage;

}

2.什么叫結構體:結構體是用戶根據實際需要自己定義的符合數據類型;


如何使用結構體

兩種方式:

struct? Student st={1000,"zhangsan",200};

struct ?Strudeng *pst=&st;

1.st.sid

2.pst->sid

pst所指向的結構體變量中sid這個成員

注意事項:

1.結構體變量不能加減乘除,但可以相互賦值;

2.普通結構體變量和結構體指針變量作為喊出傳參的問題

動態內存的分配與釋放


模塊一:線性結構【把所有的節點用一根直線穿起來】

連續存儲【數組】

? 1.什么叫數組:

元素類型相同,大小相同

? ?2.數組的優缺點:


離散存儲【鏈表】

typedef的用法:為已有的數據類型起個名字,例如:typedef int ZHANGSAN;//為int再重新多取一個名字,ZHANGSAN等價于int



定義:1.N個節點離散分配;2.彼此通過指針相連;3.每個節點只有一個前驅節點,每個節點只有一個后續節點;4.首節點沒有前驅節點,尾節點沒有后續節點;

專業術語:

首節點:第一個有效節點

尾節點:最后一個有效節點

頭結點:頭結點的數據類型和首節點類型一樣,第一個有效節點之前的那個節點;頭結點并不存放有效數據;加頭結點的目的主要是為了方便對鏈表的操作;

頭指針:指向頭結點的指針變量

尾指針:指向尾節點的指針變量

如果希望通過一個函數來對鏈表進行處理,我們至少需要接受鏈表的哪些參數:只需要一個參數:頭指針,因為我們通過頭指針可以推算出鏈表的其他參數。



分類:1.單鏈表:2.雙鏈表:每一個節點有兩個指針;3.循環鏈表:能通過任何一個節點,找到其他所有的節點;4.非循環鏈表:

算法:

?1.遍歷 ??2.查找 ? ?3.清空 ??4.銷毀 ??5.求長度 ?6.排序 ?7.刪除節點:8.插入節點:

非循環單鏈表插入節點偽算法:

r->p->pNext; ? p->pNext=q; ? ?q->pNext=r;

刪除非循環單鏈表節點偽算法:

r=p->pNext;

p->pNext=p->pNext->pNext;(單獨這一條容易內存泄漏)

free(r);

注意:free p;//刪除p指向節點所占的內存,不是刪除p本身所占內存;

p->pNext; //p所指向結構體變量中的pNext成員本身;

算法:

狹義的算法是與數據的存儲方式密切相關;廣義的算法是與數據的存儲方式無關

泛型:利用某種技術達到的效果就是不同的存儲方式,執行的操作是一樣的



鏈表的優缺點:

線性結構的兩種常見應用之一 ?棧

1.定義:一種可以實現“先進后出”的存儲結構,棧類似于箱子

2.分類:

? ? 靜態棧:這種分配策略是將整個數據的數據空間設計為一個棧.每當調用一個過程時,它所需要的數據空間就分配在棧頂,每當工作結束時就釋放這部分空間.過程所需的數據空間包括兩部分:一部分是生存期在本過程這次活動中的數據對象,如局部變量,參數單元,臨時變量等等;另一部分則是用以管理過程活動的記錄信息.即當一次過程調用出現時,調用該過程的那個過程的活動即被中斷,當前機器的狀態信息,諸如程序計數器(返回地址).,存器的值等等,也都必須保留在棧中.當控制從調用返回時,便根據棧中記錄的信息恢復機器狀態,使該過程的活動繼續進行

? 動態棧:如果一個程序語言提供用戶自由地申請數據空間和退還數據空間的機制(如new init),或者不僅有過程而且有進程的程序結構的情況下,空間的使用未必服"先申請后釋放,后申請先釋放"的原則,那么棧式的動態分配方案就不適用了.通常使用一種稱為堆式的動態存儲分配方案.假設程序允許時有一個大的存儲空間,每當需要時就從這片空間中借用一塊,不用時再退還,由于借還的時間先后不一,經一段運行之后,程序運行空間將被分劃成許多塊塊,有些占用,有些空閑.那么當運行程序要求一塊體積為N的空間時,需要決定應該從哪個空閑塊得到這個空間.理論上講,應該從比N稍大一些的空閑塊中取出N個單元,以便使大的空閑塊派更大的用場,但實際難度很大,實際中常常采用的辦法是:先遇到哪塊比N大就從其中取出N個單元.即使這樣,也會發生找不到一塊比N大的空閑塊,但所有空閑塊的總和比N大得多的情況,這時,有的分配管理系統采用廢品回收的辦法來應付.


3.算法:

出棧

壓棧

4.應用:top\ bottom

a .函數調用;b.中斷;c.表達式求值;d.內存分配

線性結構的兩種常見的應用之二 ?隊列

定義:一種可以實現“先進先出”的存儲結構(出隊、入隊)

分類:

?1.鏈式隊列--用鏈表實現:

?2.靜態隊列-用數組實現:靜態隊列通常都必須是循環隊列

循環隊列的講解:

? ? ? ? 1.靜態隊列為什么必須是循環隊列:


靜態隊列通常都必須是循環隊列,為了減少內存浪費;如果用傳統意義的數組實現隊列,無論入隊還是出隊,rear和front指針只能+不能-;比F元素下標小的數組元素下標就浪費了。

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.如何判斷隊列是否已滿


上圖這種情況,如果再插入f,r指向同一個元素。如果這樣的話就不能判斷隊列是空還是滿。

所以為了判斷循環隊列是否已滿,有一下兩種方式:

1、多增加一個表標識的參數

2、少用一個隊列中的元素(才一個,不影響的)

如果r和f緊挨著(r的下一個位置是f),則隊列已滿

隊列的具體應用:所有和時間有關的操作都有隊列的影子

專題:遞歸

定義:一個函數直接調用自己或者通過一系列的調用語句間接地調用自己的函數,稱做遞歸函數;

? ?當在一個函數的運行期間調用另一額函數時,在運行被調用函數之前,系統需先完成3件事:(1)將所有的實參、返回地址等信息傳遞給被調用函數保存;(2)為被調用函數的局部變量分配存儲區;(3)將控制轉移到被調函數的入口,而從被調用函數返回調用函數之前,系統也應完成3件工作:(1)保存被調函數的計算結果;(2)釋放被調函數的數據區;(3)依照被調函數保存的返回地址將控制轉移到調用函數。當有多個函數構成嵌套調用時,按照“后調用先返回”的原則,上述函數之前的信息傳遞和控制轉移必須通過“棧”來實現,即系統將整個程序運行時所需的數據空間安排在一個棧中,每當調用一個函數時,就為它在棧頂分配一個存儲區,每當從一個函數退出時,就釋放它的存儲區,則當前正運行的函數的數據區必在棧頂。

舉例:

1.1+2+3+4+...100的和

2.求階乘

3.漢諾塔

漢諾塔(又稱河內塔)問題是源于印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。并且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。有預言說,這件事完成時宇宙會在一瞬間閃電式毀滅。也有人相信婆羅門至今還在一刻不停地搬動著圓盤


遞歸滿足三個條件:

?1.遞歸必須得有一個明確的終止條件;2、該函數所處理的數據規模必須在遞減;3.這個轉化必須是可解的。

循環和遞歸

? ? ?1.遞歸:易于理解、速度慢、存儲空間大

? ? ? 2.循環:不易理解、速度快、存儲空間小


對計算機而言,一個函數調用別的函數與調用它自己是沒有區別的。對機器而言,遞歸和普通函數的調用沒有區別,都是借用堆棧把函數的返回地址,函數的局部變量,調用函數的實參壓棧。

遞歸的思想是軟件思想的最基本思想之一,在樹和圖論上面,幾乎全是用遞歸來實現的,最簡單的,大家用的資源管理器,我邀請無紙巾的創建用戶目錄,這實際就是冬天構造樹的問題,要用到遞歸的知識來解決。

遞歸的應用:

? ? ? 樹和森林就是以遞歸的方式定義的

? ? ? 數和圖的很多算法

? ? ? 很多數學公式就是以遞歸的方式定義的。

? ? ?菲波拉契序列:1 ? ?2 ? ?3 ? ?5 ? ? 8 ? ?13 ? ?21 ? ?34。

? 復習內容:

? ? ? 邏輯結構:? ? ? 線性(數組、鏈表)、

? ? ? ? 注意:棧和隊列是一種特殊的線性結構

? ? ? ? ? ? ? ? ? ? ? ? ? ?非線性(樹、圖)

?物理結構:

4.走迷宮

? ? ?

模塊二:非線性結構

專業定義:

1.有且只有一個稱為根的節點

2.有若干個互不相交的子樹,這些子樹也是一棵樹

通俗定義:

1.樹是有節點和邊組成

2.每個節點只有一個父節點,但可以有多個子節點

3.但有一個節點例外,該節點沒有父節點,此節點稱為根節點

專業術語

? ? ?節點 ? ? 父節點 ? ? 子節點 ? ?子孫 ? ?堂兄弟、雙親


1.度(Degree):結點擁有的子樹數稱為結點的度。A的度為3,C的度為1,F的度為0,

2.葉子(Leaf):度為0的結點稱為葉子(leaf)或者結點.結點k、L、F、G、M、I、J都是樹的葉子。(沒有子結點的結點)

3.度不為0的結點稱為非終端結點或分支結點。除了根結點之外,分支結點也稱為內部結點。(實際就是非葉子節點)

4.樹的度是樹內各結點的度的最大值。如B樹的度為3,。結點的子樹的根稱為該節點的孩子,相應地,該結點稱為孩子的雙親。同一個雙親的孩子之間互稱兄弟

5.結點的祖先是從根到該結點所經分支上的所有結點。反之,以某結點為根的子樹種的任一結點都稱為該結點的子孫

6.深度:樹中結點的最大層次稱為樹的深度(Depth)或高度,B樹的深度為4。從根節點到最底層節點的層數稱之為深度,根節點是第一層

7.如果將樹中結點的各子樹看成從左至右是有次序的(即不能互換),則稱該樹為有序樹,否則稱為無序樹

分類:

一般樹:任意一個節點的子節點的個數都不受限制

二叉樹:任意一個節點的子節點的個數最多兩個,且子節點的位置不可更改

分類:一般二叉樹、滿二叉樹:在不增加樹的層數的前提下,無法再多添加一個節點的二叉樹就是滿二叉樹;

完全二叉樹:如果只是刪除了滿二叉樹最底層最右邊的連續若干個節點,這樣形成的二叉樹就是完全二叉樹

森林:n個互不相交的樹的集合

樹的存儲

二叉樹的存儲

1.連續存儲【完全二叉樹】

優點:查找某個節點的子節點或父節點,(也包括判斷有沒有子節點)速度很快

缺點:耗用內存空間過大

2.鏈式存儲:



一般樹的存儲

? ? ? ? 1.雙親表示法:求父節點方便


? ? ? ? ?2.孩子表示法:求子節點方便


? ? ? ? ??

? ? ? ? ?3.雙親孩子表示法:求父節點和子節點都很方便

? ? ? ? ? ??

? ? ? ? ? 4.二叉樹表示法:

? 把一個普通樹轉化成二叉樹來存儲

具體轉化方法:1.設法保證任意一個節點的

? ? ?左指針域指向它的第一個孩子,

? ? ? 右指針指向它下一個兄弟結點

? ? ? 只要能滿足此條件,就可以把一個普通樹轉化為二叉樹

一個普通樹轉化成的二叉樹一定沒有右子樹


森林的存儲

? ? ??

操作(重點):

1.二叉樹操作

遍歷


先序遍歷【先訪問根節點】:先訪問根節點,再先序訪問左子樹,再先序訪問右子樹

中序遍歷【中間訪問根節點】:中序遍歷左子樹,再訪問根節點,再中序遍歷右子樹



后序遍歷【最后訪問根節點】:先中序遍歷左子樹,再中序遍歷右子樹,再訪問根節點


已知兩種遍歷序列求原始二叉樹

1.通過先序和中序,或者中序和后序我們可以

還原出原始的二叉樹

但是通過先序和后序是無法還原出原始的二叉樹的


2.換種說法:只有通過先序和中序,或者通過中序和后序

我們才可以唯一的確定一個二叉樹

樹應用:

1.樹是數據庫中數據組織一種重要形式

2.操作系統子父進程的關系本身就是一棵樹

3.面向對象語言中類的繼承關系,本身就是一棵樹

4.赫夫曼樹







模塊三:查找和排序

排序是查找的前提

排序是重點

快速排序




折半查找、排序:冒泡、插入、選擇、快速排序、歸并排序

Java中容器和數據結構相關知識:

Iterator接口、Map ?、哈希表

再次討論什么是數據結構

1.數據結構研究是數據的存儲和數據的操作的一門學問

2.數據的存儲分為兩部分:個體的存儲、個體關系的存儲

從某個角度而言,數據的存儲最核心的就是個體關系的存儲,個體的存儲可以忽略不計

再次討論什么是泛型

同一種邏輯結構,無論該邏輯結構物理存儲是什么樣子的,我們可以對它執行相同的操作。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容