编程总结(五)数据结构

编程总结(五)数据结构

0x00 前言

本部分总结了几种常见的数据结构的C++实现,预计会包括线性表,广义表,栈,队列,树,二叉树,图等等。

0x01 线性表
1、顺序表示
/*
顺序表
优点:随机存取
缺点:插入删除需要移动大量的元素
*/
template<class T>
class Array
{
public:
    Array()
    {
        m_array = new T[m_iMaxSize];
        m_iLength = 0;
    }
    Array(int n)
    {
        m_array = new T[n];
        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;
};
2、链式表示
  • 单链表
#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 有约束的线性表
1、 栈
/*
数组实现的栈
*/
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
};
笔记:
  • 栈是一种受约束的线性表,体现在,栈只能在一头(一般定义为栈顶)插入和删除,即先进后出。
  • Push,算法是Top++,然后在Top所指的位置插入值。Pop是返回Top所指的元素值,然后Top–。
  • 可以在添加一个变量size,标记栈里面元素的个数,以便查询栈元素个数以及栈是否空或者满。
栈在操作系统中的应用-函数调用

序执行的栈具有以下特点:

  • 每一个进程在用户态对应一个调用栈结构(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指令保存的值。

2、队列
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值