1. 程式人生 > >數據結構(棧,隊列,鏈表,二叉樹)

數據結構(棧,隊列,鏈表,二叉樹)

左右 stl contain ++ 訪問 元素 mes 進入 方法

棧作為一種數據結構,用途十分廣泛。在回調函數等許多場景中都有應用。我們需要了解它的基本用途,那就是先進後出和隊列的先進先出正好相反。

最近在學習數據結構和算法,於是自己來實現。我特別喜歡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的指向 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; } }![這裏寫圖片描述](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

數據結構(棧,隊列,鏈表,二叉樹)