數據結構(棧,隊列,鏈表,二叉樹)
棧
棧作為一種數據結構,用途十分廣泛。在回調函數等許多場景中都有應用。我們需要了解它的基本用途,那就是先進後出和隊列的先進先出正好相反。
最近在學習數據結構和算法,於是自己來實現。我特別喜歡C語言的指針,我發現很好用,於是用C++來實現一個簡單的範例。
主要實現就是函數就是Pop,Push
Push將數據放到一個到頂層位置。
Pop將數據從已有的數據中取出來。
Stack.h文件,主要描述裏面的數據,數據我用整形來處理,這個也可以是其他,只是示範
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的指向 voidStack::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; } }![這裏寫圖片描述](http://img.blog.csdn.net/20160504170135374) 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();
查看結果:
版權聲明:本文為博主原創文章,未經博主允許不得轉載。 http://blog.csdn.net/u013766436/article/details/51316403
數據結構(棧,隊列,鏈表,二叉樹)