您的位置:首页 > 理论基础 > 数据结构算法

编程总结(五)数据结构

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]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: