数据结构与算法(六)-背包、栈和队列
生活随笔
收集整理的這篇文章主要介紹了
数据结构与算法(六)-背包、栈和队列
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言:許多基礎數據類型都和對象的集合有關。具體來說,數據類型的值就是一組對象的集合,所有操作都是關于添加、刪除或是訪問集合中的對象。而且有很多高級數據結構都是以這樣的結構為基石創造出來的,在本文中,我們將了解學習三種這樣的數據類型,分別是背包(Bag)、棧(Stack)和隊列(Queue)
一、學習感悟
對于數據結構的學習可以用以下步驟來學習:- 首先先對該結構的場景操作進行API化;
- 然后再對數據類型的值的所有可能的表示方法以及各種操作的實現;
- 總結特點和比較;
二、API
這三種數據類型都是依賴于之前介紹過的線性表的鏈式存儲結構的,所以理解并掌握鏈式結構是學習各種算法和數據結構的第一步,若還不是很清楚,可以看一下前面關于線性表的鏈式存儲結構的文章(本文主要是對鏈式存儲結構的進行介紹,如想要對順序存儲結構了解的話,可根據其特性和API進行編寫代碼,歡迎在評論區留言討論)。2.1、背包(Bag)
背包是一種不支持從中刪除元素的集合數據類型——它的目的就是幫助用例收集元素并迭代遍歷所有收集到的元素(用例也可以檢查背包是否為空或者獲取背包中元素的數量)。 要理解背包的概念,可以想象一個喜歡收集彈珠球的人。他將所有的彈珠球都放在一個背包里,一次一個,并且會不時在所有的彈珠球中尋找某一顆;?
2.1.1 背包API
根據以上的需求,可以寫出背包的API: public class Bag<Item> implements Iterable<Item>Bag() 創建一個空背包 void add(Item item) 添加一個元素 boolean isEmpty() 背包是否為空 int size() 背包中的元素數量 使用Bag的API,用例可以將元素添加進背包并根據需要隨時使用foreach語句訪問所有的元素。用例也可以使用棧或是隊列,但是用Bag可以說明元素的處理順序不重要,比如在計算一堆Double值的平均值時,無需關注背包元素相加的順序,只需要在得到所有值的和后除以Bag中元素的數量即可。2.1.2 背包實現
根據2.1.1的API寫出具體的實現,其中關鍵方法add使用了頭插法: public class Bag<T> implements Iterable<T> {private Node<T> first;private Integer size;Bag() {first = new Node<>();first.next = null;size = 0;}//由于Bag類型不需要考慮元素的相對順序,所以這里我們可以使用頭插法來進行插入,提高效率public void add(T t) {Node<T> newNode = new Node<>();newNode.t = t;newNode.next = first.next;first.next = newNode;size++;}public Boolean isEmpty() {return size < 1;}public Integer size() {return size;}class Node<T> {T t;Node<T> next;}@Overridepublic Iterator<T> iterator() {return new ListIterator();}class ListIterator implements Iterator<T> {private Node<T> current = first.next;@Overridepublic boolean hasNext() {return current!=null;}@Overridepublic T next() {T t = current.t;current = current.next;return t;}}public static void main(String[] args) {Bag<Integer> bag = new Bag<>();for (int i = 1; i <= 100; i++) {bag.add(i);}double sum = 0;Iterator<Integer> iterator = bag.iterator();while (iterator.hasNext()) {sum = sum + iterator.next();}System.out.println("和:"+sum);double size = bag.size();String format = new DecimalFormat("0.00").format(sum / size);System.out.println("平均值:"+format);} } Bag.java核心代碼為add(),使用了頭插法::
//由于Bag類型不需要考慮元素的相對順序,所以這里我們可以使用頭插法來進行插入,提高效率public void add(T t) {Node<T> newNode = new Node<>();newNode.t = t;newNode.next = first.next;first.next = newNode;size++;}2.1.3 總結
上面就是關于Bag數據類型的實現,從中可以看出Bag是一種不支持刪除元素的、無序的、專注于取和存的集合類型。2.2、棧(Stack)
下壓棧(或簡稱棧)是一種基于后進先出(LIFO)策略的集合類型。比如在桌子上對方一疊書,我們拿書時,一般都是從最上面開始取的,這樣的操作就類似棧。?
棧管理數據的兩種操作如下:- 寫入數據(堆積)操作稱作入棧(PUSH);
- 讀取數據操作稱作出棧(POP);
2.2.1 棧API
根據對以上理解寫出背包的API: public class Stack<Item> implements Iterable<Item>Stack() 創建一個空棧 void push(Item item) 添加一個元素Item pop() 刪除最近添加的元素 boolean isEmpty() 棧是否為空 int size() 棧中的元素數量2.2.2 棧實現
根據上面的棧API實現其方法,還是使用頭插法來實現: public class Stack<T> implements Iterable<T> {private Node<T> head;private Integer size;Stack() {head = new Node<>();head.next = null;size = 0;}//頭插法public void push(T t) {Node<T> first = head.next;head.next = new Node<>();head.next.t = t;head.next.next = first;size++;}//取的時候從最上面開始取,也就是最近插入的元素public T pop() {Node<T> first = head.next;head.next = first.next;size--;return first.t;}public Boolean isEmpty() {return size < 1;}public Integer size() {return size;}class Node<T> {T t;Node<T> next;}@Overridepublic Iterator<T> iterator() {return new ListIterator<T>();}class ListIterator<T> implements Iterator<T> {private Node<T> current = (Node<T>) head.next;@Overridepublic boolean hasNext() {return current!=null;}@Overridepublic T next() {T t = current.t;current = current.next;return t;}}public static void main(String[] args) {Stack<Integer> stack = new Stack<>();for (int i = 0; i < 10; i++) {stack.push(i);System.out.println("push --> "+i);}Iterator<Integer> iterator = stack.iterator();while (iterator.hasNext()) {System.out.println("pop --> "+iterator.next());}} } Stack.java 核心方法為push()和pop(): //頭插法public void push(T t) {Node<T> first = head.next;head.next = new Node<>();head.next.t = t;head.next.next = first;size++;}//取的時候從最上面開始取,也就是最近插入的元素public T pop() {Node<T> first = head.next;head.next = first.next;size--;return first.t;} 運行結果:2.2.3 總結
它可以處理任意類型的數據,所需的空間總是和集合的大小成正比,操作所需的時間總是和集合的大小無關。- 先進后出;
- 具有記憶功能,棧的特點是先進棧的后出棧,后進棧的先出棧,所以你對一個棧進行出棧操作,出來的元素肯定是你最后存入棧中的元素,所以棧有記憶功能;
- 對棧的插入與刪除操作中,不需要改變棧底指針;
- 棧可以使用順序存儲也可以使用鏈式存儲,棧也是線性表,因此線性表的存儲結構對棧也適用線性表可以鏈式存儲;
2.3、隊列(Queue)
先進先出隊列(或簡稱隊列)是一種基于先進先出(FIFO)策略的集合類型。在生活中這種模型結構的示例有很多,比如說排隊上公交、排隊買火車票、排隊過安檢等都是先進先出的策略模型。?
隊列是一種特殊的線性表,特殊之處在于它只允許在表的前端進行刪除操作,而在表的后端進行插入操作,和棧一樣,隊列是一種操作受限制的線性表,進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。 像排隊一樣,一定是從最先的數據開始序按順處理數據的數據結構,就成為“隊列”,而像這類模型策略,被稱為FIFO(first in,first out)或者LILO(last in,last out)。 隊列在通信時的電文發送和接收中得到了應用。把接收到的電文一個一個放到了隊列中,在時間寬裕的時候再取出和處理。 當用例使用foreach語句迭代訪問隊列中的元素時,元素的處理順序就是他們被添加到隊列中的順序,而在程序中使用它的原因是在用集合保存元素的同時保存它們的相對順序:使它們入列順序和出列順序相同。2.3.1 隊列API
綜上所述,隊列的API為: public class Queue<Item> implements Iterable<Item>Queue() 創建一個空隊列 void enqueue(Item item) 添加一個元素Item dequeue() 刪除最近添加的元素 boolean isEmpty() 隊列是否為空 int size() 隊列中的元素數量2.3.2 隊列實現
根據2.3.1的API編寫隊列的實現: public class Queue<T> implements Iterable<T> {private Node<T> head;private Node<T> tail;private Integer size;Queue() {head = new Node<>();tail = null;head.next = tail;tail = head;size = 0;}//從隊列的尾部插入數據public void enqueue(T t) {Node<T> oldNode = tail;tail = new Node<>();tail.t = t;tail.next = null;if (isEmpty())head.next = tail;elseoldNode.next = tail;size++;}//從隊列的頭部取數據public T dequeue() {Node<T> first = head.next;head.next = first.next;return first.t;}public Boolean isEmpty() {return size < 1;}public Integer size() {return size;}class Node<T> {T t;Node<T> next;}@Overridepublic Iterator<T> iterator() {return new ListIterator();}class ListIterator implements Iterator<T> {private Node<T> current = head.next;@Overridepublic boolean hasNext() {return current!=null;}@Overridepublic T next() {T t = current.t;current = current.next;return t;}}public static void main(String[] args) {Queue<Integer> queue = new Queue<>();for (int i = 0; i < 10; i++) {queue.enqueue(i);System.out.println("enqueue --> "+i);}Iterator<Integer> iterator = queue.iterator();while (iterator.hasNext()) {System.out.println("dequeue --> "+iterator.next());}} } Queue.java 核心方法為enqueue()和dequeue(): //從隊列的尾部插入數據public void enqueue(T t) {Node<T> oldNode = tail;tail = new Node<>();tail.t = t;tail.next = null;if (isEmpty())head.next = tail;elseoldNode.next = tail;size++;}//從隊列的頭部取數據public T dequeue() {Node<T> first = head.next;head.next = first.next;return first.t;} 運行結果:2.3.3 總結
隊列做的事情有很多,包括我們常用的一些MQ工具,也是有隊列的影子。- 先進先出;
- 特殊的線性結構;
- 關注于元素的順序,公平性;
三、背包、棧和隊列的比較
背包:不關注元素的順序,不支持刪除操作的集合類型; 棧:先進后出,具有記憶性,多應用于需要記憶功能的業務; 隊列:先進先出,可以應用于緩沖;?本系列參考書籍:
《寫給大家看的算法書》
《圖靈程序設計叢書 算法 第4版》
轉載于:https://www.cnblogs.com/lfalex0831/p/9681804.html
總結
以上是生活随笔為你收集整理的数据结构与算法(六)-背包、栈和队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 批处理之坑爹的感叹号和变量延迟扩展
- 下一篇: 关于npm 的注意事项