编程总结(五)数据结构
2017-04-20 10:26
162 查看
编程总结(五)数据结构
0x00 前言
本部分总结了几种常见的数据结构的C++实现,预计会包括线性表,广义表,栈,队列,树,二叉树,图等等。0x01 线性表
[b]1、顺序表示[/b]/* 顺序表 优点:随机存取 缺点:插入删除需要移动大量的元素 */ template<class T> class Array { public: Array() { m_array = new T[m_iMaxSize]; m_iLength = 0; } Array(int n) { m_array = new T ; m_iLength = 0; } void Insert(int i, T x); //插入操作 void Delete(int i);//删除操作 void Resize(int n);//重新分配大小 private: int m_iMaxSize=100; T* m_array; int m_iLength = 0; };
[b]2、链式表示[/b]
单链表
#include "stdafx.h" #include <iostream> using namespace std; /* 链式表 优点:插入删除方便 缺点:不能随机访问,要从头结点开始遍历,直到找到要访问的元素 */ template<class T> struct Node { T* data;//数据 Node* pNext;//指向下一个节点的指针 }; template<class T> class List { public: //构造函数,初始情况下,链表为空 List() { } //插入操作,第i个元素后面插入 bool Insert(int i, T* x) { //第一次插入时,无论i值多少,都作为头结点插入 if (m_pListHead==nullptr) { Node<T>* node = new Node<T>(); node->data = x; node->pNext = nullptr; m_pListHead = node; m_iLength++; return true; } //0<=i<m_iLength,链表中第一个元素标记为0 if (i>=m_iLength || i<0) { return false; } //创建节点 Node<T>* node = new Node<T>(); node->data = x; node->pNext = nullptr; //p指向第i个元素 Node<T>* p = m_pListHead; int count = 0; for (; p != nullptr, count < i; count++, p = p->pNext); //保留i+1个元素的指针 Node<T>* pNext = p->pNext; //插入node p->pNext = node; //恢复i+1个元素 node->pNext = pNext; //链表长度加一 m_iLength++; } bool Delete(int i) { if (i<0 || i>=m_iLength) { return false; } Node<T>* pNext; //删除第一个节点 if (i==0) { pNext = m_pListHead->pNext; delete m_pListHead; m_pListHead = pNext; return true; } //p指向第i-1个元素 Node<T>* p = m_pListHead; int count = 0; for (; p != nullptr, count < i-1; count++, p = p->pNext); pNext = p->pNext->pNext; delete p->pNext; p->pNext = pNext; return true; } //得到第i个元素的值 T* GetIndexOf(int i) { if (i<0 || i>=m_iLength) { return NULL; } //p指向第i个元素 Node<T>* p = m_pListHead; int count = 0; for (; p != nullptr, count < i; count++, p = p->pNext); return p->data; } void Print() { int i = 1; for (Node<T>* p=m_pListHead; p!=nullptr; p=p->pNext,i++) { cout <<i<<":"<< *(p->data) << endl; } } //删除操作 private: Node<T>* m_pListHead = nullptr;//链表的头指针 int m_iLength = 0; }; int main() { List<char> c = List<char>(); c.Insert(0, "1"); c.Insert(0, "2"); c.Insert(1, "3"); c.Insert(1, "4"); c.Insert(1, "5"); c.Print(); c.Delete(3); c.Print(); cout<< c.GetIndexOf(3)<<endl; c.Print(); getchar(); return 0; }
双链表
//节点的数据结构,其他操作和单链表差不多 template<class T> struct Node { T* data;//数据 Node* pPre;//指向上一个节点 Node* pNext;//指向下一个节点的指针 };
循环链表
主要有以下两点
①在建立一个循环链表时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为NULL。此种情况还使用于在最后一个结点后插入一个新的结点。
②在判断是否到表尾时,是判断该结点链域的值是否是表头结点,当链域值等于表头指针时,说明已到表尾。而非象单链表那样判断链域值是否为NULL。
0x02 广义表
/* 广义表的数据结构 */ //元素类型,分为原子类型和表类型 enum ElemType { ATOM, LIST }; //广义表节点,可能是原子节点,可能是表节点,这个可能是通过联合体实现的 template<class T> struct GLNode { //节点类型 ElemType type; //原子部分和表结点的联合部分 union { T* data; //atom是原子结点的值域,AtomType由用户定义 struct { GLNode *hp;//头节点指针 GLNode *tp;//尾节点指针 } ptr; }; }; //链式广义表 template<class T> struct LGLNode { //节点类型 ElemType type; //原子部分和表结点的联合部分 union { T* data; //atom是原子结点的值域,AtomType由用户定义 struct { LGLNode *hp;//头节点指针 LGLNode *tp;//尾节点指针 } ptr; }; LGLNode* pNext;//指向下一个节点 };
关于广义表的笔记
广义表是线性表的一种扩展,线性表中节点类型固定,而在广义表中,节点可以是包含原子域的节点,也可以是包含一张广义表的节点。通俗的说,节点可能是数据节点,也可能存储了一张表的头指针和尾指针(我们可以以此访问该表)。
联合体,联合体中的成员同一时间只有一成员存在,所以,一个广义表的节点要么是原子节点,要么是表节点。
0x03 有约束的线性表
[b]1、 栈[/b]/* 数组实现的栈 */ template<class T> class CStack { public: CStack() { } bool Push(T t) { if (m_iSize < m_iMaxSize) { m_Stack[++m_iTop] = t; m_iSize++; return true; } else { return false; } } T Pop() { T t; if (m_iSize>0) { t = m_Stack[m_iTop--]; m_iSize--; } return t; } int Size() { return m_iSize; } bool Empty() { if (m_iTop == -1) return true; return false; } private: int m_iTop = -1; int m_iSize = 0; int m_iMaxSize = 100; T m_Stack[100];//默认情况下,栈大小为100 };
[b]笔记:[/b]
栈是一种受约束的线性表,体现在,栈只能在一头(一般定义为栈顶)插入和删除,即先进后出。
Push,算法是Top++,然后在Top所指的位置插入值。Pop是返回Top所指的元素值,然后Top–。
可以在添加一个变量size,标记栈里面元素的个数,以便查询栈元素个数以及栈是否空或者满。
[b]栈在操作系统中的应用-函数调用[/b]
序执行的栈具有以下特点:
每一个进程在用户态对应一个调用栈结构(call stack)
程序中每一个未完成运行的函数对应一个栈帧(stack frame),栈帧中保存函数局部变量、传递给被调函数的参数等信息
栈底对应高地址,栈顶对应低地址,栈由内存高地址向低地址生长
对于有特定用途的几个寄存器,简要介绍如下:
ax(accumulator): 可用于存放函数返回值
bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址
sp(stack poinger): 用于存放执行中的函数对应的栈帧的栈顶地址
ip(instruction pointer): 指向当前执行指令的下一条指令
函数调用过程:
- 初始情况下,bp寄存器的值为调用函数栈帧栈底的地址。
- 参数从后往前压栈
- call Fun;实际上2条指令,(1) 将当前的IP或者CS和IP压入栈中。
(2) 转移即进入函数体中。
- 将调用函数的栈帧栈底地址入栈,即将bp寄存器的值压入栈中
- 建立新的栈帧,将被调函数的栈帧栈底地址(当前sp的值)放入bp
- 执行实际的函数体
- 函数返回,Pop bp,此时bp中是调用函数的栈帧栈底地址,然后ret指令,本指令主要是弹出栈上面保存的值(call指令保存的值),赋值给CS和IP。实际上就是恢复call指令保存的值。
[b]2、队列[/b]
相关文章推荐
- 数据结构与程序设计第一章编程原则总结
- 数据结构与程序设计 第一章 编程原理 总结
- 数据结构与程序设计第一章:编程规则总结
- 数据结构与程序设计第一章:编程规则总结
- 编程内功修炼之数据结构—BTree(三)总结
- 数据结构之排序总结1
- Oracle 9i & 10g编程艺术-深入数据库体系结构——第15章:数据加载和卸载
- Symbian编程总结-网络与通信-套接字(1)-套接字体系结构与相关API
- 图----数据结构图论总结(四)
- (总结)数据结构之链表的基本操作说明和示例(待补充)
- 数据结构之内部排序算法总结笔记
- 总结:结构体嵌套调用内层结构体数据的问题
- 图----数据结构图论总结(三)
- 程序员的学习能力-----作文与编程、编程境界 ---数据结构和 算法 , 数学的重要性。
- 程序员的学习能力-----作文与编程、编程境界 ---数据结构和 算法 , 数学的重要性。
- 数据结构及算法学习总结(一)
- C#2005 .NET3.0高级编程学习笔记————类和结构,类的数据成员,类的函数成员(方法、属性)
- 数据结构(殷人昆) 摘录总结
- 图----数据结构图论总结(二)
- Symbian编程总结-网络与通信-套接字(1)-套接字体系结构与相关API