提到數據結構,不得不說數據類型,有人將他們比作分子和原子的關系,我們都知道大自然最小的構成單位是原子,數據類型描述的是原子的內部,如質子、中子的情況,而數據結構是分子,由不同的原子以各種各樣的結構組成。
先說Java的數據類型,包括八種基本類型以及對象類型,
| 內置類型 | 八種基本類型 | 值類型 | 傳輸時傳輸值本身 | 內存隨着值傳輸而變化 |
| 擴展類型 | 對象類型 | 引用類型 | 傳輸時僅傳遞引用 | 對象在內存的位置不發生變化 |
數據結構,是以上這些不同數據類型的數據元素之間以一種或者多種特定關系的集合。
注意關鍵詞:特定關系,集合。
特定關系我們可以理解為數據流動,而集合則是數據以什么樣的形式存儲。
數據結構之所以是程序語言的基礎,是因為它描述了程序如何集合數據,數據如何流動(使用數據和處理數據),而數據流動和集合的方式有很多種,抽象出來最基礎的當做磚,然后就像蓋大樓一樣,不斷重用他們,實現更復雜更高級的數據集合和流動的方式,就是算法,所以數據結構和算法是血濃於水的關系。
下面來分析一下可以做磚的,背包,棧,隊列是數據結構中數據流動的最佳領路者。
- 背包Bag,顧名思義,假設我們有一個背包,往里面塞入很多不同顏色的小球,在向外拿的時候,並不會按照我們當時塞進去的順序,而是無序的,伸手抓到哪個就拿哪個。在數據結構中,背包不支持刪除內容,它的特性是可以無序迭代已有內容,因此可以做計算均值,方差,標准差等算法的實現。總結來說背包就是只進不出,內容無序。
- 實現背包的時候要注意,需要設置屬性size,方法add()
- 棧Stack,這不是內存管理中的那個棧的概念,而是一種數據結構。棧的特性是后進先出(LIFO),比較典型的應用就是undo操作、電子郵件和網頁多開。我們在word等編輯器中操作時,會用到undo操作,退回上一次操作,查看電子郵件的時候,最新收到的未讀郵件總是會排在最上方,而較早的都排在了它的下面。網頁多開也是,當我們點擊一個超鏈接的時候,彈出來的頁面是這個超鏈接鏈入的新頁面,而不是最早打開的舊頁面,當我們關閉當前頁面的時候,看到的頁面也是第二新的頁面,而不是最早打開的舊頁面。所以,我總結棧就是為了保持我們的新鮮感,可以理解為永遠追着新聞走,喜新厭舊。
- 實現棧的時候要注意,需要設置屬性size和top(用來指向最新元素),方法push(), pop()
- 隊列Queue,這個就更好理解了,與我們日常排隊是相同的原理,當我們排隊買票的時候,肯定是先到先得,先來排隊的排到前頭的先買,后到的排在后面的后買票。所以隊列的特性是先進先出(FIFO)系統中我們常提及的任務隊列,消息隊列正是采用的這種數據結構,先排入的任務或者消息會優先被處理到。
- 實現隊列的時候要注意,需要設置屬性size,front(用來指向隊頭),rear(用來指向隊尾),方法enqueue(), dequeue()
- 注意事項:隊列的size是最初設定的,要保證front和rear之間的這一段隊列內容的長度,永遠小於等於size,但這一段內容看上去是整體向前移動。在入列出列的操作過程中,front和rear的值會越來越大,這個大是沒意義的,但會超過size值引發問題,所以我們要用相對位置,
1 front++ 改為 front = (front + 1) % size 2 rear++ 改為 rear = (rear + 1) % size
總結一下,背包、棧、隊列都是數據結構中描述數據流動方式的,但是各自訪問內部元素的順序和增刪情況不同,他們都是屬於基礎算法。
再重申一下這三種數據結構在存取順序上,各自的關注點,
背包是不關注順序,只存不取;
棧是只關注最頂部元素位置,支持存取;
隊列是要同時關注隊首和隊尾兩個位置,支持存取。
下面再分析一下稍微復雜的鏈表結構。
鏈表是一種實現起來稍微復雜的數據結構,但這種復雜給鏈表帶來了靈活輕巧,相對於以上三種傾向於數據流動的數據結構,鏈表更關注自身存儲數據的結構,但是他更強大。鏈表的實現與C語言的指針概念很像,鏈表不依賴具體的內存順序,也不一定是連續的位置,而是依賴指針的順序來連接整個鏈表。所以鏈表的單位結構是一個value加一個link。value是該單位存儲的內容,link是下一個鏈表單位對象的引用。鏈表有三種基本操作,
- 從表頭插入,定義表頭節點head(head只是“輔助線”,並不是真正存在的元素),新增元素node,
1 node.link = head.link; 2 head.link = node; - 從表尾插入,稍微復雜一點,我們要先用循環找到表尾節點(表尾節點的特點是他的link為null),找到以后,將表尾節點的link從null修改為新增節點,然后新增節點的link設置為null,下面展示一小段關鍵代碼。
1 Node n = head; 2 while(n.link != null){ 3 n = n.link; 4 } 5 n.link = newInsert; 6 newInsert.link = null; - 從表頭取出,直接取出head.link指向的節點node,
1 head.link = node.link; 2 node.link = null
由此我們可以看出,實現了這三種基本操作,鏈表可以自己去實現背包,棧和隊列的算法。
- 表頭插入,表頭取出,就實現了棧
- 表頭取出,表尾插入,就實現了隊列
- 背包只要插入,無所謂表頭插入還是表尾插入。
