• 数据结构(栈,队列,链表,二叉树)


    栈作为一种数据结构,用途十分广泛。在回调函数等许多场景中都有应用。我们需要了解它的基本用途,那就是先进后出和队列的先进先出正好相反。

    最近在学习数据结构和算法,于是自己来实现。我特别喜欢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
    ", stack->getSize());
        stack->Push(3);
        printf("%d
    ", stack->getSize());
        stack->Push(2);
        printf("%d
    ", stack->getSize());
        data =stack->Pop();
        printf("%d,%d
    
    ", stack->getSize(),data);
        data = stack->Pop();
        printf("%d,%d
    
    ", stack->getSize(), data);
        data = stack->Pop();
        printf("%d,%d
    
    ", stack->getSize(), data);
        stack->Clear();
        printf("%d
    ", stack->getSize());
        data  =stack->Pop();
        printf("%d,%d
    
    ", 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
    ", queue->getSize());
        queue->EnQueue(3);
        printf("%d
    ", queue->getSize());
        queue->EnQueue(2);
        printf("%d
    ", queue->getSize());
    
        int data2 =queue->DeQueue();
        printf("%d,%d
    ", data2,queue->getSize());
        data2 = queue->DeQueue();
        printf("%d,%d
    ", data2, queue->getSize());
        data2 = queue->DeQueue();
        printf("%d,%d
    ", 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
    ", t->data);
        frontOrder(t->left);
        frontOrder(t->right);
        return;
    }
    void Twotree::middleOrder(Tree *t){
        if (t != nullptr){
            middleOrder(t->left);
            //中输出左结点
            printf("%d
    ", t->data);
            middleOrder(t->right);
        }
    }
    void Twotree::behindOrer(Tree *t){
        if (t != nullptr){
            behindOrer(t->left);
            behindOrer(t->right);
            //后输出根结点
            printf("%d
    ", t->data);
        }
    }
    
    int Twotree::getSize(){
        return this->size;
    }
    
    void Twotree::floorOrder(){
        printf("************逐层遍历*****************
    ");
        for (int i = 0; i < this->size;i++)
        {
            int data = queue->DeQueue();
            printf("%d
    ", 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
    ",twotree->getSize());
        printf("***********前序遍历*************
    ");
        twotree->frontOrder(twotree->getRoot());
        printf("***********中序遍历*************
    ");
        twotree->middleOrder(twotree->getRoot());
        printf("************后序遍历************
    ");
        twotree->behindOrer(twotree->getRoot());
        twotree->floorOrder();

    查看结果: 
    这里写图片描述

    这里写图片描述

    版权声明:本文为博主原创文章,未经博主允许不得转载。 http://blog.csdn.net/u013766436/article/details/51316403

  • 相关阅读:
    XAMPP配置8080端口
    Composer安装使用
    .Net商品管理(注释,百度,提问,对比,总结)
    .Net数据库操作
    VS链接数据库
    .Net中字典的使用
    一套解决方案,多个项目
    转化一下解决问题的思路,弯道超车
    灵活的运用Model类
    Razor数组数据
  • 原文地址:https://www.cnblogs.com/vijozsoft/p/8608637.html
Copyright © 2020-2023  润新知