数据结构(一)

/*数据结构与算法
数据结构概述
    定义
        我们如何把现实中复杂而大量的问题以特定的数据结构
        和特定的存储功能保存到主存储器(内存)中,以及在
        此基础上实现某个功能而执行的相应操作,这个相应的
        操作叫算法

        数据结构=个体+个体关系

        算法(狭义)=对存储数据的操作

    算法
        算法是解题的方法和步骤

        衡量算法复杂度
            1.时间复杂度
                大概程序要执行的次数
                运行次数最多的步骤的次数
            2.空间复杂度
                算法执行过程中大概所占用的的内存
            3.难易程度

            4.健壮性

        狭义的算法和数据的存储关系有关
        广义的算法与数据的存储关系无关
        泛型:
            利用某种技术达到的结果就是:不同的存储方式,执行操作时是一样的

    数据结构的地位
        数据结构是软件中最核心的课程
        程序=数据的存储+数据的操作+可以被计算机实现的语言

预备知识:
        一、指针
            指针的重要性:
                指针是c语言的灵魂
            定义:
                地址
                    内存单元的编号
                    从零开始的非负整数
                    范围:0-ffffffff{0-4g-1}
                指针
                    指针就是地址,地址就是指针
                    指针变量是存放内存单元地址的变量
                    指针的本质是一个操作受限的非负整数
            分类:
                1.基本指针的类型
                    基本概念
                        int i=10;
                        int *p=&i;
                        1).p存放了i的地址
                        2).p和i是两个两个完全不同的变量
                        3).p指向i,*p就是i变量本身
                    注意
                        指针变量也是变量,只能存放内存单元的地址
                        普通变量前不能加*;
                        常量和表达式前面不能加&;
                    函数与指针
                        1).实参为相关变量的地址
                        2).形参以该变量的类型为类型的指针变量
                        3).以*形参变量名的方式可以改变数据
                2.指针与数组的关系
                    指针和一维数组
                        数组名
                            一维数组是个指针常量
                            存放的是一维数组第一个元素的地址
                            它的值是不变的
                            一维数组名指向的是第一个元素的地址
                        下标和指针的关系
                            a[i]<->*(a+i)【a+sizeof(数组类型)】
                        指针变量的运算
                            两个指针变量不能加,不能乘,不能除
                            两个指针变量属于同一数组时可以相减
                            指针变量可以加减一个整数
                        数组开辟的是一块连续的存储空间

        二、结构体
            为什么会出现结构体
                为了表示一些复杂的数据,而普通的基本数据变量无法满足要求
            什么叫结构体
                结构提示用户根据实际需要自己定义的基本数据类型
            如何使用结构体
                两种方式:
                    struct Student st={1000,"zhangsan",20};
                    struct Student * pst=&st;

                    1.st.sid;

                    2.pst->sid;<->(*pst).sid;
                        pst所指向的结构体变量中的sid这个成员
            注意事项:
                结构体变量不能加减乘除,但是可以赋值;
                普通结构体变量和结构体指针变量作为函数传参的问题

        三、动态内存的分配与释放
            动态构造一维数组
                假设构造一int型数组
                int * p=(int *)malloc(int len);//A aa=new()<->A * pa=(A*)malloc(sizeof(A));
                    1.动态分配sizeof(int)*len个字节的内存

                    2.int * 强制转换地址的类型,以规定每个
                      数据所占用的空间

                    3.malloc只有一个int型的形参,表示要求系统分配的字节数

                    4.malloc函数的功能是请求系统分配sizeof(int)*len个字节
                      的空间,如果分配成功则返回第一个字节地址,不成功则
                      返回NULL;

                    5.(int *)是把第一个字节的地址转化为四个字节的地址,
                      这样p就指向了第一个四个字节,p+i就指向了第i+1个四个
                      字节,如果是double则是一次8个字节

                    6.malloc动态分配的内存必须用free()进行释放,否则一直存在,
                      因此可以跨函数使用内存

                    7.指针/地址只占四个字节

        四、函数:根据功能设置流程,从而功能,通过试数来找错与优化


模块一:线性结构【把所有的节点用一根直线穿起来】
    连续存储【数组】
        1.什么叫做数组
            元素类型相同

        2.数组的优缺点


    离散存储【链表】
        是后续学习的基础

        typedef的用法

        定义:
            n个节点离散分配
            彼此通过指针相连
            每个节点只有一个前驱节点,每个节点只有一个后继节点
            首节点没有前驱节点,尾节点没有后继节点

            专业术语
                首节点:
                    第一个有效的节点
                尾节点:
                    最后一个有效的节点
                头结点:
                    头结点的数据类型和首节点的数据类型相同
                    第一个有效节点之前的节点
                    头结点存放有效数字
                    加头节点的目的是为了简化对链表的操作
                头指针:
                    指向头节点的指针变量,存放头结点的地址
                尾指针:
                    指向尾节点的指针变量
            确定一个链表需要几个参数
                 只需要头指针,通过头指针可以推算出来链 表的所有参数
        分类
            单链表
            双链表
                每一个节点有两个指针域

            循环链表
                能通过任何一个节点找到其他节点
                就是尾节点的尾指针指向头结点
                尾指针即是头指针
            非循环链表

        算法
            遍历
            查找
            清空
            销毁
            求长度
            排序
            插入节点
            删除节点


        链表的优缺点

    线性结构的常见应用--栈
        定义
            一种可以实现“先进后出”的存储结构
            栈类似箱子

        分类
            静态栈【数组实现】
            动态栈【链表实现】

        算法
            出栈
            入栈

        应用
            函数调用
            中断
            表达式求值
            内存分配
            缓存处理
            迷宫

    线性结构的常见应用--队列
        定义
            一种可以实现“先进先出”的存储结构
            类似排队
        分类
            静态队列--用数组实现
                1.静态队列为什么必须是循环队列

                2.循环队列需要几个参数来确定
                    需要两个参数来确定
                        1.front
                        2.rear

                3.循环队列各个参数的含义
                    建议初学者先记住,然后慢慢体会
                        1.队列初始化
                            front和rear的值都是零
                        2.队列非空
                            front代表的是队列的第一个元素
                            rear代表的是队列的最后一个有效元素的下一位、
                        3.队列空
                            front和rear相等,但不一定为零

                4.循环队列入队伪算法讲解
                    1.将值存入数组
                    2.r=(r+1)%数组的长度

                5.循环队列出队伪算法讲解
                    1.f=(f+1)%数组的长度

                6.如何判断循环队列是否为空
                    front==rear

                7.如何判断循环队列是否已满
                    预备知识
                        front的值可能比rear大
                        front的值可能比rear小
                        也可能相等
                    两种方式
                        1.多增加一个表标识参数
                        2.少用一个元素【通常使用】
                            无论什么时候都是从front到rear
                            所以当(r+1)%数组长度==f时队列满

                8.循环队列的程序演示

            链式队列

        算法
            出队
            入队

        队列的具体应用:
                所有和时间有关的操作都与队列有关

    专题:递归
        定义:
            一个函数直接或间接调用自己
            函数调用的过程类似栈的使用

        递归的理解:
            规模大的问题的解决依赖于规模较小的问题的解决而解决
            当规模递减到某种程度,(值可以递增,但是规模必须递
            减)当这个规模下问题可以很容易解决时再递推回来,这
            个问题就解决了

        函数调用:
            当在一个函数的运行期间调用另一个函数时,
            一、在运行被调函数前,系统需要完成三件事
                1.将所有的实际参数,返回地址等信息传递给被调函数保存
                2.为被调函数的局部变量分配存储空间
                3.将控制转移到被调函数的入口

            二、执行函数步骤

            三、从被调函数返回主调函数之前,系统要完成三件事
                1.保存被调函数的返回结果
                2.释放被调函数的存储空间
                3.依照被调函数保存的返回地址将控制转移到主调函数
            当有多个函数相互调用的时候,按照“后调用先返回”的原则
            系统将整个程序运行时所用的数据空间安排到一个栈中,每
            当调用一个函数时,就在栈顶分配一个存储区,进行压栈操
            作,每当函数退出时,就释放它的存储区,进行出栈操作,
            当前运行的函数永远都在栈顶位置

        递归需要满足的三个条件
            1.递归必须得有一个明确的终止条件
            2.该函数所处理的数据规模必须在递减
            3.这个转化必须是可解的

        递归与循环
            递归:
                易于理解
                速度慢
                存储空间大
            循环:
                不易理解
                速度快
                存储空间小

        举例:
            1.求阶乘

            2.1+2+3+...+100的和

            3.汉诺塔

            4.走迷宫

        递归的应用
            树和森林就是以递归的方式定义的
            树和图的很多算法就是以递归实现的
            很多数学公式就是以递归的方式定义的

模块二:非线性结构
    树
        定义
            1.有且只有一个称为根的节点
            2.有若干个互不相交的子树,这些子树本身就是一棵树

            1.树是由节点和边组成
            2.每个节点只有一个父节点,但可以有多个子节点
            3.但是有一个节点例外,该节点没有父节点,此节点称为根节点

            专业术语
                节点      父节点     子节点
                子孙      堂兄弟
                深度:
                    从根节点到最底层的层数
                叶子节点:
                    没有子节点的节点
                非终端节点:
                    实际就是非叶子节点
                度:
                    子节点的个数
                树的度:
                    含有最多子节点的节点称为树的度

        分类
            一般树
                任意一个节点的个数都不受限制

            二叉树
                任意一个节点的子节点个数最多两个,且子节点的位置不可改变,即左右节点不可改变位置

                分类
                    一般二叉树

                    满二叉树
                        在不增加树的节点的前提下,无法再多添加

                    完全二叉树
                        如果只是删除了满二叉树最底层最右边的若干个节点,
                        这样形成的二叉树就是完全二叉树,只能删最底层的元
                        素,只能从右向左删

                    满二叉树一定是完全二叉树

            森林
                n个互不相交的树的集合

        树的存储
            二叉树的存储
                连续存储【完全二叉树】
                    优点:
                        查找或判断某个节点的父节点和子节点非常快
                    缺点:
                        耗用内存空间过大

                链式存储
                    通过指针域进行连接
                    分为四块,三个指针域,一个数据域

            一般树的存储
                 双亲表示法
                    求父节点方便
                    存放父节点的下标
                 孩子表示法
                    求子节点方便
                    存放所有子节点的地址
                 双亲孩子表示法

                 二叉树表示法
                    把一个普通树转化称二叉树来存储
                    具体转化方法:
                        任意一个节点的左指针指向它的第一个孩子
                        任意一个节点的右指针指向它的右边第一个兄弟
                    一个普通树转化成的二叉树一定没有右子树?
                    左边的是孩子,右边的是兄弟


            森林的存储
                将几个根节点看成兄弟,然后使用二叉树表示法


        树的操作
            只要节点非零就是一棵子树
            遍历
                先序遍历【先访问根节点】
                    1.访问根节点
                    2.先序访问左子树
                    3.先序访问右子树

                中序遍历【中间访问根节点】
                    1.中序访问左子树
                    2.访问根节点
                    3.中序访问右子树

                后序遍历【最后访问根节点】
                    1.后序遍历左子树
                    2.后序访问右子树
                    3.访问根节点

            已知两种遍历序列求原始二叉树
                单个的遍历序列无法还原出原始的二叉树
                通过先序和后序无法还原出原始的二叉树
                只有通过先序和中序或中序和后序才能还原出原始的二叉树

                先序:ABCDEFGH
                中序:BDCEAFHG
                可以求出原始二叉树
                可以求出后序:DECBHGFA

                中序:BDCEAFHG
                后序:DECBHGFA
                可以求出原始二叉树
                可以求先序:ABCDEFGH

        树的应用
            树是数据库中数据组织的一种重要形式
            操作系统子父进程的关系本身就是一颗树
            面向对象语言中类的继承关系本身就是一颗树
            赫夫曼树

    图

模块三:查找和排序
    折半查找

    排序:
        冒泡
        插入
        选择
        快排
        归并排序

模块四:
    c++stl
        迭代器
            begin()	返回一个迭代器,指向第一个元素
            end()	返回一个迭代器,指向最后一个元素之后
            容器

            vector:vector<类型> 名字;
            vector<int> ve;初始化
            ve.clear();清空ve里的所有元素。
            ve.empty();判断ve是否为空,如果是返回true,否则false
            ve.size();返回ve的长度。注意这里返回的类型是unsigned int,如果ve是空的ve.size()
            ve.pop_back();删除数组里的最后一个元素。
            ve.push_back(const value_type &val);作用是在数组后面增加一个元素。括号里填的是ve里装的东西

            set:set<类型> 名字;
            set<int> se
            se.insert(1);插入元素1
            if(se.find(1)!=se.end());判断元素是否属于集合
            se.erase(1);删除元素1(如果存在)
            如何通过迭代器从小到大遍历所有元素:
            for (set<string>::iterator i = d.begin(); i != d.end(); i++)
            cout << *i << endl;

            map:map<类型 类型> 名字;
            map<int string> ma;

            queue:queue<类型> 名字;
            queue<int> qu;
            qu.front();获得队首元素

            priority_queue:默认优先级从高到低排列
            priority_queue<int> pqu;
            top();获得队首元素

            next_permutation:求全排列,要包含头文件<algorithm>
            next_permutation(a,a+2);数组

再次讨论什么是数据结构
    数据结构是研究数据的存储与数据的操作的一门学问
    数据的存储分为两部分
        个体的存储
        个体关系的存储
        数据存储的核心是个体关系的存储

什么是泛型
    同一种逻辑结构,无论该逻辑结构物理存储方式是什么样的
    我们都可以对它执行相应的操作

*/


/*1.vector:在使用它时,需要包含头文件vector,#include<vector>.
    vector 容器与数组相比其优点在于它能够根据需要随时
    自动调整自身的大小以便容下所要放入的元素,提供了许
    多的方法来对自身进行操作.*/

    /*2.初始化:vector<int> a ;                                //声明一个int型向量a
    vector<int> a(10) ;                            //声明一个初始大小为10的向量
    vector<int> a(10, 1) ;                         //声明一个初始大小为10且初始值都为1的向量
    vector<int> b(a) ;                             //声明并用向量a初始化向量b
    vector<int> b(a.begin(), a.begin()+3) ;        //将a向量中从第0个到第2个(共3个)作为向量b的初始值
    int n[] = {1, 2, 3, 4, 5} ;
    vector<int> a(n, n+5) ;              //将数组n的前5个元素作为向量a的初值
    vector<int> a(&n[1], &n[4]) ;        //将n[1] - n[4]范围内的元素作为向量a的初值*/

    /*3.输入输出:vector<int> a(10, 0) ;      //大小为10初值为0的向量a
    //对其中部分元素进行输入
    cin >>a[2] ;
    cin >>a[5] ;
    cin >>a[6] ;
    //全部输出
    int i ;
    for(i=0; i<a.size(); i++)
    cout<<a[i]<<" " ;*/
    /*4.迭代器(遍历器):vector<int>::iterator
    vector<int>::iterator t ;//先声明才能用——数组名.begin(),数组名.end()。
    for(t=a.begin(); t!=a.end(); t++)//都是地址
        cout<<*t<<" " ;// *t 为指针的间接访问形式, 意思是访问t所指向的元素值。*/
    /*5.vector向量的各种操作
    1)a.size()  //获取向量a中的元素个数
    2)a.empty() //判断向量a是否为空,空为一,不空为零。
    3)a.clear() //清空向量a中的元素
    4)a.max_size()       // 返回容器所能储存的最大的元素数目。
    5)a.pop_back()       // 删除最后一个数据。
    6)a.push_back(数据)  // 在尾部加入一个数据。
    7)a = b ;   //将b向量复制到a向量中
    8)insert    //插入前面
        1.a.insert(a.begin(), 1000);    //将1000插入到向量a的起始位置前
        2.a.insert(a.begin(), 3, 1000) ;//将1000分别插入到向量元素位置的0-2处(共3个元素)
        3.vector<int> a(5, 1) ;
          vector<int> b(10) ;
          b.insert(b.begin(), a.begin(), a.end()) ;//将a.begin(), a.end()之间的全部元素插入到b.begin()前
    9)erase      //删除
        1.b.erase(b.begin()) ;                     //将起始位置的元素删除
        2.b.erase(b.begin(), b.begin()+3) ;        //将(b.begin(), b.begin()+3)之间的元素删除
    10)swap
        1.b.swap(a);//a向量与b向量进行交换
    11)vector<int>::iterator t ;//先声明才能用——数组名.begin(),数组名.end()。
       x = find( a.begin( ), a.end( ), 3 );//查找,在向量a中查找a,返回地址如果地址==a.end,则未查找到元素
    return 0;
}*/
/*对于vector来说,每一次删除和插入,指针都有可能失效,不要使用过期的iterator*/


/*
1.next_permutation(a,a+n)改变数组为后一个排列(字典序的前后)

2.next_permutation(node,node+n,cmp)可以按照自定义的排序方式cmp进行排序

3.prev_permutation(start,end)求的是当前排列的前一个排列

#include <iostream>
#include <algorithm>//所需头文件
using namespace std;
int main()
{
    int n;
    cin>>n;
    int a[100];
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    sort(a,a+n);//需要先从小到大排序(升序排序)
    do
    {
        for(int i=0;i<n;i++)
        {
            cout<<a[i];
        }
        cout<<endl;
    }while(next_permutation(a,a+n)==1);//如果还有排列的话返回true,否则返回false
    return 0;
}*/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值