摘要:常見數據結構分析及實現說明本文中的代碼是參考編程思想某培訓機構。同時還要分析這些數據結構在時間和空間上的開銷。這種專門研究應用程序中的數據之間的邏輯關系,存儲方式及其操作的學問就是數據結構。
常見數據結構分析及實現 說明
本文中的代碼是參考《Java編程思想》、某培訓機構。
文中的代碼放Github了,有興趣的可以看看,點歌star鼓勵下我。
代碼在Sublime中敲的,坑爹的GBK,注釋了很多中文,一轉碼不能用了?。?!
重點在思想,而不是實現 。再次推薦《Java編程思想》。
1、數據結構
編程的本質就是對數據(信息以數據的形式而存在)的處理,實際編程中不得不處理大量數據,因此實際動手編程之前必須先分析處理這些數據,處理數據之間存在的關系。
現實的數據元素之間有個錯綜復雜的邏輯關系,需要采用合適的物理結構來存儲這些數據,并以此為基礎對這些數據進行相應的操作。同時還要分析這些數據結構在時間和空間上的開銷。這種專門研究應用程序中的數據之間的邏輯關系,存儲方式及其操作的學問就是數據結構。
數據元素之間存在的關聯關系被稱為數據的邏輯結構,歸納起來,大致有如下四種基本的邏輯結構:
集合:數據元素之間只有"同屬于一個集合"的關系
線性關系:數據元素之間存在一個對一個的關系
樹形結構:數據元素之間存在一個對多個的關系
圖狀結構或網狀結構:數據元素之間存在多個對多個的關系。
腦補圖:
圖片>代碼>文字,個人理解,能用圖片說明問題的就不要用代碼,同理,盡量用代碼+文字解釋問題的本質。
同一種的邏輯結構,在底層通常有兩種物理存儲結構:
順序存儲結構,如一維數組
非順序存儲結構,如鏈式存儲結構(鏈表)、散列
順序結構適合讀操作(為啥呢?因為有索引啊),鏈表存儲適合寫操作(為啥呢?斷開,加上節點就完成,不需要底層復制啊)
算法的設計取決于邏輯結構:算法的實現依賴于存儲結構。對象的設計取決于類結構,(...)
什么是數據結果呢?數據結構歸納起來所要研究的問題就三方面:
數據元素之間的客觀聯系(邏輯結構)
數據在計算機內部的存儲方式(存儲結構)
針對數據實施的有效的操作和處理(算法)
對象之間的關系(對現實的抽象,繼承?組合?),存儲在內存中哪里,堆上啊,怎么存?存在數組里?hash表里?怎么處理的???增刪改查啊,排序那,加密解密啊,
Stack對于普通的線性表而言,它的作用是一個容器,用于裝具有相似結果的數據。
分為順序存儲機構和鏈式存儲結構
可以進行插入、刪除和排序的操作
如果線性表之允許在線性表的某端添加、刪除元素,這時就演變為:棧和隊列。(先進后出(彈夾),先進先出(火車站排隊))
以下圖片來自維基百科(百X百科就別看了)
原諒沒放恐怖的,來自Google(百X就別用了)
棧(Stack),是一種特殊的線性表,只能在固定的一端(線性表的尾端)進行插入、刪除操作。
允許進行插入、刪除操作的一端為棧頂(top),另一端,你猜?(bottom)
進棧:將一個元素插入棧的過程,棧的長度+1,(壓入子彈)
出棧:刪除一個元素的過程,棧的長度-1.(彈出發射...)
先進后出,或者說后進先出。
常用操作:初始化,(隨著棧幀的移除,方法在執行??赡艹霈Fhttps://stackoverflow.com/),++i,i++,
在Java中繼承關系,Stack繼承自Vector,List,(abstractList?)
需求:
請編寫代碼實現Stack類,該類能夠實現后進先出的堆棧功能,要求實現的方法包括:
Stack(int) 實例化指定深度的棧
boolean push(E item) 像棧頂壓入對象,成功返回true,棧已滿返回false
E pop() 從棧頂移除對象并返回,為空則返回null
E peek() 查看并返回棧頂的對象,為空返回null
int size() 返回棧中當前元素數量
int depth() 返回當前堆棧深度
-
萬惡的字符編碼,無比的郁悶以下所有代碼參考網絡,在Sublime中編寫。
基于單列表實現:
class Node{ Node next = null; E data; public Node(E data) { this.data = data; } } //采用單鏈表實現棧 public class MyStack { int depth; //棧的深度 public MyStack(int i) { this.depth = i; } Node top = null; //將元素壓入棧中 public boolean push(E data) { if(size() < depth) { Node newNode = new Node (data); newNode.next = top; top = newNode; return true; } return false; } //讀取棧中的頭節點,不刪除頭節點 public E peek() { if(top ==null) { return null; } return top.data; } //獲取棧中的頭節點,并刪除頭節點 public E pop() { if(top ==null) { return null; } Node tmp = top; top = top.next; return tmp.data; } //棧的元素個數 public int size() { int len = 0; Node tmeNode = top; while(tmeNode != null) { tmeNode = tmeNode.next; len++; } return len; } //當前棧的深度 public int depth() { return this.depth; } public static void main(String[] args) { MyStack stack = new MyStack(2); System.out.println(stack.push(1)); System.out.println(stack.push(2)); System.out.println(stack.push(3)); System.out.println("棧的元素個數: " +stack.size()); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println("棧的元素個數: " + stack.depth()); } } ---------------------------此代碼來自《Java編程思想》---------------------------------- import java.util.LinkedList; public class Stack { private LinkedList storage = new LinkedList (); public void push(T v) { storage.addFirst(v); } public T peek() { return storage.getFirst(); } public T pop() { return storage.removeFirst(); } public boolean empty() { return storage.isEmpty(); } public String toString() { return storage.toString(); } }
在來看看大佬的另一種實現,簡單明了啊。
public class LinkedStackQueue{ private static class Node { U item; Node next; Node() { item = null; next =null; } Node(U item,Node next) { this.item = item; this.next = next; } boolean end() { return item == null && next == null; } } private Node top = new Node (); public void push(T item) { top = new Node (item,top); } public T pop() { T result = top.item; if (!top.end()) { top = top.next; } return result; } public static void main(String[] args) { LinkedStack lss = new LinkedStack (); for (String s : "Phasers on stun!".split(" ") ) lss.push(s); String s; while((s = lss.pop()) != null) System.out.println(s); } } 輸出如下: I:Java otesortcode>java LinkedStack stun! on Phasers
隊列(queue),也是一種特殊的線性表,使用固定的一端來插入數據,另一端用于刪除數據
先進先出,就像火車站排隊買票一樣!??!,整個隊伍向前面移動。
分為順序隊列結構和鏈式隊列結構
從JDK 5 開始,Java集合框架提供了Queue接口,實現該接口的類可以當成隊列使用,如LinkedBlockingQueue,PriorityBlockingQueue。
可以通過輪詢和等待-通知機制實現阻塞隊列。
具體Queue實現:
import java.util.*; public class SimpleQueueimplements Iterable { private LinkedList storage = new LinkedList (); public void add(T t){ storage.offer(t); } public T get() { return storage.poll(); } public Iterator iterator() { return storage.iterator(); } public static void main(String[] args) { SimpleQueue queue = new SimpleQueue(); queue.add(8); System.out.println(queue.get()); } }
我們在來看看用Stack如何實現Queue,非常不錯,《Java編程思想》
import java.util.Stack; public class MyQueue{ StackTreestack = new Stack (); Stack stackTmp = new Stack (); //Push element X to the back of queue public void push(int x) { stack.push(x); } //Removes the element form in front of queue public void pop() { if (stackTmp.isEmpty()) { while (!stack.isEmpty()) { int tmp = stack.peek(); stackTmp.push(tmp); stack.pop(); } } else { stackTmp.pop(); } } //Get the front element public int peek() { if (!stackTmp.isEmpty()) { int tmp = stack.peek(); stackTmp.push(tmp); } return stackTmp.peek(); } //Return whether the queueis empty public boolean empty() { if (stackTmp.isEmpty() && stack.isEmpty()) { return true; }else { return false; } } public static void main(String[] args) { MyQueue queue = new MyQueue(); queue.push(8); System.out.println(queue.empty()); //false } }
樹,也是一種數據結構,非線性的,這種結構內的元素存在一對多的關系。
樹,尤其是二叉樹應用很廣泛,排序二叉樹,平衡二叉樹,紅黑樹。
二叉樹,在普通的樹的基礎上,讓一顆樹中每個節點最多只能包含兩個子節點,且嚴格區分左子節點和右子節點(位置不能變化)
-遍歷二叉樹,考慮深讀,優先遍歷。(先序遍歷、中序遍歷、后續遍歷)和廣度優先遍歷。
哈夫曼樹,一種帶權路徑最短的二叉樹,在信息檢索中非常有用
哈夫曼編碼,假設需要對一個字符串如“abcabcabc”進行編碼,將它轉化為唯一的二進制碼,同時要求轉換出來的二進制碼的長度最小。可以采用哈夫曼樹來解決報文編碼問題
排序二叉樹:一種特殊的二叉樹,可以非常方便的對樹中的所有節點進行排序和檢索。
二叉樹,這里采用遞歸和內部類的思想。
public class BinaryTree { private Node root; //添加節點 public void add(int data) { if (root ==null) { root = new Node(data); }else { root.addNode(data); } } //打印節點 public void print() { root.printNode(); } private class Node { private int data; private Node left; private Node right; public Node(int data) { this.data = data; } public void addNode(int data) { //核心思想就是進來先個當前節點比,如果如果小于則在左邊添加,如果左邊沒子節點,則創建,如果有添加 if (this.data > data) { if (this.left == null) { this.left = new Node(data); }else { this.addNode(data); //這里應該是采用遞歸。 } }else { if (this.right == null) { this.right = new Node(data); }else { this.right.addNode(data); } } } //中序遍歷 public void printNode() { if (this.left != null) { this.left.printNode(); } System.out.println(this.data + "->"); if (this.right !=null) { this.right.printNode(); } } } } ------------------------測試----------------------------------------------- public static void main(String[] args) { BinaryTree bt = new BinaryTree(); // 8、3、10、1、6、14、4、7、13 bt.add(8);bt.add(3);bt.add(10); bt.add(1);bt.add(6);bt.add(14); bt.add(4);bt.add(7);bt.add(13); bt.print(); } 輸出: 1->3->4->6->7->8->10->13->14->LinkedList
ArrayList因為亂碼,寫了一半,無奈啊,完全坑我,其思想就是根據索引,涉及到擴容,判斷越界了么,。這里先不管了。直接看LinkedList。
public class MyLinkedList { protected Node first; // 鏈表的第一個節點 protected Node last; // 鏈表的最后一個節點 private int size; // 節點的數量 // 鏈表中的每一個節點 public class Node { public Node(Object ele) { this.ele = ele; } Node prev; // 上一個節點對象 Node next; // 下一個節點對象 public Object ele; // 當前節點中存儲的數據 } public void addFirst(Object ele) { Node node = new Node(ele); //需要保存的節點對象 //進來一個節點,如果為空的話,它可定時第一個,也是最后一個 if (size == 0) { this.first = node; this.last = node; }else { node.next = this.first; // 把之前第一個作為新增節點的下一個節點,(進來一個,當前的只能當老二了。) this.first.prev = node; // 把新增節點作為之前第一個節點的上一個節點 this.first = node; // 把新增的節點作為第一個節點 } size++; } //這里很重要,別忘記 public void addLast(Object ele) { Node node = new Node(ele); if (size == 0) { this.first = node; this.last = node; }else { this.last.next = node; // 新增節點作為之前最后一個節點的下一個節點(因為是加在后面,所以當前節點的下一個才是 新增節點) node.prev = this.last; // 之前最后一個節點作為新增節點的上一個節點 this.last = node; // 把新增的節點作為最后一個節點 } } //原諒我復制了 public void remove(Object ele) { // 找到被刪除的節點 Node current = this.first;// 確定為第一個節點,從頭到尾開始找 for (int i = 0; i < size; i++) { if (!current.ele.equals(ele)) {// 當前為true !true 為false ,說明找到當前ele,輸出 if (current.next == null) { // 續上: 如果false取反為true, 判斷是否最后一個, return; } current = current.next; } } //刪除節點 if(current==first){ this.first = current.next; //當前的下一個作為第一個 this.first.prev = null; //當前下一個對上一個的引用設置為null }else if(current==last){ this.last = current.prev; this.last.next = null; }else{ //把刪除當前節點的下一個節點作為刪除節點的上一個節點的next current.prev.next =current.next; //把刪除節點的上一個節點作為刪除節點下一個節點的prev current.next.prev = current.prev; } size--; //System.out.println("current =" + current.ele); } public String toString() { if (size == 0) { return "[]"; } StringBuilder sb = new StringBuilder(size * 2 + 1); Node current = this.first;// 第一個節點 sb.append("["); for (int i = 0; i < size; i++) { sb.append(current.ele); if (i != size - 1) { sb.append(","); } else { sb.append("]"); } current = current.next; // 獲取自己的下一個節點 } return sb.toString(); } }
這個雙向列表有點難理解,還是看圖吧,
線性鏈表:
雙向鏈表:
先到這里吧,gogogo,機會是留給有準備的人,
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68685.html
摘要:完全二叉樹深度為有個結點的二叉樹,其每個結點的編號與深度為的滿二叉樹中編號從的結點一一對應葉子結點只可能在層數最大的兩層上出現。 二叉樹的性質 (1) 在二叉樹的第 i 層最多有 2^i-1 個結點 (i>=1). (2) 深度為 k 的二叉樹最多有 2^k - 1 個結點 (k>=1). (3) 對任何一棵二叉樹,如果其葉子結點數為 n0, 度為 2 的結點數為 n2,...
摘要:序列文章面試之函數面試之對象面試之數組的幾個不操作面試之對比分析前言數據結構是計算機存儲組織數據的方式算法是系統描述解決問題的策略。了解基本的數據結構和算法可以提高代碼的性能和質量。 showImg(https://segmentfault.com/img/bVbqYZQ?w=3000&h=2250); 序列文章 JS面試之函數(1)JS面試之對象(2)JS面試之數組的幾個不low操作...
摘要:棧迭代復雜度時間空間遞歸??臻g對于二叉樹思路用迭代法做深度優先搜索的技巧就是使用一個顯式聲明的存儲遍歷到節點,替代遞歸中的進程棧,實際上空間復雜度還是一樣的。對于先序遍歷,我們出棧頂節點,記錄它的值,然后將它的左右子節點入棧,以此類推。 Binary Tree Preorder Traversal Given a binary tree, return the preorder tr...
閱讀 2996·2021-11-23 09:51
閱讀 2817·2021-11-11 16:55
閱讀 2926·2021-10-14 09:43
閱讀 1402·2021-09-23 11:22
閱讀 1044·2019-08-30 11:04
閱讀 1672·2019-08-29 11:10
閱讀 964·2019-08-27 10:56
閱讀 3114·2019-08-26 12:01