zoukankan      html  css  js  c++  java
  • 数据结构(栈,队列,链表,二叉树)

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

    最近在学习数据结构和算法,于是自己来实现。我特别喜欢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

  • 相关阅读:
    Using Resource File on DotNet
    C++/CLI VS CSharp
    JIT VS NGen
    [Tip: disable vc intellisense]VS2008 VC Intelisense issue
    UVa 10891 Game of Sum(经典博弈区间DP)
    UVa 10723 Cyborg Genes(LCS变种)
    UVa 607 Scheduling Lectures(简单DP)
    UVa 10401 Injured Queen Problem(简单DP)
    UVa 10313 Pay the Price(类似数字分解DP)
    UVa 10635 Prince and Princess(LCS N*logN)
  • 原文地址:https://www.cnblogs.com/vijozsoft/p/8608637.html
Copyright © 2011-2022 走看看