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

棧做爲一種數據結構,用途十分普遍。在回調函數等許多場景中都有應用。咱們須要瞭解它的基本用途,那就是先進後出和隊列的先進先出正好相反。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;
    }
}![這裏寫圖片描述](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();

 

 

查看結果: 
這裏寫圖片描述

這裏寫圖片描述

相關文章
相關標籤/搜索