摘要:常見數(shù)據(jù)結(jié)構(gòu)分析及實現(xiàn)說明本文中的代碼是參考編程思想某培訓(xùn)機構(gòu)。同時還要分析這些數(shù)據(jù)結(jié)構(gòu)在時間和空間上的開銷。這種專門研究應(yīng)用程序中的數(shù)據(jù)之間的邏輯關(guān)系,存儲方式及其操作的學(xué)問就是數(shù)據(jù)結(jié)構(gòu)。
常見數(shù)據(jù)結(jié)構(gòu)分析及實現(xiàn) 說明
本文中的代碼是參考《Java編程思想》、某培訓(xùn)機構(gòu)。
文中的代碼放Github了,有興趣的可以看看,點歌star鼓勵下我。
代碼在Sublime中敲的,坑爹的GBK,注釋了很多中文,一轉(zhuǎn)碼不能用了!!!
重點在思想,而不是實現(xiàn) 。再次推薦《Java編程思想》。
1、數(shù)據(jù)結(jié)構(gòu)
編程的本質(zhì)就是對數(shù)據(jù)(信息以數(shù)據(jù)的形式而存在)的處理,實際編程中不得不處理大量數(shù)據(jù),因此實際動手編程之前必須先分析處理這些數(shù)據(jù),處理數(shù)據(jù)之間存在的關(guān)系。
現(xiàn)實的數(shù)據(jù)元素之間有個錯綜復(fù)雜的邏輯關(guān)系,需要采用合適的物理結(jié)構(gòu)來存儲這些數(shù)據(jù),并以此為基礎(chǔ)對這些數(shù)據(jù)進行相應(yīng)的操作。同時還要分析這些數(shù)據(jù)結(jié)構(gòu)在時間和空間上的開銷。這種專門研究應(yīng)用程序中的數(shù)據(jù)之間的邏輯關(guān)系,存儲方式及其操作的學(xué)問就是數(shù)據(jù)結(jié)構(gòu)。
數(shù)據(jù)元素之間存在的關(guān)聯(lián)關(guān)系被稱為數(shù)據(jù)的邏輯結(jié)構(gòu),歸納起來,大致有如下四種基本的邏輯結(jié)構(gòu):
集合:數(shù)據(jù)元素之間只有"同屬于一個集合"的關(guān)系
線性關(guān)系:數(shù)據(jù)元素之間存在一個對一個的關(guān)系
樹形結(jié)構(gòu):數(shù)據(jù)元素之間存在一個對多個的關(guān)系
圖狀結(jié)構(gòu)或網(wǎng)狀結(jié)構(gòu):數(shù)據(jù)元素之間存在多個對多個的關(guān)系。
腦補圖:
圖片>代碼>文字,個人理解,能用圖片說明問題的就不要用代碼,同理,盡量用代碼+文字解釋問題的本質(zhì)。
同一種的邏輯結(jié)構(gòu),在底層通常有兩種物理存儲結(jié)構(gòu):
順序存儲結(jié)構(gòu),如一維數(shù)組
非順序存儲結(jié)構(gòu),如鏈式存儲結(jié)構(gòu)(鏈表)、散列
順序結(jié)構(gòu)適合讀操作(為啥呢?因為有索引啊),鏈表存儲適合寫操作(為啥呢?斷開,加上節(jié)點就完成,不需要底層復(fù)制啊)
算法的設(shè)計取決于邏輯結(jié)構(gòu):算法的實現(xiàn)依賴于存儲結(jié)構(gòu)。對象的設(shè)計取決于類結(jié)構(gòu),(...)
什么是數(shù)據(jù)結(jié)果呢?數(shù)據(jù)結(jié)構(gòu)歸納起來所要研究的問題就三方面:
數(shù)據(jù)元素之間的客觀聯(lián)系(邏輯結(jié)構(gòu))
數(shù)據(jù)在計算機內(nèi)部的存儲方式(存儲結(jié)構(gòu))
針對數(shù)據(jù)實施的有效的操作和處理(算法)
對象之間的關(guān)系(對現(xiàn)實的抽象,繼承?組合?),存儲在內(nèi)存中哪里,堆上啊,怎么存?存在數(shù)組里?hash表里?怎么處理的啊?增刪改查啊,排序那,加密解密啊,
Stack對于普通的線性表而言,它的作用是一個容器,用于裝具有相似結(jié)果的數(shù)據(jù)。
分為順序存儲機構(gòu)和鏈式存儲結(jié)構(gòu)
可以進行插入、刪除和排序的操作
如果線性表之允許在線性表的某端添加、刪除元素,這時就演變?yōu)椋簵:完犃小?先進后出(彈夾),先進先出(火車站排隊))
以下圖片來自維基百科(百X百科就別看了)
原諒沒放恐怖的,來自Google(百X就別用了)
棧(Stack),是一種特殊的線性表,只能在固定的一端(線性表的尾端)進行插入、刪除操作。
允許進行插入、刪除操作的一端為棧頂(top),另一端,你猜?(bottom)
進棧:將一個元素插入棧的過程,棧的長度+1,(壓入子彈)
出棧:刪除一個元素的過程,棧的長度-1.(彈出發(fā)射...)
先進后出,或者說后進先出。
常用操作:初始化,(隨著棧幀的移除,方法在執(zhí)行。可能出現(xiàn)https://stackoverflow.com/),++i,i++,
在Java中繼承關(guān)系,Stack繼承自Vector,List,(abstractList?)
需求:
請編寫代碼實現(xiàn)Stack類,該類能夠?qū)崿F(xiàn)后進先出的堆棧功能,要求實現(xiàn)的方法包括:
Stack(int) 實例化指定深度的棧
boolean push(E item) 像棧頂壓入對象,成功返回true,棧已滿返回false
E pop() 從棧頂移除對象并返回,為空則返回null
E peek() 查看并返回棧頂?shù)膶ο螅瑸榭辗祷豱ull
int size() 返回棧中當前元素數(shù)量
int depth() 返回當前堆棧深度
-
萬惡的字符編碼,無比的郁悶以下所有代碼參考網(wǎng)絡(luò),在Sublime中編寫。
基于單列表實現(xiàn):
class Node{ Node next = null; E data; public Node(E data) { this.data = data; } } //采用單鏈表實現(xiàn)棧 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; } //讀取棧中的頭節(jié)點,不刪除頭節(jié)點 public E peek() { if(top ==null) { return null; } return top.data; } //獲取棧中的頭節(jié)點,并刪除頭節(jié)點 public E pop() { if(top ==null) { return null; } Node tmp = top; top = top.next; return tmp.data; } //棧的元素個數(shù) 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("棧的元素個數(shù): " +stack.size()); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println("棧的元素個數(shù): " + 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(); } }
在來看看大佬的另一種實現(xiàn),簡單明了啊。
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),也是一種特殊的線性表,使用固定的一端來插入數(shù)據(jù),另一端用于刪除數(shù)據(jù)
先進先出,就像火車站排隊買票一樣!!!,整個隊伍向前面移動。
分為順序隊列結(jié)構(gòu)和鏈式隊列結(jié)構(gòu)
從JDK 5 開始,Java集合框架提供了Queue接口,實現(xiàn)該接口的類可以當成隊列使用,如LinkedBlockingQueue,PriorityBlockingQueue。
可以通過輪詢和等待-通知機制實現(xiàn)阻塞隊列。
具體Queue實現(xiàn):
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如何實現(xiàn)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 } }
樹,也是一種數(shù)據(jù)結(jié)構(gòu),非線性的,這種結(jié)構(gòu)內(nèi)的元素存在一對多的關(guān)系。
樹,尤其是二叉樹應(yīng)用很廣泛,排序二叉樹,平衡二叉樹,紅黑樹。
二叉樹,在普通的樹的基礎(chǔ)上,讓一顆樹中每個節(jié)點最多只能包含兩個子節(jié)點,且嚴格區(qū)分左子節(jié)點和右子節(jié)點(位置不能變化)
-遍歷二叉樹,考慮深讀,優(yōu)先遍歷。(先序遍歷、中序遍歷、后續(xù)遍歷)和廣度優(yōu)先遍歷。
哈夫曼樹,一種帶權(quán)路徑最短的二叉樹,在信息檢索中非常有用
哈夫曼編碼,假設(shè)需要對一個字符串如“abcabcabc”進行編碼,將它轉(zhuǎn)化為唯一的二進制碼,同時要求轉(zhuǎn)換出來的二進制碼的長度最小。可以采用哈夫曼樹來解決報文編碼問題
排序二叉樹:一種特殊的二叉樹,可以非常方便的對樹中的所有節(jié)點進行排序和檢索。
二叉樹,這里采用遞歸和內(nèi)部類的思想。
public class BinaryTree { private Node root; //添加節(jié)點 public void add(int data) { if (root ==null) { root = new Node(data); }else { root.addNode(data); } } //打印節(jié)點 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) { //核心思想就是進來先個當前節(jié)點比,如果如果小于則在左邊添加,如果左邊沒子節(jié)點,則創(chuàng)建,如果有添加 if (this.data > data) { if (this.left == null) { this.left = new Node(data); }else { this.addNode(data); //這里應(yīng)該是采用遞歸。 } }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因為亂碼,寫了一半,無奈啊,完全坑我,其思想就是根據(jù)索引,涉及到擴容,判斷越界了么,。這里先不管了。直接看LinkedList。
public class MyLinkedList { protected Node first; // 鏈表的第一個節(jié)點 protected Node last; // 鏈表的最后一個節(jié)點 private int size; // 節(jié)點的數(shù)量 // 鏈表中的每一個節(jié)點 public class Node { public Node(Object ele) { this.ele = ele; } Node prev; // 上一個節(jié)點對象 Node next; // 下一個節(jié)點對象 public Object ele; // 當前節(jié)點中存儲的數(shù)據(jù) } public void addFirst(Object ele) { Node node = new Node(ele); //需要保存的節(jié)點對象 //進來一個節(jié)點,如果為空的話,它可定時第一個,也是最后一個 if (size == 0) { this.first = node; this.last = node; }else { node.next = this.first; // 把之前第一個作為新增節(jié)點的下一個節(jié)點,(進來一個,當前的只能當老二了。) this.first.prev = node; // 把新增節(jié)點作為之前第一個節(jié)點的上一個節(jié)點 this.first = node; // 把新增的節(jié)點作為第一個節(jié)點 } 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; // 新增節(jié)點作為之前最后一個節(jié)點的下一個節(jié)點(因為是加在后面,所以當前節(jié)點的下一個才是 新增節(jié)點) node.prev = this.last; // 之前最后一個節(jié)點作為新增節(jié)點的上一個節(jié)點 this.last = node; // 把新增的節(jié)點作為最后一個節(jié)點 } } //原諒我復(fù)制了 public void remove(Object ele) { // 找到被刪除的節(jié)點 Node current = this.first;// 確定為第一個節(jié)點,從頭到尾開始找 for (int i = 0; i < size; i++) { if (!current.ele.equals(ele)) {// 當前為true !true 為false ,說明找到當前ele,輸出 if (current.next == null) { // 續(xù)上: 如果false取反為true, 判斷是否最后一個, return; } current = current.next; } } //刪除節(jié)點 if(current==first){ this.first = current.next; //當前的下一個作為第一個 this.first.prev = null; //當前下一個對上一個的引用設(shè)置為null }else if(current==last){ this.last = current.prev; this.last.next = null; }else{ //把刪除當前節(jié)點的下一個節(jié)點作為刪除節(jié)點的上一個節(jié)點的next current.prev.next =current.next; //把刪除節(jié)點的上一個節(jié)點作為刪除節(jié)點下一個節(jié)點的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;// 第一個節(jié)點 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; // 獲取自己的下一個節(jié)點 } return sb.toString(); } }
這個雙向列表有點難理解,還是看圖吧,
線性鏈表:
雙向鏈表:
先到這里吧,gogogo,機會是留給有準備的人,
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/68685.html
摘要:完全二叉樹深度為有個結(jié)點的二叉樹,其每個結(jié)點的編號與深度為的滿二叉樹中編號從的結(jié)點一一對應(yīng)葉子結(jié)點只可能在層數(shù)最大的兩層上出現(xiàn)。 二叉樹的性質(zhì) (1) 在二叉樹的第 i 層最多有 2^i-1 個結(jié)點 (i>=1). (2) 深度為 k 的二叉樹最多有 2^k - 1 個結(jié)點 (k>=1). (3) 對任何一棵二叉樹,如果其葉子結(jié)點數(shù)為 n0, 度為 2 的結(jié)點數(shù)為 n2,...
摘要:序列文章面試之函數(shù)面試之對象面試之數(shù)組的幾個不操作面試之對比分析前言數(shù)據(jù)結(jié)構(gòu)是計算機存儲組織數(shù)據(jù)的方式算法是系統(tǒng)描述解決問題的策略。了解基本的數(shù)據(jù)結(jié)構(gòu)和算法可以提高代碼的性能和質(zhì)量。 showImg(https://segmentfault.com/img/bVbqYZQ?w=3000&h=2250); 序列文章 JS面試之函數(shù)(1)JS面試之對象(2)JS面試之數(shù)組的幾個不low操作...
摘要:棧迭代復(fù)雜度時間空間遞歸棧空間對于二叉樹思路用迭代法做深度優(yōu)先搜索的技巧就是使用一個顯式聲明的存儲遍歷到節(jié)點,替代遞歸中的進程棧,實際上空間復(fù)雜度還是一樣的。對于先序遍歷,我們出棧頂節(jié)點,記錄它的值,然后將它的左右子節(jié)點入棧,以此類推。 Binary Tree Preorder Traversal Given a binary tree, return the preorder tr...
摘要:樹和樹的算法一樹樹的概念樹英語是一種抽象數(shù)據(jù)類型或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。一種時間復(fù)雜度額外空間復(fù)雜度的二叉樹的遍歷方式,為二叉樹的節(jié)點個數(shù)。 樹和樹的算法 一、樹 1.1 樹的概念 樹(英語:tree)是一種抽象數(shù)據(jù)類型(ADT)或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。它是由n(n>=1)個有限節(jié)點組成一個...
摘要:樹和樹的算法一樹樹的概念樹英語是一種抽象數(shù)據(jù)類型或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。一種時間復(fù)雜度額外空間復(fù)雜度的二叉樹的遍歷方式,為二叉樹的節(jié)點個數(shù)。 樹和樹的算法 一、樹 1.1 樹的概念 樹(英語:tree)是一種抽象數(shù)據(jù)類型(ADT)或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。它是由n(n>=1)個有限節(jié)點組成一個...
閱讀 2994·2021-11-23 09:51
閱讀 2813·2021-11-11 16:55
閱讀 2918·2021-10-14 09:43
閱讀 1399·2021-09-23 11:22
閱讀 1041·2019-08-30 11:04
閱讀 1670·2019-08-29 11:10
閱讀 962·2019-08-27 10:56
閱讀 3111·2019-08-26 12:01