棧做爲一種數據結構,用途十分普遍。在回調函數等許多場景中都有應用。咱們須要瞭解它的基本用途,那就是先進後出和隊列的先進先出正好相反。java
最近在學習數據結構和算法,因而本身來實現。我特別喜歡C語言的指針,我發現很好用,因而用C++來實現一個簡單的範例。
主要實現就是函數就是Pop,Push
Push將數據放到一個到頂層位置。
Pop將數據從已有的數據中取出來。
Stack.h文件,主要描述裏面的數據,數據我用整形來處理,這個也能夠是其餘,只是示範ios
typedef struct mData { int data; mData *next; }Data; class Stack { public: Stack(); ~Stack(); void Push(int data); int Pop(); void CreateNode(int data); void Clear(); int getSize(); private: Data * pop; int size; }; Stack::Stack() { pop = nullptr; size = 0; } Stack::~Stack() { }
實現的代碼: 面試
Stack.cpp算法
#include"Stack.h" #include "iostream" using namespace std; //建立一個新的結點,並放在頂層 void Stack::CreateNode(int data){ Data *p = new Data; if (p == nullptr){ printf("新建失敗"); return; } pop = p; p->data = data; pop->next = nullptr; size++; } //將新的數據放在頂層,若是頂層不爲空,須要將本來的頂層下移,變爲內部的next的指向 void Stack::Push(int data){ Data * p = pop; if (pop == nullptr) CreateNode(data); else{ CreateNode(data); pop->next = p; } } //得到當前的棧的個數 int Stack::getSize() { return size; } //從棧頂取出一個數據,並刪除頂層內存,將底層位置上移 int Stack::Pop() { Data *p = pop; if (pop == nullptr){ printf("數據沒有"); return -10000; } else{ pop = pop->next; int data = p->data; delete p; size--; return data; } } //清空數據和內存 void Stack::Clear() { while(pop!= nullptr){ Data* p = pop; pop = pop->next; size--; delete p; } } int main(){ Stack *stack = new Stack(); int data; stack->Push(5); printf("%d\n", stack->getSize()); stack->Push(3); printf("%d\n", stack->getSize()); stack->Push(2); printf("%d\n", stack->getSize()); data =stack->Pop(); printf("%d,%d\n\n", stack->getSize(),data); data = stack->Pop(); printf("%d,%d\n\n", stack->getSize(), data); data = stack->Pop(); printf("%d,%d\n\n", stack->getSize(), data); stack->Clear(); printf("%d\n", stack->getSize()); data =stack->Pop(); printf("%d,%d\n\n", stack->getSize(), data); getchar(); return 0; }
執行效果如圖:
能夠看到一開始大小在增長,而後數據取出一次是2,3,5正好和Push的方式相反。正好就是先進後出。數組
隊列做爲一種數據結構,它的特色是先進先出,就至關於排隊同樣,在咱們的生活中許多現象都是由此構成。它的特色就是有序前行,後來的排在最後。
看下代碼實現:數據結構
class Queue { public: Queue(); ~Queue(); //進入隊列 void EnQueue(int data); //出來隊列 int DeQueue(); //判斷是否爲空 bool IsQueueEmpty(); //得到尺寸 int getSize(); void Clear(); private: int size; Data *pop; };
{ if (pop == nullptr){ Data* p = new Data; if (p == nullptr){ printf("新建失敗"); return; } pop = p; pop->data = data; size++; pop->next = nullptr; } else{ //須要先判斷是否它的下個結點是爲空,不然指的就是空指針,指針必需要有對應的地址,才能在次基礎上繼續進行 Data* temp = pop; while (temp->next != nullptr){ temp = temp->next; } temp->next = new Data; if (temp == nullptr){ printf("新建失敗"); return; } temp->next->data = data; size++; temp->next->next = nullptr; } } void Queue::Clear() { while (pop != nullptr) { size--; Data * p = pop; pop = pop->next; delete p; } } int Queue::DeQueue() { if (pop != nullptr){ size--; Data * p = pop; int data = pop->data; pop = pop->next; delete p; return data; } else{ printf("沒有數據了"); return -10000000; } } int Queue::getSize(){ return size; } bool Queue::IsQueueEmpty(){ if (size == 0) return true; else return false; }
其實實現的過程要抓緊其數據的構成,如何去遍歷,將每一個結點裏面的指針對應便可,可是因爲我在中間致使了一個錯誤:錯誤的將temp判斷,而不是temp->next,致使temp一開始是指向空指針,致使了很長時間調試,最後發現指向空指針後,temp沒法和一開始pop指針相鏈接,這是很重要,在進行兩個指針的鏈接關係時,必需要有一個指針是有具體的內存的,不然不能創建鏈接。數據結構和算法
調用:函數
int main(){ Queue *queue = new Queue(); queue->EnQueue(5); printf("%d\n", queue->getSize()); queue->EnQueue(3); printf("%d\n", queue->getSize()); queue->EnQueue(2); printf("%d\n", queue->getSize()); int data2 =queue->DeQueue(); printf("%d,%d\n", data2,queue->getSize()); data2 = queue->DeQueue(); printf("%d,%d\n", data2, queue->getSize()); data2 = queue->DeQueue(); printf("%d,%d\n", data2, queue->getSize()); queue->Clear(); getchar(); return 0; }
出現結果: 工具
能夠看到size在一開始增長,後面逐漸減小,一開始的數據順序是5,3,2,如今能看到數據取出順序仍是5,3,2,符合先進先出的原則。學習
鏈表做爲最基本的數據結構,有許多應用,在java的類集中,經過分析鏈表來得到理解,許多類的使用的底層內容都和鏈表有關。
其實鏈表準確地說,就是動態數組。
在內存中,每一個結點有兩個關鍵的地方,第一個就是鏈表結點中存儲的數據,還有一個就是鏈表結點中,會存儲下個結點的地址或者引用。
通俗地說,就是每一個結點被放在內存中,經過一個根結點將他們一一串起來構成整個鏈表。
看java實現的一個鏈表的代碼:
package DataStruct; import javax.swing.RootPaneContainer; import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper; //鏈表工具類 class Link { // 結點 private class Node { // 結點數據 private String data; // 下個結點指針 private Node next; // 初始化數據 public Node(String data) { this.data = data; } // 當前this第一次爲this.root // 第二次執行時this爲 this.root.next // 第三次執行時this爲 this.root.next.next public void addNode(Node newNode) { if (this.next == null) this.next = newNode; else this.next.addNode(newNode); } public void printNode() { System.out.println(this.data); if (this.next != null) { this.next.printNode(); } } public boolean containsNode(String data) { if (this.data.equals(data)) { return true; } else { if (this.next != null) return this.next.containsNode(data); else return false; } } // 得到數據 public String getNode(int index) { if (Link.this.foot++ == index) { return this.data; } else { return this.next.getNode(index); } } // 修改數據 public void setNode(int index, String data) { if (Link.this.foot++ == index) { this.data = data; } else { this.next.setNode(index, data); } } // 要傳遞上個結點的引用 public void removeNode(Node previous, String data) { if (data.equals(this.data)) { previous.next = this.next; } else { this.next.removeNode(this, data); } } public void toArrayNode() { Link.this.retArray[Link.this.foot++] = this.data; if (this.next != null) { this.next.toArrayNode(); } } } // ===================上面爲內部類====================== // 保存根結點 private Node root; private int count = 0; private int foot = 0; private String[] retArray; public Link() { root = null; } public void add(String data) { if (data == null) return; Node newNode = new Node(data); if (root == null) { root = newNode; } else { this.root.addNode(newNode); } this.count++; } // 鏈表大小 public int size() { return this.count; } public void print() { if (this.root != null) { this.root.printNode(); } } // 判斷是否爲空鏈表 public boolean isEmpty() { if (this.count == 0) return true; return false; } // 判斷數據是否存在 public boolean contains(String data) { if (data == null || this.root == null) { return false; } return this.root.containsNode(data); } // 根據索引來獲取數據 public String get(int index) { if (index >= this.count) { return null; } this.foot = 0; // 查詢過程當中,須要在Node中查詢 return this.root.getNode(index); } // 根據索引修改數據 public void set(int index, String data) { if (index >= this.count) { return; } this.foot = 0; this.root.setNode(index, data); } // 數據刪除 public void remove(String data) { if (this.contains(data)) { // 內部類能夠訪問私有屬性,判斷是否爲跟元素 if (data.equals(this.root.data)) { this.root = this.root.next; } else { this.root.next.removeNode(this.root, data); } this.count--; } } // 鏈表以數組返回 public String[] toArray() { if (this.root == null) { return null; } this.foot = 0; this.retArray = new String[this.count]; this.root.toArrayNode(); return this.retArray; } } public class TestList { public static void main(String arg[]) { Link link = new Link(); link.add("fwef"); link.add("毛毛"); link.add("問題"); link.add("啥問題"); // link.set(3, "data"); link.remove("fwef"); link.print(); String[] data = link.toArray(); for (int i = 0; i < data.length; i++) { System.out.println(data[i]); } } }
這裏經過內部類更加方便地構造,由於在程序裏面能夠直接訪問私有屬性。而不須要在寫對應的方法。
二叉樹的應用比較普遍,也是很重要的一種數據結構,在面試以及許多地方均可能用獲得。主要講下,我本身寫的二叉樹的代碼。
首先,咱們經過創建一個類來操做二叉樹
爲二叉樹添加一個元素,我這個類裏面實現的是,每一個結點都須要添加兩個元素,除了根元素之外。
好比如今有個數組,裏面內容須要從1-9元素,創建的最終結果就是:
這裏面咱們須要經過四種方法來遍歷每一個元素:(命名規則實際上是根據根結點前後順序命名的)
先序遍歷,就是先根結點,而後左結點,最後右結點。124895367
中序遍歷,先左結點,而後根結點,最後右結點。849251637
後序遍歷,先左結點,而後右結點,最後根結點。894526731
逐層遍歷:每一個層開始遍歷,123456789,逐層遍歷,用了隊列的先入先出的特性,保存了數據。
類的方法和信息
typedef struct tree { int data; tree * right; tree * left; }Tree; class Twotree { public: void createNode(int data); void add(Tree * T,int data); void frontOrder(Tree *t); //前序遍歷,先根,後左,在右,根據根結點位置來區分遍歷形式 void middleOrder(Tree *t); //中序遍歷,先左,後根,在右 void behindOrer(Tree *t); //後序遍歷,先左,再右,後根 void floorOrder(); //經過隊列來逐層遍歷 Tree *getRoot(); //得到根結點 int getSize(); //得到元素個數 Twotree(); ~Twotree(); private: Tree * root; int size; Queue *queue; }; Twotree::Twotree() { queue = new Queue(); size = 0; root = nullptr; } Twotree::~Twotree() { }
類的具體實現:
Tree* Twotree::getRoot() { return this->root; } void Twotree::createNode(int data){ if (root == nullptr){ root = new Tree; if (root == nullptr) { printf("建立失敗"); return; } //把數據放在隊列中 queue->EnQueue(data); size++; root->data = data; root->left = nullptr; root->right = nullptr; } } void Twotree::add(Tree * T, int data){ //若是左子樹爲空,則添加左子樹 if (T->left == nullptr){ Tree *temp = new Tree; if (temp == nullptr){ printf("建立失敗!"); } queue->EnQueue(data); T->left = temp; T->left->data = data; size++; T->left->left = nullptr; T->left->right = nullptr; return; } else if (T->right == nullptr){ Tree *temp = new Tree; if (temp == nullptr){ printf("建立失敗!"); } queue->EnQueue(data); T->right = temp; T->right->data = data; T->right->left = nullptr; T->right->right = nullptr; size++; return; } //若是右子樹不爲空,而且下個節點的左子樹或者右子樹爲空,作須要創建下個節點左子樹或者右子樹。 //若是左右子樹的下個結點都完成了分配,那麼就須要從左子樹開始 else if ((T->right != nullptr && (T->left->left == nullptr || T->left->right == nullptr)) || (T->right != nullptr&&T->right->left!= nullptr&&T->right->right != nullptr)){ add(T->left, data); return; } else { add(T->right, data); return; } } void Twotree::frontOrder(Tree *t){ if (t == nullptr){ return; } //先輸出根結點 printf("%d\n", t->data); frontOrder(t->left); frontOrder(t->right); return; } void Twotree::middleOrder(Tree *t){ if (t != nullptr){ middleOrder(t->left); //中輸出左結點 printf("%d\n", t->data); middleOrder(t->right); } } void Twotree::behindOrer(Tree *t){ if (t != nullptr){ behindOrer(t->left); behindOrer(t->right); //後輸出根結點 printf("%d\n", t->data); } } int Twotree::getSize(){ return this->size; } void Twotree::floorOrder(){ printf("************逐層遍歷*****************\n"); for (int i = 0; i < this->size;i++) { int data = queue->DeQueue(); printf("%d\n", data); } }
這裏面用到的隊列就是上面程序隊列的原型。請查看隊列的介紹。
對於沒有用遞歸的方法,有人建議使用棧的先入後出特性來解決問題。
但我以爲遞歸方法更加費腦殼來理解,是個很不錯的練習方式。
調用:
Twotree *twotree = new Twotree(); twotree->createNode(1); twotree->add(twotree->getRoot(),2); twotree->add(twotree->getRoot(), 3); twotree->add(twotree->getRoot(), 4); twotree->add(twotree->getRoot(), 5); twotree->add(twotree->getRoot(), 6); twotree->add(twotree->getRoot(), 7); twotree->add(twotree->getRoot(), 8); twotree->add(twotree->getRoot(), 9); printf("%d\n",twotree->getSize()); printf("***********前序遍歷*************\n"); twotree->frontOrder(twotree->getRoot()); printf("***********中序遍歷*************\n"); twotree->middleOrder(twotree->getRoot()); printf("************後序遍歷************\n"); twotree->behindOrer(twotree->getRoot()); twotree->floorOrder();
查看結果: