数据结构入门学习③——栈和队列

前言:

本篇博客主要介绍有关栈和队列数据结构相关知识

思维导图介绍:

开始之前,先介绍一下这篇博客主要介绍的主体内容:

栈和队列:

回顾线性表:

在刚才的思维导图里我们也了解到了——栈和队列都属于线性表,那么这里复习一下什么是线性表呢?

线性表——根据数据结构的分类来讲——

1.对于它的逻辑结构而言——它是线性结构

2.而它的物理结构有两种——顺序存储结构和链式存储结构

它通俗的来讲就是把数据按顺序存储在内存中的一种数据结构

那现在就让我们进入正题吧——

基本定义:

栈和队列都属于一种限制出入的线性表,不过它们所限制的方式不同

属于限制线性表先进入到表里的最后才可以获取到——也就是“先进后出”,类比装在弹夹里的子弹,先装进去的最后才打出来。

队列是限制——一端是进入表中的,一端是出表的,讲究"先进先出"的规则,这个其实可以类比排队的 ,先排队的总是可以先进行活动,而一端来人,一端出人。

使用案例:

进制转换

这里的主要是在十进制转化为其余进制而使用的——对于十进制转化为二、八、十六进制,它们都有一个基本的共识——除R取余(这里的R指转化成进制的2、8、16)

而对于这个取余,我们一般都是倒着取,也就是——先除R得到的那个余数在最后一位。

这个很明显的符合我们刚才介绍的栈的特性——先进后出。

因此栈可以在这个进制转换中使用,具体使用方法等介绍完栈的知识再做讲解


括号的匹配

假如给你几个括号,让你判断它们是否都配对成功了。

例如:{([])}这个就是配对成功了,{)[(}而这个显然不是

那么如何解决这一道题目呢——使用栈

这里先基本介绍一下使用方法——

先把一半的括号插入到栈中,然后再插另一半,插一个的时候要判断,是否和栈中的括号匹配,如果不匹配就直接报错,如果匹配则对应的括号出栈,直到所有元素出栈则可以说明所有的括号都匹配成功


表达式求值

对于一个简单的表达式,由于符号的优先级不同,则运算数据也不同,而利用表达式求值可以使用栈的方法,这个设计的算法比较复杂,在讲解完栈后再讲解。


舞伴问题

假如要你要开办一个舞会,舞会上让男女配对跳舞,则男生和女生站两列,她们位置一致则匹配成功,排在前面的,就先和对应的搭档开始跳舞,如果有一对的人比较多,配对不成,则他就成为下一首歌的第一个和异性匹配并跳舞的人。

栈(stack):

定义:

具有先进后出限制的线性表。

有顺序栈和链栈两种形式。

创建栈:

顺序栈的创建——

设置一个结构体,里面存放两个指针——一个指向栈顶,一个指向栈尾。和一个数组大小

构建基本代码:

typedef struct {
    Stackelem *top;
    Stackelem *base;
    int stacksize;
}Sqstack;

注意这里的Stackelem是指放在栈中的元素类型 

两种溢出形式

对于栈而言,有两种溢出形式:

一般而言上溢出只发生在顺序栈中,而链栈由于可以自己手动分配空间,基本不存在上溢出的问题。

1.上溢出,入栈元素过多,超过栈的最大空间,则发生错误,上溢出。

2.下溢出,一直出栈,但是栈里已经没有元素了,因此发生错误,下溢出。

但是,一般把上溢出看为错误,而把下溢出看为一种结束条件

满栈和空栈:

那么如何判断这个栈是否满?或者是否为空?

这里画一个图:

如图所示,这是一个基本的顺序栈,而在这个栈中,初始状态——也就是base和top指向第一个位置,这时候栈内为空,而当top所指向的空间是开辟空间最后一个了,那么栈满了。

那现在理论解释明白了,代码该怎么表示呢?

——

对于栈空:当top==base时栈为空

对于栈满:当top-base==stacksize时栈满

栈空和栈满在下面的一些基本操作中也会用到,所以这里先提前介绍一下如何判断栈空或是栈满的情况。

顺序栈

基本操作:

初始化

初始化就是先开辟一块大小确定的空间,然后base指针指向这块空间,判断是否开辟成功,如果空间开辟失败,则错误退出,如果开辟成功,则让top指针=base指针,让stacksize等于开辟空间的大小

代码:

int initstack(Sqstack& s)
{
	s.base = new int[MAXSIZE];//c++开辟新空间的语句,利用new开辟,相当于c语言中的malloc
	if (s.base)
	{
		return -1;
	}
	s.top = s.base;
	s.size = MAXSIZE;
	return 0;
}
销毁栈

销毁栈,就是需要把栈所占的空间给释放掉,而刚开始开辟的时候是动态开辟,这里就用到delete关键字,这个关键字作用和c语言中的free一样,都是释放掉动态开辟的内存,直接delete base,然后把top和base指针置空,栈长=0即可。

代码:

Status Destorystack (SqStack &s)

{

        if(s.base)

        {

        delete s.base;

        s.stacksize=0;

        s.base=s.top=NULL;

        }

        return OK;

}

置空栈

把栈中元素置空,而不管空间的事,这个操作就直接让top=base即可,这样top和base之间的元素为0,置空栈操作成功

Status ClearStack(SqStack s)

{

        if(s.base)

        {

         s.top=s.base;

        }

        return OK;

};

入栈

假如栈为空则——入栈就是先把top指针的位置给赋值成传进来的值,然后把top指针往上移一位。

 Status Push(Sqstack &s,Stackelem e)

{

       if(s.top-s.base==s.stacksize)

                return ERROR;

        *s.top=e;

        s.top++;

        return 0;

}

出栈

设定一个变量,把top下面位置存放的数据赋值给这个变量,然后top指向这个位置即可

 Status Push(Sqstack &s,Stackelem e)

{

       if(s==NULL)

                return ERROR;

        e=s.--top;

        return 0;

}

判断是否为空

上面已经介绍了如何判断栈是否为空了,就是当top和base相等时栈为空。

代码:

Status  Stackempty(SqStack s)

{

        if(s.top==s.base)

         {

                return true;

        }else{

                return false;

        }

}

链栈

构建:

链栈和顺序栈的最大区别就是物理结构不同,顺序栈用到了c++中的数列,而链栈要用到前面的链表,在链表中存放数据和指针。

这意味之前的结构体构建到这里不能用了,因此这里介绍一下新的构建:

结构体里要包含数据域和指针域

typedef struct StackNode

{

        stackelem data;

        struct StackNode *nextl

}StackNode,*LinkStack;

LinkStack s;//s代表指向结构体类型变量的指针

 

🎄注意:链表的头指针就是栈顶,这个链栈没有头结点,而这个一般不会溢出——出现栈满的情况。

基本操作:

初始化:

Status InitStack(LinkStack &s)

{

        s=NULL;

        return OK;

}

对于链栈的初始化就和顺序栈的有很大区别了,向初始化函数传入头指针,让头指针置为空,即头指针=NULL即可——这里会有人问了,为什么让头指针置为空?不应该是让指针所指的next(指针域)为空吗?

其实,这里要注意一点——链栈的链表没有头结点

🎄头指针和头结点不一样,头结点初始化是要把头结点的next域置为空,而头指针是直接把自己=NULL,置为空,因为指针所占用的空间是存储所指的位置的,指针位置置为空,则对于这个链栈的初始化来说——完成。

入栈

入栈就是先开辟一块空间,空间类型为自建的结构体类型,然后让这块空间的指针域指向头指针所指向的空间,然后让头指针指向新开辟的空间——这样就可以只借助一个头指针来完成栈的操作了

 Status Push(LinkStack &s,stackelem e)

{

        LinkStack a=new StackNode;

        a->data=e;

        a->next=s;

        s=a;

        return OK;

}

出栈

出栈就是先提取出来要出栈的数据域的值,然后让头指针指向出栈的下一个,最后释放掉出栈的空间。

Status Pop(LinkStack &s)

{

       if(s=NULL){

                return ERROR;

        }

        e=s->data;

        p=s;

        s=p->next;

        delete p;

        return OK;

}

队列(queue):

定义:

队列是一种限制进出的线性表,“先进先出”(FIFO),在生活中这个性质比较常见些,比如排队买饭……

而队列是限制一端进,一端出,每一端都有不同的操作。

常见应用:

  • 多用户系统中,多用户排队,按照来的顺序来循环使用CPU和主存。
  • 按照用户的优先级拍成多个队,每个优先级一个队列
  • 实时控制系统,信号按照接受的先后依次处理。
  • 网络电文传输,按照到达的实践依次进行
  • ……

顺序队列

构建表示:

上图就展示了一个队列拥有的基本特征,其中的rear是指向队尾,front是指向队头。

rear端进行进入队列操作,而front端进行出队列操作,所以这里可以引入——如何判断队列为空:

当front和rear指向同一位置时,队列为空。

 那顺序队列的类型构建该怎么构建呢?

示例代码如下:

#define MAXQSIZE 100
typedef struct{
    QElemType *base;//动态分配存储空间
    int rear;
    int front;
}SQueue;

这里写的的rear尾指针和front头指针为int类型,这时候会有些疑惑——不是说这个它们是指针吗?怎么写成整型的形式?

这是因为这里顺序队列所借指的是数组,而且不是动态分配的数组?所以数组的下标和指针有着一样的作用,则可用整型代表更安全。

一般顺序队列

操作:
初始化:

初始化顺序队列,就是把头指针和尾指针指向初始位置即可

代码如下:

Status  InitQueue (SQueue &q)

{

       q.base=new  QElemType[MAXQSIZE];//这里的QElemType是指所队列存放数据的类型,按需求填入不同数据类型。

        if(!q.base)        exit(-1);

        q.front=q.rear=0;

        return OK;

}

入队: 

入队,就是让入队的数据赋值给rear指针指向的区域,再让指针++

即:

q.base[rear]=x;

q.rear++;

出队:  

先把front指针指向的空间存储数据赋值给某个变量,再把front头指针往上移动一位即可

简易代码:

e=q.base[q.front];

q.front++;

注意当front==rear时队列为空,不能再出队。

❀BUT

当我们执行这些操作直到队列满,在空间内,我们发现还有一些空间没有利用,这块空间是出队列时front往上移动而产生的结果,那这块空间我们能不能再利用呢?就像排队一样,排完队的人出队后,后面的人就会自动往前补上。

答案是——当然可以。

下面就介绍队列中比较常用的顺序队列——循环队列

循环队列

这个队列的设置是为了解决假上溢出的较好的方法,那什么是假上溢呢?其实刚才就提到了——当出队后使头指针下的位置为空,而队列内——即头指针和尾指针之间的位置已经满了,如果再入队则会导致溢出,BUT这种溢出被称为假溢出,因为所开辟的空间内还有未用的存储单元,所以解决办法就是循环队列——

将队列设想为一个循环的表:

在这样一个表中可以重复利用开辟的存储空间,当rear=maxqsize时,假如开始端空着,则可以从头利用空间,而当front=maxqsize时也可以如此,这便称为循环队列

操作:

那如何实现这循环队列呢?即当rear+1=maxqsize ,则令rear=0;

——

利用取余符号%来实现一个圈的循环

让rear=rear%maxqsize,当rear<maxqsize时rear仍=rear,当rear=maxqsize时rear=0

同理front也是。

插入元素:

那循环队列如何插入元素?

q.base[q.rear]=x;//x是插入的元素

q.rear=(q.rear+1)%MAXQSIZE

//注意:这里+1的原因是插入元素后,rear需要往上移动一位

删除元素:

e=q.base[q.front];

q.front=(q.front+1)%MAXQSIZE

//这里的+1的原因如上。

队空:

如何判断循环队列为空?

q.rear==q.front

队满:

那如何判断队满呢?

思考一下发现队满也是q.rear==q.front

那如何区分所谓的队空和队满呢?

方法如下:

  • 1.把循环队列空出一格出来,意思是少用一个元素空间
  • 2.设置一个变量,记录元素的个数来判断区分队满和队空
  • 3.设置一个标志区分队空和队满

而最常用的就是少用一块空间,

这样:

当空队时front==rear

当队满时(rear+1)%MAXQSIZE==front

那这个式子为什么用%符号了?——因为循环队列,不清楚到底rear大还是front大,rear指向队列中空出的区域,当rear+1就是可能和front相等,也可能和front差一个MAXQSIZE,那用取余符号列出的式子就可以概括这两种情况,完成判断队满的操作了。

具体的操作:
初始化:

Status  InitQueue (SQueue &q)

{

       q.base=new  QElemType[MAXQSIZE];//这里的QElemType是指所队列存放数据的类型,按需求填入不同数据类型。

        if(!q.base)        exit(-1);

        q.front=q.rear=0;

return OK;

}

 求队列长度:

int LengthQueue(SQueue q)

{

        return ((q.rear-q.front+MAXQSIZE)%MAXQSIZE);

}

这个式子其实有点难理解,下面就进行具体的讲解一下这个式子到底怎么得出的

——

这里列举以下几种情况

如上图,我们列出了它们分别的计算式和结果,计算式的本质式分别为:rear-front,rear-front+Maxqsize,rear-front+Maxqsize

那么现在是想怎么把这两个式子合为一个——取余符号

即rear-front+MAXQSIZE再整体取余MAXQSIZE

入队:

Status EnQueue (SQueue &q,QElemType e)

{

if((q.rear+1)%MAXQSIZE==q.front)

        return ERROR;

q.base[q.rear]=e;

q.rear=(q.rear+1)%MAXQSIZE;

return OK;

}

出队:

Status OutQueue (SQueue &q,QElemType x)

{

if(q.rear+1==q.front)

        return ERROR;

x=q.base[q.front];

q.front=(q.front+1)%MAXQSIZE;

return OK;

}

链队

基本操作:

链队即使用数据结构中的物理结构——链式结构来实现队列的一个数据结构

注意:front为头指针,它指向头结点,而头结点中没有data数据域。

类型定义:

#define MAXQSIZE 100

typedef  struct Qnode{

        QElemType data;

        struct Qnode*next;

}Qnode,*Queueptr;

typedef struct 
{

        Queueptr front;

        Queueptr rear;

}LinkQueue;

初始化链队:

传入LinkQueue自定义类型,并动态开辟一块空间,空间类型为Qnode。front和rear指向同一块空间

Status InitListQueue(LinkQueue &q)

{

        q.front=q.rear=new QNode;

        q.front->next=NULL;

        return OK;

}

入队:

Status InQueue(LinkQueue &q,ElemType e)

{

        LinkQueue s=new QNode;

        s->data=e;

        s->next=NULL;

        q.rear->next=s;

        q.rear=s;

        return OK;

}

出队:

Status OutQueue(LinkQueue &q,ElemType x)

{

       if(q.front==q.rear)        return ERROR;

        p=q.front->next;

        x=p->data;

        front->next=p->next;

        if(q.rear==p)        q.rear=q.front;

        delete p;

        return OK;

}

销毁: 

Status DeleteQueue(LinkQueue &q)

{

        while(q.front)

        {

                p=q.front->next;

                delete q.front;

                q.front=p;

         }

}

总结:

好了,到这里,栈和队列的知识就介绍完毕,欢迎点赞收藏,或者关注专栏,以免找不到哦🤭

文章如有问题,可以私信或者评论区留言,感谢观看完毕o(* ̄▽ ̄*)ブ

  • 31
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大家都是抱着对算法与数据结构极大的兴趣才参加集训的,我们也希望大家学有所成,但是刚刚接触信息学领域的同学往往存在很多困惑,不知道从何入手学习,在这篇向导里,我希望能将自己不多的经验与大家分享,希望对各位有所帮助. 一、语言是最重要的基本功 无论侧重于什么方面,只要是通过计算机程序去最终实现的竞赛,语言都是大家要过的第一道关.亚洲赛区的比赛支持的语言包括C/C++与JAVA.虽然JAVA在应用极为广泛,但是其运行速度不可恭维.而且在以往的比赛中来看,大多数队伍还是采用了C或者C++.而且C语言是大家接触的第一门编程语言,所以我们集训队都采用C和C++混编的方式写代码. 新来的同学可能C的基础知识刚刚学完,还没有接触过C++,其实在赛场上使用纯C的选手还是大有人在的,它们主要是看重了纯C在效率上的优势,所以这部分同学如果时间有限,并不需要急着去学习新的语言,只要提高了自己在算法设计上的造诣,纯C一样能发挥巨大的威力.但是我还是希望大家都能够学点C++. C++相对于C,在输入输出流上的封装大大方便了我们的操作,同时降低了出错的可能性,并且能够很好地实现标准流与文件流的切换,方便了调试的工作。如果有些同学比较在意这点,可以尝试C和C++的混编,毕竟仅仅学习C++的流操作还是不花什么时间的。 C++的另一个支持来源于标准模版库(STL),库中提供的对于基本数据结构的统一接口操作和基本算法的实现可以缩减我们编写代码的长度,这可以节省一些时间。但是,与此相对的,使用STL要在效率上做出一些牺牲,对于输入规模很大的题目,有时候必须放弃STL,这意味着我们不能存在“有了STL就可以不去管基本算法的实现”的想法;另外,熟练和恰当地使用STL必须经过一定时间的积累,准确地了解各种操作的时间复杂度,切忌对STL中不熟悉的部分滥用,因为这其中蕴涵着许多初学者不易发现的陷阱。像STL中的很多容器, vector,queue,stack,map,set等一定要比较熟悉,STL中的sort是必需要掌握的.掌握这些STL知识后写代码的时候相对于纯C会节省不少时间. C语言学习推荐:C程序设计(谭浩强编著) C++学习推荐: C++Prime, C++大学教程.(其实基本上的C++教程都行的…) STL学习推荐: C++Prime,STL标准库.(理论联系实际,边学就用学的最快) 二、以数学为主的基础知识十分重要 虽然被定性为程序设计竞赛,但是参赛选手所遇到的问题更多的是没有解决问题的思路,而不是有了思路却死活不能实现,这就是平时积累的基础知识不够。竞赛中对于基础学科的涉及主要集中于数学,此外对于物理、电路等等也可能有一定应用,但是不多。因此,大一的同学也不必为自己还没学数据结构而感到不知从何入手提高,把数学捡起来吧!下面来谈谈在竞赛中应用的数学的主要分支。 1、离散数学——作为计算机学科的基础,离散数学是竞赛中涉及最多的数学分支,其重中之重又在于图论和组合数学,尤其是图论。 图论之所以运用最多是因为它的变化最多,而且可以轻易地结合基本数据结构和许多算法的基本思想,较多用到的知识包括连通性判断、DFS和BFS,关节点和关键路径、欧拉回路、最小生成树、最短路径、二部图匹配和网络流等等。虽然这部分的比重很大,但是往往也是竞赛中的难题所在,如果有初学者对于这部分的某些具体内容暂时感到力不从心,也不必着急,可以慢慢积累。 竞赛中设计的组合计数问题大都需要用组合数学来解决,组合数学中的知识相比于图论要简单一些,很多知识对于小学上过奥校的同学来说已经十分熟悉,但是也有一些部分需要先对代数结构中的群论有初步了解才能进行学习。组合数学在竞赛中很少以难题的形式出现,但是如果积累不够,任何一道这方面的题目却都有可能成为难题。 2、数论——以素数判断和同余为模型构造出来的题目往往需要较多的数论知识来解决,这部分在竞赛中的比重并不大,但只要来上一道,也足以使知识不足的人冥思苦想上一阵时间。素数判断和同余最常见的是在以密码学为背景的题目中出现,在运用密码学常识确定大概的过程之后,核心算法往往要涉及数论的内容。 3、计算几何——计算几何相比于其它部分来说是比较独立的,就是说它和其它的知识点很少有过多的结合,较常用到的部分包括——线段相交的判断、多边形面积的计算、内点外点的判断、凸包等等。计算几何的题目难度不会很大,但也永远不会成为最弱的题。 4、线性代数——对线性代数的应用都是围绕矩阵展开的,一些表面上是模拟的题目往往可以借助于矩阵来找到更好的算法。 5、概率论——竞赛是以黑箱来判卷的,这就是说你几乎不能动使用概率算法的念头,但这也并不是说概率就没有用。关于这一点,只有通过一定的练习才能体会。而且近年来概率题出现的次数越来越多了.
讲述了c++的各种使用的数据结构和算法,对于初学者和已经入门的都很有帮助,里面的内容有 目 录 译者序 前言 第一部分 预备知识 第1章 C++程序设计 1 1.1 引言 1 1.2 函数与参数 2 1.2.1 传值参数 2 1.2.2 模板函数 3 1.2.3 引用参数 3 1.2.4 常量引用参数 4 1.2.5 返回值 4 1.2.6 递归函数 5 1.3 动态存储分配 9 1.3.1 操作符new 9 1.3.2 一维数组 9 1.3.3 异常处理 10 1.3.4 操作符delete 10 1.3.5 二维数组 10 1.4 类 13 1.4.1 类Currency 13 1.4.2 使用不同的描述方法 18 1.4.3 操作符重载 20 1.4.4 引发异常 22 1.4.5 友元和保护类成员 23 1.4.6 增加#ifndef, #define和#endif语句 24 1.5 测试与调试 24 1.5.1 什么是测试 24 1.5.2 设计测试数据 26 1.5.3 调试 28 1.6 参考及推荐读物 29 第2章 程序性能 30 2.1 引言 30 2.2 空间复杂性 31 2.2.1 空间复杂性的组成 31 2.2.2 举例 35 2.3 时间复杂性 37 2.3.1 时间复杂性的组成 37 2.3.2 操作计数 37 2.3.3 执行步数 44 2.4 渐进符号(O、 健?、 o) 55 2.4.1 大写O符号 56 2.4.2 椒?58 2.4.3 符号 59 2.4.4 小写o符号 60 2.4.5 特性 60 2.4.6 复杂性分析举例 61 2.5 实际复杂性 66 2.6 性能测量 68 2.6.1 选择实例的大小 69 2.6.2 设计测试数据 69 2.6.3 进行实验 69 2.7 参考及推荐读物 74 第二部分 数据结构 第3章 数据描述 75 3.1 引言 75 3.2 线性表 76 3.3 公式化描述 77 3.3.1 基本概念 77 3.3.2 异常类NoMem 79 3.3.3 操作 79 3.3.4 评价 83 3.4 链表描述 86 3.4.1 类ChainNode 和Chain 86 3.4.2 操作 88 3.4.3 扩充类Chain 91 3.4.4 链表遍历器类 92 3.4.5 循环链表 93 3.4.6 与公式化描述方法的比较 94 3.4.7 双向链表 95 3.4.8 小结 96 3.5 间接寻址 99 3.5.1 基本概念 99 3.5.2 操作 100 3.6 模拟指针 102 3.6.1 SimSpace的操作 103 3.6.2 采用模拟指针的链表 106 3.7 描述方法的比较 110 3.8 应用 111 3.8.1 箱子排序 111 3.8.2 基数排序 116 3.8.3 等价类 117 3.8.4 凸包 122 3.9 参考及推荐读物 127 第4章 数组和矩阵 128 4.1 数组 128 4.1.1 抽象数据类型 128 4.1.2 C++数组 129 4.1.3 行主映射和列主映射 129 4.1.4 类Array1D 131 4.1.5 类Array2D 133 4.2 矩阵 137 4.2.1 定义和操作 137 4.2.2 类Matrix 138 4.3 特殊矩阵 141 4.3.1 定义和应用 141 4.3.2 对角矩阵 143 4.3.3 三对角矩阵 144 4.3.4 三角矩阵 145 4.3.5 对称矩阵 146 4.4 稀疏矩阵 149 4.4.1 基本概念 149 4.4.2 数组描述 149 4.4.3 链表描述 154 第5章 堆 161 5.1 抽象数据类型 161 5.2 派生类和继承 162 5.3 公式化描述 163 5.3.1 Stack的效率 164 5.3.2 自定义Stack 164 5.4 链表描述 166 5.5 应用 169 5.5.1 括号匹配 169 5.5.2 汉诺塔 170 5.5.3 火车车厢重排 172 5.5.4 开关盒布线 176 5.5.5 离线等价类问题 178 5.5.6 迷宫老鼠 180 5.6 参考及推荐读物 188 第6章 队列 189 6.1 抽象数据类型 189 6.2 公式化描述 190 6.3 链表描述 194 6.4 应用 197 6.4.1 火车车厢重排 197 6.4.2 电路布线 201 6.4.3 识别图元 204 6.4.4 工厂仿真 206 6.5 参考及推荐读物 217 第7章 跳表和散列 218 7.1 字典 218 7.2 线性表描述 219 7.3 跳表描述 222 7.3.1 理想情况 222 7.3.2 插入和删除 223 7.3.3 级的分配 224 7.3.4 类SkipNode 224 7.3.5 类SkipList 225 7.3.6 复杂性 229 7.4 散列表描述 229 7.4.1 理想散列 229 7.4.2 线性开型寻址散列 230 7.4.3 链表散列 234 7.5 应用——文本压缩 238 7.5.1 LZW压缩 239 7.5.2 LZW压缩的实现 239 7.5.3 LZW解压缩 243 7.5.4 LZW解压缩的实现 243 7.6 参考及推荐读物 247 第8章 二叉树和其他树 248 8.1 树 248 8.2 二叉树 251 8.3 二叉树的特性 252 8.4 二叉树描述 253 8.4.1 公式化描述 253 8.4.2 链表描述 254 8.5 二叉树常用操作 256 8.6 二叉树遍历 256 8.7 抽象数据类型BinaryTree 259 8.8 类BinaryTree 260 8.9 抽象数据类型及类的扩充 263 8.9.1 输出 263 8.9.2 删除 264 8.9.3 计算高度 264 8.9.4 统计节点数 265 8.10 应用 265 8.10.1 设置信号放大器 265 8.10.2 在线等价类 268 8.11 参考及推荐读物 275 第9章 优先队列 276 9.1 引言 276 9.2 线性表 277 9.3 堆 278 9.3.1 定义 278 9.3.2 最大堆的插入 279 9.3.3 最大堆的删除 279 9.3.4 最大堆的初始化 280 9.3.5 类MaxHeap 281 9.4 左高树 285 9.4.1 高度与宽度优先的最大及最小 左高树 285 9.4.2 最大HBLT的插入 287 9.4.3 最大HBLT的删除 287 9.4.4 合并两棵最大HBLT 287 9.4.5 初始化最大HBLT 289 9.4.6 类MaxHBLT 289 9.5 应用 293 9.5.1 堆排序 293 9.5.2 机器调度 294 9.5.3 霍夫曼编码 297 9.6 参考及推荐读物 302 第10章 竞?303 10.1 引言 303 10.2 抽象数据类型WinnerTree 306 10.3 类WinnerTree 307 10.3.1 定义 307 10.3.2 类定义 307 10.3.3 构造函数、析构函数及Winner 函数 308 10.3.4 初始化赢者树 308 10.3.5 重新组织比赛 310 10.4 输者树 311 10.5 应用 312 10.5.1 用最先匹配法求解箱子装载 问题 312 10.5.2 用相邻匹配法求解箱子装载 问题 316 第11章 搜索树 319 11.1 二叉搜索树 320 11.1.1 基本概念 320 11.1.2 抽象数据类型BSTree和 IndexedBSTree 321 11.1.3 类BSTree 322 11.1.4 搜索 322 11.1.5 插入 323 11.1.6 删除 324 11.1.7 类DBSTree 326 11.1.8 二叉搜索树的高度 327 11.2 AVL树 328 11.2.1 基本概念 328 11.2.2 AVL树的高度 328 11.2.3 AVL树的描述 329 11.2.4 AVL搜索树的搜索 329 11.2.5 AVL搜索树的插入 329 11.2.6 AVL搜索树的删除 332 11.3 红-黑树 334 11.3.1 基本概念 334 11.3.2 红-黑树的描述 336 11.3.3 红-黑树的搜索 336 11.3.4 红-黑树的插入 336 11.3.5 红-黑树的删除 339 11.3.6 实现细节的考虑及复杂性分析 343 11.4 B-树 344 11.4.1 索引顺序访问方法 344 11.4.2 m 叉搜索树 345 11.4.3 m 序B-树 346 11.4.4 B-树的高度 347 11.4.5 B-树的搜索 348 11.4.6 B-树的插入 348 11.4.7 B-树的删除 350 11.4.8 节点结构 353 11.5 应用 354 11.5.1 直方图 354 11.5.2 用最优匹配法求解箱子装载 问题 357 11.5.3 交叉分布 359 11.6 参考及推荐读物 363 第12章 图 365 12.1 基本概念 365 12.2 应用 366 12.3 特性 368 12.4 抽象数据类型Graph和Digraph 370 12.5 无向图和有向图的描述 371 12.5.1 邻接矩阵 371 12.5.2 邻接压缩表 373 12.5.3 邻接链表 374 12.6 网络描述 375 12.7 类定义 376 12.7.1 不同的类 376 12.7.2 邻接矩阵类 377 12.7.3 扩充Chain类 380 12.7.4 类LinkedBase 381 12.7.5 链接类 382 12.8 图的遍历 386 12.8.1 基本概念 386 12.8.2 邻接矩阵的遍历函数 387 12.8.3 邻接链表的遍历函数 388 12.9 语言特性 389 12.9.1 虚函数和多态性 389 12.9.2 纯虚函数和抽象类 391 12.9.3 虚基类 391 12.9.4 抽象类和抽象数据类型 393 12.10 图的搜索算法 394 12.10.1 宽度优先搜索 394 12.10.2 类Network 395 12.10.3 BFS的实现 395 12.10.4 BFS的复杂性分析 396 12.10.5 深度优先搜索 397 12.11 应用 399 12.11.1 寻找路径 399 12.11.2 连通图及其构件 400 12.11.3 生成树 402 第三部分 算法设计方法 第13章 贪婪算法 405 13.1 最优化问题 405 13.2 算法思想 406 13.3 应用 409 13.3.1 货箱装船 409 13.3.2 0/1背包问题 410 13.3.3 拓扑排序 412 13.3.4 二分覆盖 415 13.3.5 单源最短路径 421 13.3.6 最小耗费生成树 424 13.4 参考及推荐读物 433 第14章 分而治之算法 434 14.1 算法思想 434 14.2 应用 440 14.2.1 残缺棋盘 440 14.2.2 归并排序 443 14.2.3 快速排序 447 14.2.4 选择 452 14.2.5 距离最近的点对 454 14.3 解递归方程 462 14.4 复杂性的下限 463 14.4.1 最小最大问题的下限 464 14.4.2 排序算法的下限 465 第15章 动态规划 467 15.1 算法思想 467 15.2 应用 469 15.2.1 0/1背包问题 469 15.2.2 图像压缩 471 15.2.3 矩阵乘法链 476 15.2.4 最短路径 480 15.2.5 网络的无交叉子集 483 15.2.6 元件折叠 486 15.3 参考及推荐读物 491 第16章 回溯 492 16.1 算法思想 492 16.2 应用 496 16.2.1 货箱装船 496 16.2.2 0/1背包问题 503 16.2.3 最大完备子图 506 16.2.4 旅行商问题 508 16.2.5 电路板排列 510 第17章 分枝定界 516 17.1 算法思想 516 17.2 应用 519 17.2.1 货箱装船 519 17.2.2 0/1背包问题 526 17.2.3 最大完备子图 528 17.2.4 旅行商问题 529 17.2.5 电路板排列 532
12篇学通csharp网络编程——第四篇 TCP应用编程 12篇学通csharp网络编程——第三篇 HTTP应用编程(下) 12篇学通csharp网络编程——第二篇 HTTP应用编程(上) 12篇学通csharp网络编程——第一篇 基础之进程线程 Lucene(1)lucene,你也会(7篇)——第一篇 快速入门 MongoDB(8)8天学通MongoDB——第八天 驱动实践 8天学通MongoDB——第七天 运维技术 8天学通MongoDB——第六天 分片技术 8天学通MongoDB——第五天 主从复制 8天学通MongoDB——第四天 索引操作 8天学通MongoDB——第三天 细说高级操作 8天学通MongoDB——第二天 细说增删查改 8天学通MongoDB——第一天 基础入门 UML系列(4)团队沟通利器之UML——类图 团队沟通利器之UML—— 序列图 团队沟通利器之UML——用例图 团队沟通利器之UML——活动图 wcf系列(5)wcf系列学习5天速成——第五天 服务托管 wcf系列学习5天速成——第四天 wcf之分布式架构 wcf系列学习5天速成——第三天 事务的使用 wcf系列5天速成——第二天 binding的使用(2) wcf系列5天速成——第一天 binding的使用(1) wpf系列(8)8天入门wpf—— 第八天 最后的补充 8天入门wpf—— 第七天 画刷 8天入门wpf—— 第六天 细说控件 8天入门wpf—— 第五天 数据绑定 8天入门wpf—— 第四天 模板 8天入门wpf—— 第三天 样式 8天入门wpf—— 第二天 xaml详解 8天入门wpf—— 第一天 基础概念介绍 并行开发(8)8天玩转并行开发——第八天 用VS性能向导解剖你的程序 8天玩转并行开发——第七天 简要分析任务与线程池 8天玩转并行开发——第六天 异步编程模型 8天玩转并行开发——第五天 同步机制(下) 8天玩转并行开发——第四天 同步机制(上) 8天玩转并行开发——第三天 plinq的使用 8天玩转并行开发——第二天 Task的使用 8天玩转并行开发——第一天 Parallel的使用 多线程系列(5)5天不再惧怕多线程——第五天 线程池 5天不再惧怕多线程——第四天 信号量 5天不再惧怕多线程——第三天 互斥体 5天不再惧怕多线程——第二天 锁机制 5天不再惧怕多线程——第一天 尝试Thread 经典算法专题(21)经典算法题每日演练——第二十一题 十字链表 经典算法题每日演练——第二十题 三元组 经典算法题每日演练——第十九题 双端队列 经典算法题每日演练——第十八题 外排序 经典算法题每日演练——第十七题 Dijkstra算法 经典算法题每日演练——第十六题 Kruskal算法 经典算法题每日演练——第十五题 并查集 经典算法题每日演练——第十四题 Prim算法 经典算法题每日演练——第十三题 赫夫曼树 经典算法题每日演练——第十二题 线段树 经典算法题每日演练——第十一题 Bitmap算法 经典算法题每日演练——第十题 树状数组 经典算法题每日演练——第九题 优先队列 经典算法题每日演练——第八题 AC自动机 经典算法题每日演练——第七题 KMP算法 经典算法题每日演练——第六题 协同推荐SlopeOne 算法 经典算法题每日演练——第五题 字符串相似度 经典算法题每日演练——第四题 最长公共子序列 经典算法题每日演练——第三题 猴子吃桃 经典算法题每日演练——第二题 五家共井 经典算法题每日演练——第一题 百钱买百鸡 开发利器系列(1)介绍一个小工具 Linqer 那点所谓的分布式(2)那点所谓的分布式——memcache 那点所谓的分布式——redis 树结构专题(5)6天通吃树结构—— 第五天 Trie树 6天通吃树结构—— 第四天 伸展树 6天通吃树结构—— 第三天 Treap树 6天通吃树结构—— 第二天 平衡二叉树 6天通吃树结构—— 第一天 二叉查找树 算法速成系列(15)算法系列15天速成——第十五天 图【下】(大结局) 算法系列15天速成——第十四天 图【上】 算法系列15天速成——第十三天 树操作【下】 算法系列15天速成——第十二天 树操作【中】 算法系列15天速成——第十一天 树操作(上) 算法系列15天速成——第十天 算法系列15天速成——第九天 队列 算法系列15天速成——第八天 线性表【下】 算法系列15天速成——第七天 线性表【上】 算法系列15天速成——第六天 五大经典查找【下】 算法系列15天速成——第五天 五大经典查找【中】 算法系列15天速成——第四天 五大经典查找【上】 算法系列15天速成——第三天 七大经典排序【下】 算法系列15天速成——第二天 七大经典排序【中】 算法系列15天速成——第一天 七大经典排序【上】 算法洗脑系列(8)算法洗脑系列(8篇)——第八篇 概率思想 算法洗脑系列(8篇)——第七篇 动态规划 算法洗脑系列(8篇)——第六篇 回溯思想 算法洗脑系列(8篇)——第五篇 分治思想 算法洗脑系列(8篇)——第四篇 枚举思想 算法洗脑系列(8篇)——第三篇 贪心思想 算法洗脑系列(8篇)——第二篇 递归思想 算法洗脑系列(8篇)——第一篇 递推思想 天籁数学(3)天籁数学——数列篇(3) 天籁数学——数列篇(2) 天籁数学——数列篇(1) 图形图像(1)玩玩图形图像——第一篇:图片灰度化 小爬虫系列(4)玩玩小爬虫——抓取时的几个小细节 玩玩小爬虫——抓取动态页面 玩玩小爬虫——试搭小架构 玩玩小爬虫——入门

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值