数据结构(栈,队列,链表,二叉树)
栈
栈作为一种数据结构,用途十分广泛。在回调函数等许多场景中都有应用。我们需要了解它的基本用途,那就是先进后出和队列的先进先出正好相反。
最近在学习数据结构和算法,于是自己来实现。我特别喜欢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