重邮23考研802数据结构考试大纲笔记

文章目录

802数据结构大纲

试卷结构:

        1.选择题 30',15x2。
        2.填空题 30',10x3。
        3.综合应用题,60',7题不等分。
        4.编程题,30',15x2。

一、概述

(一)熟悉数据结构相关术语含义,掌握基本概念

1.数据结构三要素:
(1)三要素包括:逻辑结构、存储结构、数据的运算。
(2)逻辑结构包括:线性结构(线性表、栈、队列)和非线性结构(树、图、集合)。
2.数据:
是对客观事物的符号表示,在计算机科学中表示所有能输入到计算机中并被计算机处理的符号的总称。
3.数据元素:
是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理。一个数据元素可以有多个数据项,数据项是不可分割的最小单位。
4.数据对象:
性质相同的数据元素的集合,是数据的一个子集。

(二)掌握数据结构中逻辑结构、存储结构以及两者间关系

1.逻辑结构:
相互之间存在一种或多种特定关系的数据元素的集合。根据数据元素之间关系的不同特性,分为四类基本结构:
集合(同属一个集合)、线性结构(一对一关系)、树形结构(一对多关系)、图状结构或网状结构(多对多关系)。
2.存储结构:
(1)数据结构在计算机中的表示,又称映像称为物理结构,或存储结构。
(2)数据元素之间的关系在计算机中有两种不同的表示方法:顺序映像和非顺序映像。
对应得到两种不同的存储结构:顺序存储结构和链式存储结构。
3.逻辑结构和存储结构之间关系:
数据的逻辑结构和存储结构是密不可分的两个方面。一个算法的设计取决于所选的逻辑结构,而算法的实现依赖于所采取的存储结构。

(三)了解抽象数据类型定义和表示方法

1.数据类型分为两类:
原子类型(如int bool等)和结构类型。
2.抽象数据类型(ADT):
一个数学模型以及定义在该模型上的一组操作。
3.抽象数据类型定义格式:
ADT 抽象数据类型名 {
    数据对象:<数据对象定义>
    数据关系:<数据关系定义>
    基本操作:<基本操作定义>
}ADT 抽象数据类型名
4.本文档预定义常量和类型:
//函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#defien INFEASIBLE -1
#define OVERFLOW -2
//Status是函数的类型,其值为函数结果状态代码
typedef int Status

(四)掌握计算机语言频度和估算算法时间复杂度的方法

1.算法:
是对特定问题求解步骤的一种描述,它是指令的有限序列,其中每条指令表示一个或多个操作。
2.算法特性:
有穷性、确定性、可行性、输入、输出。
3.算法设计要求:
正确性、可读性、健壮性、效率与地存储要求。
4.算法度量:
事后统计和事前分析估算的方法
5.T(n)=O(f(n))–渐进时间复杂度,简称时间复杂度:
时间开销T与问题规模之间n的度量。
阶数大小:
O(1)<O(log2n)<O(n)<O(n*log2n)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)。
6.S(n)=O(f(n))–空间复杂度:
内存空间S与问题规模之间的关系。
算法的性能问题只有当问题规模n很大的时候才会暴露出来。

二、线性结构–线性表、栈、队列、串

(一)理解线性表的逻辑结构定义

1.线性表:
是一种最简单最常用的一种数据结构。简单地说,有n个相同类型的数据元素组成的有限序列,一个数据元素可以由若干个数据项构成。
2.栈:
是仅限定在表尾进行插入和删除的线性表。表尾称为栈顶,表头称为栈底。后进先出的特性。
3.队列
是一种先进先出的线性表,它只允许在表的一端进行插入元素,而在另一端删除元素。
4.串

(二)熟悉抽象数据类型的定义

1.线性表的抽象数据类型定义:
ADT List{
    数据对象:D={ai|ai属于ElemSet,i=1,2,3,..,n,n>=0}
    数据关系:R1={<ai-1,ai>|ai-1,ai属于D,i=1,2,3,...,n}
    基本操作:
        //课本提供的12种基本操作,带上&表示为引用类型
        InitList(&L):构造空线性表L
        DestroyList(&L):销毁线性表L
        ClearList(&L):清空线性表L
        ListEmpty(L):线性表L判空
        ListLength(L):获取线性表L长度
        GetElem(L,i,&e):获取线性表中i位置,并赋给e返回
        LocateElem(L,e,compare()):找到元素e的位序
        PriorElem(L,cur_e,&pre_e):找到cur_e的前驱元素
        NextElem(L,cur_e,&next_e):找到cur_e的后继元素
        ListInsert(&L,i,e):第i位置之前插入元素e
        ListDelete(&L,i,&e):删除第i位置元素,并赋给e返回
        ListTraverse(L,vist()):对线性表L进行遍历操作
}ADT List
2.栈的抽象数据类型定义:
ADT Stack{
    数据对象:D={ai|ai属于ElemSet,i=1,2,3,..,n,n>=0}
    数据关系:R1={<ai-1,ai>|ai-1,ai属于D,i=1,2,3,...,n}
    基本操作:
        //课本提供9种操作
        InitStack(&S):构造空栈
        DestroyStack(&S):销毁栈
        ClearStack(&S):清空栈
        StackEmpty(S):栈判空
        StackLength(S):求栈深度
        GetTop(S,&e):获取栈顶元素,并赋给e返回
        Push(&S,e):插入栈元素e
        Pop(&S,&e):弹出栈顶元素,并赋给e返回
        StackTracerse(S,vist()):遍历栈
}ADT Stack
3.队列的抽象数据类型定义:
ADT Stack{
        数据对象:D={ai|ai属于ElemSet,i=1,2,3,..,n,n>=0}
        数据关系:R1={<ai-1,ai>|ai-1,ai属于D,i=1,2,3,...,n}
        基本操作:
            //课本提供9种操作
            InitQueue(&Q):初始化队列
            DestroyQueue(&Q):销毁队列
            ClearQueue(&Q):清空队列
            QueueEmpty(Q):判空队列
            QueueLength(Q):求队列长
            GetHead(Q,&q):获取对头元素
            EnQueue(&Q,q):入队操作
            DeQueue(&Q,&q):出队操作
            QueueTraverse(Q,vist()):遍历队列
}ADT Queue
4.串的抽象数据类型定义:

(三)熟练掌握线线性结构的顺序和链式存储结构

(六)熟练掌握在顺序和链式存储结构上实现相关基本操作

1.线性表的动态分配顺序存储结构
#define LIST_INIT_SIZE 100  
#define LISTINCREMENT 10   
typedef struct {
    ElemType *elem;
    int length;
    int listsize;
}SqList

//(1)初始化线性表操作
Status InitList_Sq(SqList &L){
    L.elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType)) ;
    if(!L.elem) exit(OVERFLOAT);//存储分配失败
    L.length=0;
    Li.listsize=LIST_INIT_SIZE;//初始存储容量
    return OK ;
}

//(2)插入操作
Status ListInsert_Sq(SqList &L,int i,ElemType e){
    if(i<1||i>L.length+1) return ERROR;
    if(L.length>=L.listsize){//当前空间已满,增加分配
        newbase = (ElemType *)malloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType)) ;
        if(!newbase) exit(OVERFLOW) ;
        L.elem = newbase ;
        L.listsize+=LISTINCREMENT;
    }
    q=&(L.elem[i-1]) ;
    for(p=&(L.elem[L.length-1]);p>=q;--p) *(p+1)=*p ;//依次往后移动
    *q = e;++L.length;
    return OK;
}
//时间复杂度O(n)
//(3)删除操作
Status DeleteList_Sq(SqList &L,int i,ElemType &e){
    if(i<1||i>L.length+1) return ERROR;
    p = &(L.elem[i-1]) ;
    e=*p;
    q=L.elem+L+length-1;
    for(++p;p<=q;++q) *(p-1) = *p ;
    --L.length;
    return OK ;
}
//时间复杂度O(n)
//(4)按值查找
Status LocateElem_Sq(SqList L,ElemType e,Status(* compare)(ElemType,ElemType)){
    int i=1;
    p=L.elem ;
    while(i<L.length && !(* compare)(*p++,e)) ++i;
    if(i<L.length) return i;
    else return 0; 
}
2.线性表的静态顺序存储结构(王道数据结构)
#define MAXSIZE 100
typedef struct{
    ElemType data[MAXSIZE];
    int length;
}SqList 

(1)从第i个位置插入操作(注意与数组下表不同,对应数组下表为i-1)
bool ListInsert_Sq(SqList &L,int i,ElemType e){
    if(i<1||i>L.length+1) return false;
    if(L.length>=MXSIZE) return false;
    for(int j=L.length;j>=i;j--) L.data[j]=L.data[j-1];
    L.data[i-1]=e;
    L.length++;
    return true ;    
}
//时间复杂度:最好O(1);最坏O(n);平均O(n)

(2)从第i个位置删除操作(注意与数组下表不同,对应数组下表为i-1)
bool ListDelete_Sq(SqList &L,int i,ElemType &e){
    if(i<=1||i>L.length) return false;
    e=L.data[i-1];
    for(int j=i-1;j<L.length-1;j++) L.data[j]=L.data[j+1];
    L.length--;
    return true;
}
//时间复杂度:最好O(1);最坏O(n);平均O(n)

(3)按值查找
int LocateElem_Sq(SqList q,ElemType e){
    int i=0;
    for(int i;i<L.length;i++)
        if(L.data[i]==e)
            retuen i+1;
    return 0;
}
//时间复杂度:最好O(1);最坏O(n);平均O(n)
//****基于java实现代码为当前文件夹下的Main.java文件****
3.线性表的链式表示–单链表
typedef struct{
    ElemType data ;
    struct LNode *next;
}LNode,*LinkList
//LNode,强调这是一个节点;*LinkList,强调这是一个单链表

//(1)头插法建立单链表(王道数据结构)
LinkList List_HeadInsert(LinkList &L){
    LNode *s ;int x;
    L=(LinkList)malloc(sizeof(LNode));
    L->next = NULL;
    scanf("%d",&x) ;
    while(x!=9999){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=x;
        s->next = L->next ;
        L->next = s;
        scanf("%d",&x) ;
    }
    return L ;
}//时间复杂度为O(n)

//(2)尾插法建立单链表
LinkList List_TailInsert(LinkList &L){
    int x;
    L=(LinkList)malloc(sizeof(LNode)) ;
    LNode *s,*r=L ;
    scanf("%d",&x) ;
    while(x!=9999){
        s=(LNode *)maollc(sizeof(LNode)) ;
        s->data=x;
        r->next=s;
        r=s;
        scanf("%d",&x) ;
    }
    r->next=NULL;
    return L;
}//时间复杂度为O(n)

//(3)按序号查找节点值
LNode *GetElem(LinkList L,int i){
    LNode *p = L->next;
    int j=0;
    if(i==0) return L;
    if(i<1) return NULL;
    While(p&&j<i){
        p=p->next ;
        j++;
    }
    return p ;
}
//(4)按值查找表节点

LNode *LocateElem(LinkList L,ElemType e){
    LNode *p = L->next ;
    while(p!=NULL && p->data!=e){
        p=p->next;
    }
    return p;
}
//(5)插入节点操作
bool LinkListInsert(LinkList &L,int i;LNode *s){
    LNode *p ;
    p=GetElem(L,i-1) ;
    s->next = p->next;
    p->next=s ;
    return true ;
}//时间复杂度:查找时间复杂度为O(n),插入时间复杂度为O(1)
//(6)删除节点操作
bool LinkListDelete(LinkList &L,int i,ElemType &e){
    LNode *p ;
    p = GetElem(L,i-1) ;
    LNode *q ;
    q=p->next ;e=q->data;
    p->next=q->next ;
    free(q) ;
    return true ;
}
//(7)求表长操作
int GetLinkListLength(LinkList L){
    int i;
    LNode *p ;
    p = L->next;
    while(p!=NULL){
        p=p->next ;
        i++;
    } 
    return i ;
}//时间复杂度为O(n)
//****基于java实现代码为当前文件夹下的LinkListDemo.java文件****
4.线性表的链式表示–双链表
typedef struct{
    ElemType data ;
    struct LNode *next,*prior;
}DNode,*DLinkList
//(1)插入核心代码 在指针p所指的节点后插入节点*s
    s->next = p->next;
    p->next->prior = s ;
    p->next = s;
    s->prior = p ;
//(2)删除核心代码 删除指针p所指的节点
    p->next->prior = p->prior ;
    p->prior->next = p->next ;
    free(p)
4.线性表的链式表示–循环链表
(1)单循环链表、双循环链表表中最后一个节点的指针域指向头结点。
(2)循环条件:p或p->next等于头指针
5.栈的顺序存储结构(王道数据结构)
//定义:
#define MAXSIZE 50
typedef struct{
    ElemType data[MAXSIZE] ;
    int top;//top指针指向当前栈顶元素
}SqStack;
//(1)初始化
    void InitStack(SqStack &S){
        S.top =-1;
    }
//(2)栈判空
    bool StackEmpty(SqStack S){
        if(S.top == -1){
            return true ;
        }else{
            return false ;
        }
    }
//(3)进栈
    bool Push(SqStack &S,ElemType x){
        if(S.top==MAXSIZE-1) return false ;
        S.top++;
        S.data[S.top] = x;//top指针指向当前栈顶元素
        return true ;
    }
//(4)出栈
    bool Pop(SqStack &S,ElemType &x){
        if(S.top == -1) return false ;
        x = S.data[S.top] ;
        S.top--;
        return true ;
    }
//(5)获取栈顶元素
    bool GetTop(SqStack S,ElemType &x){
        if(S.top==-1) return false ;
        x = S.data[S.top] ;
        return true ;
    }
6.栈的顺序存储结构(数据结构课本)
//定义:
#define STACK_INIT_SIZE 100 
#define STACKINCREMENT 10
typedef struct{
    SElemType *base ;
    SElemType *top ;//top指针指向栈顶元素的下一个位置
    int stacksize ;
}SqStack ;
//(1)构造空栈
Status InitStack(SqStack &S){
    S.base = (SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType)) ;
    if(!S.base) exit(OVERFLOAT) ;
    S.top = S.base ;
    S.stacksize = STACK_INIT_SIZE;
    return OK;
}
//(2)获取栈顶元素
Status GetTop(SqStack S,SElemType &s){
    if(S.top == S.base) return ERROR ;
    s = *(S.top-1) ;//top指针指向栈顶元素的下一个位置
    return OK ;
}
//(3)入栈
Status Push(SqStack &S,SElemType &s){
    if(S.top-S.base>=S.stacksize){
        S.base = (SElemType *)malloc(S.base,(STACK_INIT_SIZE+STACKINCREMENT)*sizeof(SElemType)) ;
        if(!S.base) exit(OVERFLOAT) ;
        S.top = S.base +S.stacksize ;
        S.stacksize+=STACKINCREMENT;
    }
    S.top++;
    *(S.top) = s;
    return Ok ;
}
//(4)出栈
Status Pop(SqStack &S,SElemType &s){
    if(S.top == S.base) return ERROR ;
    s = *(S.top-1) ;
    S.top--;
    return OK ;
}
7.栈的链式存储结构(王道数据结构)–有待证明对错
//定义:
typedef struct LinkNode {
    ElemType data ;
    struct LinkNode *next ;
}*LinkStack ;
//尾插法实现,带头结点(头指针)
//(1)初始化链栈
void InitStack(LinkStack &LS){
    LS->next = NULL ;
}
//(2)入栈
void Push(LinkStack &LS,ElemType s){
    LinkNode *s,*t ;//s,新增节点;t,栈顶节点
    t = GetTop(LS) ;
    s->data = s ;
    s->next = NULL ;
    t->next = s ;
}
//(3)出栈
void Pop(LinkStack &LS,ElemType &s){
    LinkNode *t ;
    //寻找栈顶节点
    t = GetTop(LS) ;
    s = t->data ;
    //寻找倒数第二个节点p
    LinkNode *p ;
    p = LS->next ;
    while(p!=NULL && p->next != t){//p->next = t 时,跳出while
        p=p->next ;
    }
    p->next = NULL ;
    free(t) ;

}
//(4)求栈深度及数据个数
int StackLength(LinkStack LS){
    LinkNode *p ;int i = 0;
    p = LS->next ;
    while(p!=NULL){
        p=p->next ;
        i++;
    }
    return i ;
}
//(5)获取栈顶元素节点
LinkNode *GetTop(LinkStack LS){
    LinkNode *p ;
    p = LS->next ;
    while(p!=NULL){
        p=p->next ;
    }
    return p ;
}
8.队列的顺序存储结构(王道数据结构)1
//定义
#define MAXSIZE 50
typedef struct {
    ElemType data[MAXSIZE] ;
    int front , rear ;
} SqQueue ;

//(1)初始状态:Q.front = Q.rear = 0
//(2)入队操作:队不满时,先送值到队尾元素,rear指针在加1(非空时,rear指针指向队尾元素的下一个位置)
//(3)出队操作:先取出对头元素,然后front指针加1
//(4)队满和队空:队空,Q.front == Q.rear == 0;队满,不能用Q.front == MAXSIZE来判断,
     队中仅有一个元素时也可能成立,这时入队出现上溢出,并不是真正的溢出,队中仍有空位来入队,这种称为假溢出。
9.队列的顺序存储结构(王道数据结构)–循环队列1
//相关知识:
    a.循环队列出现解决顺序队列的缺点[8.(4)]
    b.初始时:Q.front = Q.rear = 0
    c.队首指针进1:Q.front = (Q.front+1)%MAXSIZE(求模保证了指针范围在MAXSZIE之内)
    d.队尾指针进1:Q.rear = (Q.rear+1)%MAXSIZE
    e.队列长度:(Q.rear-Q.front+MAXSIZE)%MAXSIZE(注意Q.rear-Q.front为负的情况)
    f.队满和队空:不做处理前,队空--Q.front == Q.rear;当入队速度快于出队速度时,队尾指针就会赶上队首指针,
    队满时也会出现--Q.front == Q.rear 
    g.区分队空和队满,3种处理方案:
        g.1牺牲一个单元来区分队空和队满,入队时少用一个队列单元
            队空条件:Q.front == Q.rear 
            队满条件:(Q.rear+1)%MAXSIZE == Q.front
            元素个数:Q.rear-Q.front+MAXSIZE)%MAXSIZE
        g.2类型中增设表示元素个数的数据成员
            队满、队空都有Q.front == Q.rear情况
            队空:Q.size ==0 
            队满:Q.size ==MAXSIZE
        g.3类型中增设tag数据成员,以区分队满还是队空。tag=0时,若因删除导致Q.front = Q.rear,则为队空;tag=1时,
        若因插入导致Q.front = Q.rear,则为队满。

//基于g.1实现的代码
//(1)初始化
void InitQueue(SqQueue &Q){
    Q.rear = Q.front =0 ;
}
//(2)判队空
bool QueueEmpty(SqQueue Q){
    if(Q.front == Q.rear) return true ;
    return false ;
}
//(3)入队
bool EnQueue(SqQueue &Q,ElemType e){
    if(Q.rear == (Q.rear+1)%MAXSIZE) return false ;
    Q.data[Q.rear] = e ;
    Q.rear = (Q.rear+1)%MAXSIZE ;
    return true ;
}
//(4)出队
bool DeQueue(SqQueue &Q,ElemType &e){
    if(Q.front == Q.rear) return false;
    e = Q.data[Q.front] ;
    Q.front = (Q.front+1)%MAXSIZE ;
    return true ;
}
10.队列的顺序存储结构(数据结构课本)–循环队列2
//定义:
#define MAXQUEUESIZE 100
typedef struct{
    ElemType *base ;//初始化的动态分配存储空间
    int front,rear;
} SqQueue ;

//基于g.1实现的代码
//(1)初始化
 Status InitQueue(SqQueue &Q){
    Q.base = (ElemType *)malloc(MAXQUEUESIZE*sizeof(ElemType)) ;
    if(!Q.base) exit(OVERFLOAT) ;
    Q.rear = Q.front =0 ;
    return OK ;
}
//(2)求队长
int QueueLength(SqQueue Q){
    return (Q.rear-Q.front+MAXQUEUESIZE)%MAXQUEUESIZE ;
}
//(3)入队
Status EnQueue(SqQueue &Q,ElemType q){
    if((Q.rear+1)%MAXQUEUESIZE == Q.front) return ERROR ;
    Q.base[Q.rear] = q ;
    Q.rear = (Q.rear+1)%MAXQUEUESIZE ;
    return OK ;
}
//(4)出队
Status DeQueue(SqQueue &Q,ElemType &q){
    if(Q.front == Q.rear) ERROR ;
    q = Q.base[Q.front] ;
    Q.front = (Q.front +1)%MAXQUEUESIZE ;
    return OK;
}
11.队列的链式存储结构(数据结构课本)1(王道数据结构也差不多)
//定义
typedef struct {
    ElemType data ;
    struct LinkNode *next ;
}LinkNode ;
typedef struct {
    LinkNode *front,*rear ;
}LinkQueue ;

//(1)初始化
void InitQueue(LinkQueue &LQ){
    LQ.front = LQ.rear = (LinkNode *)malloc(sizeof(LinkNode)) ;
    LQ.front->next = NULL;
}
//(2)判队空
bool QueueEmpty(LinkQueue LQ){
    if(LQ.front == LQ.rear) return true ;
    return false ;
}
//(3)入队
void EnQueue(LinkQueue &LQ,ElemType lq){
    LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode)) ;
    s->data = lq ;
    s->netx = NULL ;
    LQ.rear->next = s ;
    LQ.rear = s ;
}
//(4)出队
bool DeQueue(LinkQueue &LQ,ElemType &lq){
    if(LQ.front == LQ.rear) return false ;
    LinkNode *s = LQ.front->next ;
    lq = s->data;
    LQ.front->next = s->next ;
    if(LQ.rear == s) LQ.rear = LQ.front ;
    free(s) ;
    return true ;
}
12.双端队列相关知识(王道数据结构P81)(课本未提)–了解一下
(1)双端队列是指允许两端都可以进行入队和出队操作的队列。
(2)输出受限的双端队列:允许在一端进行插入和删除但在另一端只允许插入的队列。
(3)输入受限的双端队列:允许在一端进行插入和删除但在另一端只允许删除的队列。

(四)掌握线性表、栈、队列的应用,理解各种线性结构间的关系(课本)

1.线性表的应用:
(1)一元多项式的表示与相加
(2)链表逆置(真题)
2.栈的应用:
(1)数制转换
(2)括号匹配的检验
(3)行编辑程序
(4)迷宫求解
(5)表达式求值
(6)栈与递归的应用
3.队列的应用:
离散事件模拟
4.三者之间的关系:

(五)熟悉串的逻辑结构和典型存储方式,理解串的主要运算

1.串的相关概念(串的逻辑结构)
(1)串:是由零个或多个字符组成的有限序列。一般记为 s='a1a2...an' 。串长为n,n=0时为空串。
(2)两个串是相等:当且仅当,这两个串的值相等。只有当两个串的长度相等,且各个对应的位置字符都相等时才相等。
2.串的抽象数据类型定义
ADT String{
        数据对象:D={ai|ai属于CharacterSet,i=1,2,3,..,n,n>=0}
        数据关系:R1={<ai-1,ai>|ai-1,ai属于D,i=1,2,3,...,n}
        基本操作:
            //课本提供13种操作
            StrAssign(&T,chars):生成串
            StrCopy(&T,S):复制串
            StrEmpty(S):串的判空
            StrCompare(S,T):两串比较
            StrLength(S):求串长
            ClearString(&S):清空串
            Concat(&T,S1,S2):合并串
            SubString(&Sub,S,pos,len):提取子串
            index(S,T,pos):主串S中是否存在子串T
            Replace(&S,T,V):用串V替换串T
            StrInsert(&S,pos,T):在串的第pos个字符之前插入新串
            StrDelete(&S,pos,len):删除串中从第pos开始长度为len的子串
            DestroyString(&S):销毁串
}ADT String
3.串的典型存储方式
(1)定长顺序表示 :类似于线性表的顺序存储结构,用一组地址连续的存储单元存储串值得字符序列。定义如下:
    #define MAXSTRLEN 255 
    typedef unsigned SString[MAXSTRLEN+1] ;
    
    a.串连接Concat(&T,S1,S2)
        Status Concat(SString &T,SString S1,SString S2){
            if(S1[0]+S2[0]<=MAXSTRLEN){
                T[1...S1[0]] = S1[1...S1[0]] ;//注意如何理解这种表达,以及S1[0]和S2[0]表达含义
                T[S1[0]+1...S1[0]+S2[0]] = S2[1...S2[0]] ;
                T[0] = S1[0]+S2[0] ;
                uncut = TRUE ;
            }else if(S1[0]<MAXSTRLEN){
                T[1...S1[0]] = S1[1...S1[0]] ;
                T[S1[0]+1...MAXSTRLEN] = S2[1...MAXSTRLEN-S1[0]] ;
                T[0] = MAXSTRLEN ;
                uncut = FALSE ;
            }else{
                T[0..MAXSTRLEN] = S1[0..MAXSTRLEN] ;
                uncut = FALSE ;
            }
            return uncut ;
        }

    b.求子串SubString(&Sub,S,pos,len) 
        Status SubString(SString &Sub,SString S ,int pos,int len){
            if(pos<1 || pos > S[0] || len>S[0]-pos+1)
                return ERROR ;
            Sub[1..len] = S[pos..pos+len-1] ;
            Sub[0] = len ;
            return OK ;
        }

(2)堆分配存储表示:仍以一组地址连续的存储单元存放串值序列,但它们的存储空间是可以在程序中动态分配得到的。在C语言中,
存在一个堆的自由存储区,并由动态分配函数free()和malloc()来管理。定义如下:
    typedef struct{
        char *ch ;
        int length ;
    } HString ;

    //StrInsert(&S,pos,T):
    Status StrInsert(HString &S,int pos,HString T){
        if(i<1 || pos>S.length+1) return ERROR ;
        if(T.length){
            if(!(S.ch = (char *)malloc(S.ch,(S.length+T.length)*sizeof(char))))//增加分配存储空间
            exit(OVERFLOAT)
            for(i=S.length-1;i>pos-1;i--)
            S.ch[i+T.length] = A.ch[i] ;//往后移,腾出位置
            S.ch[pos-1..pos+T.length-2] = T.ch[0..T.length-1] ;//插入到腾出的空位
            S.length += T.length ;//修改长度
        }
        return OK ;
    }
    a.Status StrAssign(HString &T,char *chars) ://生成与char相等的串
        Status StrAssign(HString &T,char *chars){
            if(T.ch) free(T.ch) ;//非空时清空串
            for(i=0,c=chars;c;i++,c++)//串的下标i对应指针c的增加
            if(!i){
                T.ch = NULL ;T.length = 0 ;
                exit(OVERFLOAT) ;
            }else {
                T.ch  = (char *)malloc(i*sizeof(char));//申请存储空间
                if(!(T.ch)) exit(OVERFLOAT) ;
                T.ch[0..i-1] = chars[0..i-1] ;//一一对应添加
                T.length = i ;
            }
            return OK ;
        }
    b.int StrLength(HString S) :
        int StrLength(HString S){
            return S.length ;
        }
    c.int StrCompare(HString S,HString T):
        int StrCompare(HString S,HString T){
            for(i=0;i<S.length && i<T.length ;i++)
                if(S.ch[i]!=T.ch[i]) return S.ch[i]-T.ch[i] ;
            return S.length -T.length ;
        }
    d.Status ClearString(HString &S):
        Status ClearString(HString &S){
            if(S.ch) {free(S.ch) ; S.ch=NULL}
            S.length = 0;
            return OK ;
        }
    e.Status Concat(HString &T,HString S1,HString S2):
        Status Concat(HString &T,HString S1,HString S2){
            if(T.ch) free(T.ch) ;
            T.ch = (char *)malloc((S1.length+S2.lenth)*sizeof(char)) ;
            if(!(T.ch)) exit(OVERFLOAT) ;
            T.ch[0..S1.length-1] = S1.ch[0..S1.length-1] ;
            T.ch[S1.length..T.length-1] = S1.ch[0..S2.length-1] ;
            return OK ;
        }
    f.HString SubString(HString S,int pos,int len):
        Status SubString(HString &Sub,HString S,int pos,int len){
            if(pos<1 || pos>S.length || len<0 || len>S.length -pos+1)
                return ERROR ;
            if(Sub.ch) free(Sub.ch) ;
            if(!len) {Sub.ch =NULL ;Sub.length = 0 ;}
            else {
                Sub.ch = (char *)malloc(len*sizeof(char)) ;
                Sub.ch[0..len-1] = S.ch[pos-1..pos+len-2] ;
                Sub.length = len ;
            }
            return OK ;
        }
(3)串的块链存储表示:和线性表的链式结构相似,也可以采用链表方式存储。由于串结构的特殊性,每个节点可以存一个字符,
也可以存多个字符。当节点的大小大于1时,由于串长不一定是节点大小的整数倍,则链表的最后一个节点不一定全被串值占满,
此时补上“#”或 其他字符非串值字符。定义如下:
    #define CHUNKSIZE 80  //用户定义的块大小
    typedef struct {
        char ch[CHUNKSIZE] ;
        struct Chunk *next ;
    }Chunk
    typedef struct {
        Chunk *head,*trail ;
        int curlen ;
    }LString ;
    注:在一般情况下,对串的操作时,只需要从头到尾顺序扫描,则对串值不必建立双向链表。
    设置尾指针的目的是为了便于进行联结操作,但应注意联结时需处理第一个串尾的无效字符。
4.串的主要运算–串的模式匹配算法
子串的定位操作通常称为串的模式匹配,其中T被称为模式串。时间复杂度为O(mn)。
(1)求子串的位置的定位函数 Index(S,T,pos) 
    int Index(SString S,String T,int pos){
        i=pos ;j=1 ;
        while(i<S[0] && j<T[0]){
            if(S[i] == S[j]){i++;j++;} 
            else{
                i=i-j+2;j=1 ;
            }
            if(j>T[0]) return i-T[0] ;
            else return 0;
        }
    }

    int index(SString S,SString T){
        int i=1,j=1;
        while(i<S.length && j<T.length){
            if(S.ch[i]==T.ch[j]){
                i++;j++;
            }
            else{
                i=i-j+2 ;
            }
            if(j>T.length) return i-T.length ;
            else return 0 ;
        }
    }

(2)模式匹配的一种改进算法--KMP算法
    此算法可以在O(n+m)的时间数量级上完成串的模式匹配。
    其改进在于:没当一趟匹配过程中出现字符比较不相等时,不用回溯i的指针,
    而是利用已经得到的部分匹配的结果将模式串向右滑动一段尽可能远的距离,继续进行比较。
    a.int Index_KMP(SString S,SString T,int pos):
        int int Index_KMP(SString S,SString T,int next[]){
            int i = 1,j = 1 ;
            while(i<S.length && j<T.length){
                if(j==0 || S.ch[i]=T.ch[j]){
                    i++;j++;
                }
                else{
                    j=next[j] ;
                }
            }
            if(j>T.length) return i-T.length ;
            else return 0 ;
        }
    b.void get_next(SString T,int &next[])://求next数组
        void get_next(SString T,int &next[]){
            int i=1,j=0 ;
            next[1]=0;
            while(i<T.length){
                if(j==0 || T.ch[i]=T.ch[j]){
                    i++;j++;
                    next[i]=j ;
                }
                else{
                    j=next[j] ;
                }
            }
        }
    c.void get_nextval(SString T,int &nextval[])://求nextval数组
        //对next数组的优化
        void get_nextval(SString T,int &nextval[]){
            int i=1,j=0;
            nextval[1] = 0;
            while(i<T.length){
                if(j==0 || T.ch[i] = T.ch[j]){
                    i++;j++;
                    if(T.ch[i]!=T.ch[j]) nextval[i] = j ;
                    else nextval[i] = nextval[j] ;
                }
                else{
                    j=nextval[j] ;
                }
            }
        }
5.串的应用(课本)
(1)文本编辑
(2)建立词索引表

三、数组和广义表

(一)掌握数组的逻辑特征和存储方式

(二)掌握矩阵的压缩存储方式及其特点

(三)理解广义表的逻辑特征和存储方式

(四)掌握广义表的基本操作

1.数组(王道数据结构)
(1)数组逻辑特征
    数组是由n个相同类型的数据元素构成的有限序列,每个元素称为一个数组元素。
    数组是线性表的推广,一维数组可视为一个线性表,二维数组可视为其元素也是定长线性表的线性表。
    数组一旦被定义,其维数和维界就不在改变。因此除了结构的初始化和销毁外,数组只会有存取和修改元素的操作。
(2)数组存储方式
    按行优先顺序存放(注意数组下标是从0开始还是1开始)
    按列优先顺序存放(注意数组下标是从0开始还是1开始)
(3)矩阵压缩存储方式及其特点
    压缩存储:指多个相同的元素只分配一个存储空间,对0元素不分配存储空间。其目的是节省存储空间。
    a.对称矩阵
        对一个n阶方阵A[1..n][1..n]中的任意一个元素都有ai,j=aj,i
        n阶方阵A[1..n][1..n]存放到一维数组为B[n(n+1)/2] 
    b.三角矩阵
        下三角矩阵中,上三角区所有元素均为同一常量
        n阶方阵A[1..n][1..n]压缩存储到数组B[n(n+1)/2+1]
        上三角矩阵同理
    c.三对角矩阵
        也称带状矩阵。对方阵中的元素,当|i-j|>1时,有aij=0
        a1,1存到B[0],那么ai,j(|i-j|<=1)存到B[k],k=2i+j-3
    d.稀疏矩阵
        矩阵中非零元素的个数t,相对矩阵元素的个数s来说非常少,即s>>t
        非零元素及其相应的行列构成一个三元组(行标,列标,值)。稀疏矩阵压缩存储后就失去了随即存取的特性。
        十字链表(待)
2.广义表
(1)广义表逻辑特征
    广义表是线性表的推广,也称为列表Lists
    广义表一般记为  LS(a1,a2,a3,...,an) ,n是广义表的长度,ai在线性表中仅限单个元素,
    而在广义表中可以是广义表,分别称为广义表LS的原子和子表。习惯上,用大写字母表示子表,
    小写字母表示原子。当广义表非空时,第一个元素a1称为LS的表头,其余元素(a2,a3...,an)称为LS的表尾。
(2)广义表存储方式
    广义表的数据元素可以具有不同的结构,因此难以用顺序存储结构表示,通常采用链式存储结构,每个元素可以用一个节点表示。
    a.原子节点:(tag=0,atom)两部分;表结点:(tag=1,hp,tp)三部分。
    //广义表的头尾链表存储表示
    typedef enum {ATOM,LIST} ElemTag ;//ATOM==0:原子;LIST==1:子表
    typedef struct GLNode{
        ElemTag tag ;//公共部分,用于区分原子节点和表结点
        union{//原子节点和表结点的联结部分
            AtomType atom ;//原子节点的值域部分
            struct {
                struct GLNode *hp,*tp;
            } ptr;//ptr是表结点的指针域,ptr.hp和ptr.tp分别指向表头和表尾
        };
    }*GList ;//广义表类型
    b.原子节点:(tag=0,atom,tp)三部分;表结点:(tag=1,hp,tp)三部分。
    //广义表的扩展线性链表存储表示
    typedef enum {ATOM,LIST} ElemTag ;//ATOM==0:原子;LIST==1:子表
    typedef struct GLNode{
        ElemTag tag ;//公共部分,用于区分原子节点和表结点
        union{//原子节点和表结点的联结部分
            AtomType atom ;//原子节点的值域部分
            struct GLNode *hp;
        };
        struct GLNode *tp ; 
    }*GList ;//广义表类型
(3)广义表基本操作

四、树和二叉树

(一)熟练掌握二叉树的基本性质

二叉树的性质
    (1)二叉树在第i层上至多有 2^(i-1) 个结点
    (2)深度为k的二叉树至多有 2^k-1 个结点
    (3)对任意一棵二叉树,如果其终端结点数为n0,度为2的结点数为n2,则 n0=n2+1
    (4)具有n个结点的 完全二叉树 的深度为 logn(向下取整)+1
    (5)对一棵有n个结点的完全二叉树(深度:logn(向下取整)+1)的结点按层序编号。对任一结点i(1<=i<=n)有:
        a.i=1,i为二叉树的根;i>1,其双亲为 i/2(向下取整)
        b.如果2i>n,i无左孩子;否则其左孩子为2i
        c.2i+1>n,i无无右孩子;否则其右孩子为2i+1

(二)熟练掌握二叉树的各种存储结构的实现、各存储结构的特点及适用范围

1.二叉树的存储结构–顺序存储结构
//二叉树的顺序存储表示
#define MAX_TREE_SIZE 100
typedef TElemType SqBiTree[MAX_TREE_SIZE];//0号存储根节点
SqBiTree bt ;
(1)特点:用一组地址连续的存储单元自上而下,自左至右存储完全二叉树上的结点元素
(2)适用:仅适用于存储完全二叉树。因为在最坏情况下,一个深度为k且只有k个结点的单支树却需要长度为2^(k-1)-1的一维数组。
2.二叉树的存储结构–链式存储结构
//二叉树的二叉链表存储表示
typedef struct BiTNode {
    TElemType data;
    BiTNode *lchild,*rchild ;
}BiTNode ,*BiTree ;

//基本操作
Status CreateBiTree(BiTree &T) ;
Status PreOderTraverse(BiTree T,Status (* Vist)(TElemType e)) ;
Status InOderTraverse(BiTree T,Status (* Vist)(TElemType e)) ;
Status PostOderTraverse(BiTree T,Status (* Vist)(TElemType e)) ;
Status LevelOderTraverse(BiTree T,Status (* Vist)(TElemType e)) ;

(1)特点:二叉树结点包含三个部分(数据域、两个指针域,分别指向该节点的左右子树)。在含有n个结点的二叉链表中有
        n+1个空链域
(2)适用:所有二叉树

(三)熟练掌握二叉树各种遍历策略的递归算法

1.遍历策略–先序遍历(先根遍历–根左右)
(1)王道数据结构
    void PreOder(BiTree T){
        if(T!=NULL){
            vist(T);
            PreOder(T->lchild) ;
            PreOder(T->rchild) ;
        }
    }
(2)课本
    Status PreOderTraverse(BiTree T,Status (* Vist)(TElemType e)){
        if(T){
            if(Vist(T->data))
                if(PreOderTraverse(T->lchild,Vist))
                    if(PreOderTraverse(T->rchild,Vist))
                    return OK ;
            return ERROR ;
        }else{
            return OK ;
        }
    }
2.遍历策略–中序遍历(中根遍历–左根右)
(1)王道数据结构
    void InOder(BiTree T){
        if(T!=NULL){
            InOder(T->lchild) ;
            vist(T);
            InOder(T->rchild) ;
        }
    }
(2)课本(无)
3.遍历策略–后序遍历(后根遍历–左右根)
(1)王道数据结构
    void PostOder(BiTree T){
        if(T!=NULL){
            PostOder(T->lchild) ;
            PostOder(T->rchild) ;
            vist(T) ;
        }
    }
(2)课本(无)
4.遍历策略–层序遍历
(1)王道数据结构
    void LevelOder(BiTree T){
        initQueue(Q);//初始化辅助队列
        BiTree p ;
        EnQueue(Q,T);//根节点入队
        while(!IsEmpty(Q)){
            DeQueue(Q,p) ;//头结点出队
            vist(p) ;//访问出队的头结点
            if(p->lchild!=NULL){
                EnQueue(Q,p->lchild) ;
            }
            if(p->rchild!=NULL){
                EnQueue(Q,p->rchild) ;
            }
        }
    }
(2)课本(无)

(四)熟练掌握基于遍历策略的二叉树操作及其应用

1.前缀、中缀、后缀表达式
分别由先序遍历、中序遍历、后序遍历所得
2.线索二叉树
规定:若结点有左子树,其lchild指向其左孩子,若无则指向其前驱;
若结点有右子树,其rchild指向其右孩子,若无则指向其后继。也增加了两个标志域LTag和RTag。
(1)LTag=0:lchild域指向结点的左孩子
       =1:lchild域指向结点的前驱
(2)RTag=0:rchild域指向结点的右孩子
       =1:rchild域指向结点的后继
(3)二叉树的线索存储表示
    #typedef enum PointetTag {Link,Tread} ;
    typedef struct BiThrNode {
        TElemType data ;
        Struct BiThrNode *lchild ,*rchild ;
        PionterTag LTag,RTag;
    }BiThrNode,*BiThrTree ;
(4)相关操作

(五)树(森林)与二叉树的关系(存储)

1.树的三种存储结构
(1)双亲表示法
(2)孩子表示法
(3)孩子兄弟表示法(常用)
2.关系(暂时不做赘述)

(六)了解最优树的特性,掌握建立最优树和哈夫曼编码的方法

1.最优树的特性
最优树也就是哈夫曼树。是一类带权路径最短的树。
2.建立最优树
核心步骤:从集合中选取两棵根节点权值最小的树
构造一棵新的二叉树-->删除原来这两棵树-->将新的二叉树加入原集合中-->重复上几步操作-->直到集合中只剩一棵树即哈夫曼树
3.建立哈夫曼编码–压缩存储
设计长度不等的编码,则必须是任意一个字符的编码都不能是另一个字符的编码的前缀,这种编码称为前缀编码。
设计原则:根据最优树设计,左分支为0,右分支为1。

五、图

(一)掌握图的定义及其他基本概念

1.图的概念
是一种较线性表和树更为复杂的数据结构。图像结构中,结点之间的关系可以是任意的,任意两个数据元素之间都可能相关。
2.其他概念(n个结点,e条边)
(1)<v,w>:表示一条弧;v,弧头,w,弧尾
(2)完全图:有0.5*n(n-1)条边的无向图。无向图边的取值范围0~0.5*n(n-1)
(3)有向完全图:有n(n-1)条边的有向图。有向图边的取值范围0~n(n-1)
(4)网:带权值的图
(5)简单路径:序列中不重复出现的路径
(6)连通:无向图G中,从顶点v到顶点v'有路径,则称该两点是连通的
(7)连通图:无向图中,任意两个顶点都是连通的
(8)连通分量:无向图中的极大连通子图
(9)强连通图:有向图中,对于每一对vi和vj , vi到vj 和 vj到vi 都存在路径
(10)强连通分量:有向图中的极大连通子图

(二)掌握图的存储结构–邻接矩阵和邻接表

1.邻接矩阵(数组表示法)
用一个一维数组存储图中顶点的信息,用一个二维数组存储图中边的信息,即各顶点间的关系
(1)图的邻接矩阵存储结构的定义
    #define MaxVertexNum 100 //最大顶点书目
    typedef char VertexType ;//顶点数据类型
    typedef int EdgeType ;//带权图的边上权值数据类型
    typedef struc {
        VertexType Vex[MaxVertexNum] ;//顶点表
        EdgeType Edge[MaxVertexNum][MaxVertexNum] ;//邻接矩阵,边表
        int vexnum,arcnum ;//当前顶点数和弧数
    }
(2)图的邻接矩阵存储结构表示有以下特点
    a.无向图的邻接矩阵一定是对称矩阵且唯一,在实际存储中,可采用上三角或下三角矩阵
    b.无向图的邻接矩阵中第i行或第i列非零元素的个数正好是该顶点的度
    c.有向图的邻接矩阵中第i行的非零元素或非∞元素的个数正好是该顶点的出度,
    第i列的非零元素或非∞元素的个数正好是该顶点的入度
    d.使用邻接矩阵存储图,很容易确定两个顶点之间是否有边,但是要确定图中有多少边,时间花费很大
    e.稠密图适合使用邻接矩阵的存储表示
2.邻接表
(1)邻接表结构
    顶点表:顶点域(data)、边表头指针(firstarc)
    边表结点:邻接点域(adjvex)、指针域(nextarc)
(2)图的邻接表存储结构定义
    #define MaxVertexNum 100 
    typedef struct ArcNode{//边表结点
        int adjvex ;//该弧指向的位置
        struct ArcNode *next ;
    }ArcNode ;
    typedef struct VNode{//定点表结点
        VertexType data ;
        ArcNode *first ;
    }VNode , AdjList[MaxVertexNum] ;
    typedef stuct {
        AdjList vertices ;//邻接表
        int vexnum,arcnum ;//当前顶点数和弧数
    }ALGraph ;
(3)图的邻接表存储结构表示有以下特点
    a.若G为无向图,所需存储空间为O(|V|+2|E|); 若为有向图,所需为O(|V|+|E|)
    b.邻接表适合存储稀疏图,大大节省了存储空间
    c.在邻接表中很容易找到给定顶点的所有邻边,只需读取该顶点的邻接表。在邻接矩阵中相同的操作,扫描一行花费的时间为O(n)。
    若要判断两个顶点间是否存在边,在邻接矩阵中可以立刻查到,而在邻接表中需要查询相应的邻边,效率较低
    d.出度计算该顶点的邻接表的结点个数;入度需要遍历全部邻接表;采用逆邻接表可以加速求解入度
    e.图的邻接表表示并不唯一,取决于建立邻接表的算法及边的输入次序
3.十字链表(考纲不要求)
4.多重邻接表(考纲不要求)

(三)掌握图的遍历算法–深度优先搜索和广度优先搜索

1.深度优先搜索–DFS
(1)算法递归代码(王道)
    bool visited[MAX_VERTEX_NUM] ;//访问标记数组
    void DFSTraverse(Graph G){
        for(v=0;v<G.vexnum;v++) 
            visited[v] = false ;
        for(v=0;v<G.vexnum;v++){
            if(!visited[v]) DFS(G,v) ;
        }
    }
    void DFS(Graph G,int v){
        visit(v) ;
        visited[v] = false ;
        for(w=FrstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
            if(!visited[w]){//w是v尚未访问的结点
                DFS(G,w) ;
            }
        }
    }
(2)算法分析
    DFS是一个递归算法,需要借助一个工作栈,空间复杂度为O(|V|) 。
    邻接矩阵表示时,查询每个顶点的邻接点所需时间为O(|V|),总时间复杂度为O(|V^2|)  。
    邻接表表示时,查询所有邻接点所需时间为O(|E|),访问顶点时间为O(|V|)总时间复杂度为O(|V|+|E|) 。
2.广度优先搜索–BFS
类似于二叉树的层序遍历,使用到辅助队列。图的若边的输入次序不同,邻接矩阵是唯一的,但对于邻接表来说生成的邻接表也不同。
对于同样一个图,基于邻接矩阵的遍历得到的DFS和BFS序列是一样的,基于邻接表的遍历所得到的DFS序列和BFS序列是不唯一的。
(1)算法代码(王道)
    bool visited[MAX_VERTEX_NUM] ;//访问标记数组
    void BFSTraverse(Graph G){
        for(i=0;i<G.vexnum;i++) visited[i] = false ;
        InitQueue(Q) ;
        for(i=0;i<G.vexnum;i++){
            if(!visited[i]) BFS(G,i) ;
        }
    }
    void BFS(Graph G,int v){
        visit(v) ;
        visited[v] = false ;
        EnQueue(Q,v) ;
        while(!isEmpty(Q)){
            DeQueue(Q) ;
            for(w=FrstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
                if(!visited[w]){
                    visit[w] ;
                    visited[w]=true ;
                    EnQueue(Q,w) ;
                }
            }
        }
    }
(2)算法分析
    无论是邻接表还是了邻接矩阵的存储方式,BFS算法都要借助一个辅助队列,n个顶点均需入队一次,最坏情况下空间复杂度为O(|V|)。
    采用邻接表存储时,每个顶点均需搜索一次(入队一次),时间复杂度为O(|V|),
    在搜索任一顶点的邻接点时,每条边至少访问一次,时间复杂度为O(|E|),总时间复杂度为O(|V|+|E|)。
    采用邻接矩阵存储时,查找每个顶点的邻接点所需时间O(|V|),总时间复杂度为O(|V^2|) 。

(四)掌握最小生成树的方法

1.概念性质
最小生成树不是唯一的,树形不唯一
最小生成树的边的权值之和总是唯一的
最小生成树的边树等于顶点数减一
2.方法
(1)Prim算法(普里姆算法)
    "点--边---点":选择权值最小边及其邻接点
(2)Kruskal算法(克鲁斯卡尔算法)
    "边--边":按权值递增次序选择合适的边构成

(五)掌握图最短路径算法

1.最短路径算法–Dijkstra(迪杰斯特拉)
求单源最短路径。设置一个集合S记录已求得的最短路径的顶点,初始时把源点v0放入S,
集合S每并入一个新顶点vi,都要修改源点v0到集合V-S中dd当前的最短路径长度值。
构造过程中需要设置两个辅助数组:
dist[]:记录从源点v0到其他各顶点当前最短路径长度。若v0到vi有弧,dist[i]记录为弧上的权值。否则置为∞
path[]:path[i]表示从源点到顶点i之间的最短路径的前驱结点。在算法结束时,可根据其值追溯得到源点到顶点vi之间的最短路径。
算法步骤:...
2.最短路径算法–Floyd(弗洛伊德)
求每对顶点间最短路径

(六)了解拓扑排序概念,了解关键路径算法

1.拓扑排序
2.关键路径算法

六、查找(检索)

(一)掌握静态查找表–顺序表、有序表、索引表的查找算法;理解算法复杂性的分析过程;熟悉算法特点

1.顺序表
(1)算法
    typedef stuct {
        ElemType *elem;
        int length ;
    }SSTable ;
    int Search_Table(SSTable ST,ElemType key){
        //引入哨兵
        ST.elem[0] = key ;
        for(i=ST.length;ST.elem[i]!=key;i--)
        return i ;
    }
(2)特点
    a.引入哨兵后,循环判定时不用判断数组是否越界,当i==0,一定会跳出循环
    b.对于有n个元素的表,当每个元素查找概率相等时Pi=1/n,有
        ASL(成功) = (i=1~n)∑Pi(n-i+1) = (n+1)/2 
        ASL(失败) = n+1
    c.当顺序表的n比较大时,平均查找长度较大,效率低;
    优点是对数据元素存储没有要求,顺序存储或链式存储都可。对表中的记录有序性也没有要求。
    对线性的链表只能进行顺序查找
2.有序表(折半查找)
(1)算法
    int Binary_Search(SqList L,ElemType key){
        int low =0 ,high = L.length ,mid ;
        while(low<high){
            mid = (low+high)/2 ;
            if(L.elem[mid]==key) return mid;
            else if(L.elem[mid]>key) high = mid-1 ;
            else low = mid+1
        }
        return -1;
    }
(2)特点
    a.折半查找过程中会有判定树。用折半查找法查找到给定值的比较次数最多不会超过树的高度,
    在等概率查找时,查找成功的平均查找长度为
        ASL(成功) = (1/n)*(i=1~n)∑li 
        = (1/n)*(1*1+2*2+...+h*2^(h-1)) = (n+1)/n*log(n+1)~~log(n+1)-1
    n个元素,树的高度为h=(向上取整)[log(n+1)-1],所以折半查找的时间复杂度为O(logn)。
    b.因为折半查找需要方便的定位查找区域,要求线性表有随即存取的特性。因此该方法只适用于顺序存储结构,不适合链式存储结构。
3.索引表
(1)算法--索引查找(分块查找)
    //暂无
(2)特点
    a.吸取了顺序查找和折半查找各自的优点,既有动态结构又适于快速查找
    b.思想:将查找表分为若干子块。块内元素可以无序,块间元素有序,第一块的记录的最大关键字小于第二块中所有记录的关键字,
    第二块的记录的最大关键字小于第三块中所有记录的关键字,以此类推。
    再建立一个索引表,索引表中的每个元素含有各块的最大关键字和各块中的第一个元素的地址,索引表按关键字有序排列。
    c.步骤:一、查索引表(可以是顺序查找也可以是折半查找);二、块内顺序查找。
    d.平均查找长度:ASL = Ll + Ls 
        查找表均匀分为b块,每块中有s个记录(索引表和块内均采用顺序查找时)
        ASL = Ll + Ls =(b+1)/2+(s+1)/2 = (s^2+2s+n)/(2s)
        若n=s^2时,ASL最小为 n^(1/2)+1 
    e.若对索引表采取折半查找时 ASL = (向上取整)log(b+1)+(s+1)/2

(二)掌握动态查找表–二叉排序树和平衡二叉树的概念、基本操作及其实现

1.二叉排序树
(1)概念
    二叉排序树也叫二叉查找树,非空二叉排序树具有以下特性:
        若左子树非空,左子树的所有结点的值均小于根节点的值
        若右子树非空,右子树的所有结点的值均大于根节点的值
        左右子树也分别是一棵二叉排序树
    对二叉排序树进行中序遍历得到一个递增的有序序列
(2)基本操作及其实现
    a.查找算法非递归算法(王道)
        BSTNode *BST_Serach(BiTree T,ElemType key){
            while(T!=NULL && key!=T->data){
                if(key<T->data) T = T->lchild ;
                else T = T->rchild ;
            }
            return T ; 
        }
    b.查找算法递归算法(课本)
        BiTree BST_Search(BiTree T,ElemType key){
            if(T!=NULL || key==T->data) return(T) ;
            else if(key<T->data) return (BST_Search(T->lchild,key)) ;
            else return(BST_Search(T->rchild,key)) ;
        }
    c.插入算法(王道)
    int BST_Insert(BiTree &T,KeyType k){
        if(T==NULL){
            T = (BiTree)malloc(sizeof(BSTNode)) ;
            T->data = k ;
            T->lchild = T->rchild = NULL ;
            return 1 ;
        }
        else if(k==T->data) return 0 ;
        else if(k<T->data) BST_Insert(T->lchild,k) ;
        else return BST_Insert(T->lchild,k) ;
    }
    d.构造算法(王道)
    void Create_BST(BiTree &T,KeyType str[],int n){
        T = NULL ;
        int i = 0;
        while(i<n){
            BST_insert(T,str[i]) ;
            i++ ;
        }
    }
    e.算法分析
        二叉排序树的查找效率,主要取决于树的高度。若左右子树的高度差绝对值不超过1,又称为平衡二叉树。
        它的平均查找长度为O(logn)。若二叉排序树是单支树,则其平均查找长度为O(n)。
        从查找过程来看,二叉排序树和二分查找相似。
        就平均时间性能而言,两者差不多。
        但二分查找的判定树唯一,二叉排序树的查找不唯一,相同关键字的其插入顺序不同可能生产的二叉排序树也不一样。
2.平衡二叉树
(1)概念
    为避免树的高度增长过快,降低二叉排序树的性能,规定在插入和删除二叉树结点时,
    保证任意结点的左右子树的高度差绝对值不超过1,将这样的二叉树称为平衡树。
    定义该高度差为平衡因子,可为-1、0、1。每当插入一个结点时导致不平衡,
    则先找到插入路径上离插入节点最近的平衡因子的绝对值大于1的节结点A,
    再以A为根的子树,在保持二叉排序树的特性下,调整各结点的位置关系,使重新达到平衡。有四种操作。
(2)插入基本操作及其实现
    a.LL平衡旋转(右单旋转)
    b.RR平衡旋转(左单旋转)
    c.LR平衡旋转(先左后右双旋转)
    d.RL平衡旋转(先右后左双旋转)
    //实现代码
(3)删除基本操作及其实现
    a.用二叉排序树的方法对结点w执行删除
    b.从结点w开始,向上回溯,找到第一个不平衡结点z(即最小不平衡子树);y为结点z的高度最高的孩子结点;
    x是节点y的高度最高的孩子结点
    c.然后对以z为根的子树进行平衡调整,其中xyz可能位置有四种情况

(三)理解B?树的概念和特点

1.B树概念和特点
(1)概念:又多路平衡查找树,B树中所有结点的孩子个数的最大值称为B树的阶,通常用m表示。
(2)特点:树中每个结点2至多有m棵树,至多含有m-1个关键字
        若根节点不是终端结点,则至少有两棵子树
        除根节点以外的所有非叶子结点至少有 t=(向上取整)m/2,至少含有t-1个关键字
        所有的叶子结点都出现在同一层上,且不带信息
        结点中关键字的个数n:  t-1<=n<=m-1
2.B+树概念和特点
(1)概念:是应数据库所需要而出现的一种B树的变形
(2)特点:
    a.m阶B+树满足以下条件
        每个分支节点最多有m棵子树(孩子结点)
        非叶根节点至少有两棵子树,其他每个分支节点至少有(向上取整)m/2棵子树
        节点子树个数和关键字个数相等
        所有结点包含全部关键字及其指向相应记录的指针,叶结点中将关键字按大小顺序排列,
        并且相邻叶结点按大小顺序相互链接起来
        所有分支结点中仅包含它各个子节点中关键字的最大值及其指向其子节点的指针
    b.与B树的主要差异
        在B+树中,n个关键字的结点含有n棵子树;
        在B树中n个关键字的结点含有n-1棵子树。
        在B+树中,每个结点(非根内部结点)的关键字的个数n的范围为(向上取整)m/2<=n<=m(根节点:2<=n<m);
        在B树中每个结点(非根内部结点)的关键字的个数n的范围为(向上取整)m/2-1<=n<=m-1(根节点:1<=n<m-1)。
        在B+树中,非叶结点起到索引作用,叶结点包含信息,
        非叶结点的每个索引项只含有对应子树的最大关键字和指向该子树的指针,并没有该关键字对应记录的地址。
        在B+树中,叶子结点包含了所有关键字,即非叶子结点出现的关键字也会出现在叶子结点中;
        而在B树中,叶结点包含的关键字和其他非叶子结点包含的关键字是不重复的
    B+树和B树的查找、删除操作基本类似。
    只是在查找过程中,非叶节点上的关键字等于给定时并不终止,而是继续向下查找,
    直到叶结点上的该关键字为止。所以在B+树查找时,无论成功与否,每次查找都是一条从根到叶结点的路径。
1.B-树概念和特点??????

(四)熟练掌握哈希查找思想、哈希冲突解决方法、哈希查找性能

1.哈希查找思想
(1)散列函数(哈希函数):
一个把查找表中的关键字映射成关键字对应的地址函数,记为Hash(Key)=Addr(这里的地址可以是数组下标,索引,内存地址)。
(2)散列表:
根据关键字而直接进行访问的数据结构。也就是说,散列表建立了关键字和存储地址之间的一种直接映射关系。
理想情况下,查找时间复杂度为O(1)。
(3)常用散列函数:直接定址法、除留余数法(最常用)、数字分析法、平方取中法、折叠法、随机数法。
2.哈希冲突解决方法
(1)开放定址法--3种
    Hi = (H(key)+di) MOD m   i=1,2,3...k(k<=m-1)
    a.di=1,2,3,...,m-1  称为线性探测再散列
    b.di=1^2,-1^2,2^2,-2^2,...,+-k^2   称为二次探测再散列
    c.di=伪随机数序列,称为伪随机探测再散列
(2)再哈希法:这种方法不容易产生聚集,但增加计算的时间。
(3)链地址法:将所有关键字为同义词的记录存储在同一线性链表中。
假设某哈希函数地址在区间[0..m-1],则设立一个指针型向量 Chain ChainHash[m]
    其每个分量的初始状态都是 空指针。凡是哈希地址为i的记录都插入头指针为ChainHash[i]的链表中。
    插入位置可以是头或者尾;也可以是中间,以保持同义词在同一线性表中安关键字有序。
(4)建立一个公共溢出区:由HashTable[0..m-1] 和 OverTable[0..v]。
3.哈希查找性能
查找过程中需和给定值进行比较的关键字的个数取决于三个因素:哈希函数、处理冲突的方法、哈希表的填装因子
填装因子:α = 表中填入的记录数 / 哈希表的长度

七、排序

(一)掌握直接插入排序、希尔排序、冒泡排序、简单选择排序的思想及实现方法

(二)掌握快速排序、堆排序、归并排序的思想及实现方法

(三)掌握算法复杂度及其分析方法:熟悉算法特性及其适用场景

1.排序算法分类:按排序过程中涉及的存储器不同,可分为 内部排序 和 外部排序
(1)插入排序
    直接插入排序(顺序法)
    折半插入排序(二分法)(802考纲不要求)
    希尔排序(缩小增量排序)
(2)交换排序
    冒泡排序
    快速排序
(3)选择排序
    简单选择排序
    堆排序
(4)归并排序
(5)基数排序(802考纲不要求)
(6)外部排序(802考纲不要求)
2.各种内部排序方法比较:
类别        排序方法                    时间复杂度          空间复杂度      稳定性
                          最好          最坏        平均
插入排序     直接插入排序    O(n)       O(n^2)      O(n^2)       O(1)       稳定
            折半插入排序                           O(n^2)       O(1)       稳定
            希尔排序        O(n)        O(n^2)      ~O(n^1.3)   O(1)       不稳定
交换排序     冒泡排序        O(n)       O(n^2)      O(n^2)       O(1)       稳定
            快速排序        O(nlogn)    O(n^2)      O(nlogn)    O(nlogn)   不稳定
选择排序     简单选择排序    O(n^2)     O(n^2)      O(n^2)       O(1)       不稳定
            堆排序          O(nlogn)    O(nlogn)    O(nlogn)    O(1)       不稳定
归并排序     归并排序        O(nlogn)   O(nlogn)    O(nlogn)     O(n)       不稳定
基数排序    k:关键字个数,m:
          关键取值范围个数   O(n+m)     O(k*(n+m))  O(k*(n+m))   O(n+m)     稳定
3.排序算法思想及其实现–算法特性及其适用场景
//大多数算法数据类型如下:
    #define MAXSIZE 20 
    typedef int KeyType ;
    typedef struct {
        KeyType key ;
        InfoType info ;
    }RedType ;
    typedef struct {
        RedType red[MAXSIZE+1] ;
        int length ;
    }SqList ;

(1)直接插入排序(Straight Insertion Sort)
    a.算法思想:
        将以一个待排记录插入到一个已排好序的有序表中,得到一个新的记录数加1的有序表。
    b.算法实现:
    void InsertSort(SqList &L){//带哨兵
        int i,j ;
        for(i=2;i<=L.length;++i){
            if(L.red[i].key<L.red[i+1].key){
                L.red[0] = L.red[i] ;
                for(j=i-1;L.red[0].key<L.red[j].key;--j){
                    L.red[j+1] = L.red[j];
                }
                L.red[j+1] = L.red[0] ;
            }
        }
    }
    //***基于java实现的算法为InsertSort.java文件***
    c.算法分析:
        当待排记录正序时,所需进行关键字比较的次数达到最小值n-1,无需移动;
        当待排记录逆序时,所需进行关键字比较的次数达到最大值(n+2)(n+1)/2,记录移动次数也达最大值(n+4)(n-1)/2 。
        若待排记录是随机的,即待排记录可能出现各种排列的概率相同,则我们可取上诉最大值和最小值的平均值
        ,作为直接插入排序所需的进行关键字的比较次数和移动记录的次数,约为n^2/4。时间复杂度O(n^2)。
        空间复杂度为O(1)。是稳定算法。
        适用性:顺序存储和链式存储的线性表。
(2)希尔排序(Shell's Sort)
    a.算法思想:
        对直接插入排序的改动--先将整个待排记录分割成若干个子序列分别进行直接插入排序,
        待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。
    b.算法实现:
        void Shellinsert(SqList &L,int dk){
            for(i=dk+1;i<=L.length;i++){
                if(L.red[i].key<L.red[i-dk].key){
                    L.red[0] = L.red[i] ;
                    for(j=i-dk;j>0 && L.red[0].key<L.red[j].key;j-=dk){
                        L.red[j+dk] = L.red[j] ;
                    } 
                    L.red[j+dk] = L.red[0] ;
                }
            }
        }
        void Shell(SqList &L,int dlta[],int t){
            for(k=0;k<t;k++){
                ShellInsert(L,dlat[k])
            }
        }//***基于java实现的算法为ShellSort.java文件***
    c.算法分析:
        时间是所选取的增量序列的函数。
        当n在某个特定范围内,希尔排序所需比较和移动的次数约为n^1.3,当n->无穷大时,可以减少到n(logn)^2[2]。
        最坏情况下,时间复杂度为O(n^2)。空间复杂度为O(1)。增量序列中的值应为质数,最后一个增量值必须是1。是不稳定算法。
        适用性:仅适应线性表为顺序存储的情况下。
(3)冒泡排序(Bubble Sort)
    a.算法思想:
        每一趟只能确定将一个记录归位,即第一趟只能确定将末位上的记录归位,第二趟只能将倒数第二位的记录归位,以此类推。
        对n个记录,只需将n-1个记录归位,也就是执行n-1趟。而每一趟需要从第一位开始进行相邻的两个记录比较
        ,将较大的记录放后面,比较完成之后向后挪一位继续比较下面相邻的两个记录的关系,
        重复此步骤,直到最后一个还没归位的记录。(升序例子)
    b.算法实现:
    //void BubbleSort(SqList &L){
        int i;int j;int ;RedType =tempData ;
        for(i=1;i<L.length;i++){
            for(j=0;j<L.length-i;j++){//length = 10
                if(L.red[j]>L.red[j+1]){    
                    //i=1时,j到8,j+1到9,8<10-1,确定了L.red[9]的值
                    //i=2时,j到7,j+1到8,7<10-2,确定了L.red[8]的值
                    //i=3时,j到6,j+1到7,6<10-3,确定了L.red[7]的值
                    //......
                    //i=8时,j到1,j+1到2,1<10-8,确定了L.red[2]的值
                    //i=9时,j到0,j+1到1,0<10-9,确定了L.red[1]的值
                    tempData = L.red[j];L.red[j]=L.red[j+1];L.red[j+1]=tempData ;
                }
            }
        }
    }
    //***基于java实现的算法为BubbleSort.java文件***
    c.算法分析:
        待排记录正序时,排序过程中进行n-1次比较,不移动记录;
        逆序时,需要n-1趟排序,要进行n(n-1)/2次比较,并做等数量级的移动记录。时间复杂度最好O(n),最坏O(n^2)。
        空间复杂度O(1)。算法是稳定的。
        适用性:链表和顺序表都适用。

(4)快速排序(Quick Sort)
    a.算法思想:
        快速排序是对冒泡排序的一种改进。通过第一趟将待排记录分割成独立的两部分
        ,其中一部分记录的关键字小比另一部分的关键字小,则可分别对这两部分记录继续继续排序,以达到整个序列有序。
    b.算法实现:
        int Partition(SqList &L,int low,int high){
            RedType pivotkey  = L.red[low] ;
            while(low<high){
                while(low<high && L.red[high]>=pivotkey)
                --high;
                L.red[low]=L.red[high] ;//比枢轴小的值交换到低端
                while(low<high && L.red[low]<=pivotkey)
                ++low;
                L.red[low]=L.red[high] ;//比枢轴大的值交换到低端
            }
            L.red[low] = pivotkey ;
            return low ;//返回枢轴位置
        }
        void QSort(SqList &L,int low,int high){
            if(low<high){
                pivotloc = Partition(L,low,high);
                QSort(L,low,pivotloc-1) ;
                QSort(L,pivotloc+1,high) ;
            }
        }
        //***基于java实现的算法为QuickSort.java文件***
    c.算法分析:
        空间复杂度为最好情况下O(logn),最坏情况下O(n),平均 为O(logn)。快速排序是内部排序中平均性能最优的算法。
        时间复杂度为O(nlogn)。若待排记录按关键字有序或基本有序时,快速排序退化成冒泡排序,其时间复杂度为O(n^2)。
        算法是不稳定的。
(5)简单选择排序(Simple Selection Sort)
    a.算法思想:
        每一趟在n-i+1 个记录中选取关键字最小的记录作为有序序列中第i个记录。
    b.算法实现:
        void SimpleSelectSort(SqList &L){
            for(int i=1 ;i<L.length;i++){
                int n = i-1 ;RedType tempData;
                while(n<L.length){
                    if(L.red[n]<tempData){
                        tempData = L.red[n] ;
                        L.red[n] = L.red[i-1] ;
                        L.red[i-1] = tempData ;
                    }
                    n++;
            }
        }
        //***基于java实现的算法为SinmpleSelectForSort.java文件***
    c.算法分析:
        空间复杂度O(1)。元素移动的次数比较少,不会超过3(n-1)次,最好情况是移动0次,对应的表是有序转态。
        但元素间比较次数与序列的初始状态无关,始终是n(n-1)/2,因此时间复杂度始终是O(n^2)。算法是不稳定的。
        适用性:链表和顺序表都适用。
(6)堆排序(Heap Sort)
    a.算法思想:
        只需要一个记录大小的辅助空间,每个待排记录仅占一个存储空间。
        小根堆:ki<=k2i ;ki<=k2i+1 根节点记录最小
        大根堆:ki>=k2i ;ki>=k2i+1 根节点记录最大
    b.算法实现:
    //采用顺序表实现
    typedef SqList HeapType ;
    //调整函数
    void HeapAdjust(HeapType &H ,int s,int m){
        rc = H.red[s] ;
        for(j=2*s;j<=m;j*=2){
            if(m<m && H.red[j].key<H.red[j+1].key) ++j ;
            if(!(H.red[j].key<H.red[j+1].key))  break ;
            H.red[s] = H.red[j] ;s = j ;
        }
        H.red[s]=rc;
    }
    //排序算法
    void HeapSort(HeapType &H){
        for(i=H.length/2;i>0;i--){
            HeapAdjust(H,i,H.length) ;
        }
        for(i=H.length;i>1;i--){
            H.red[1]<-->H.red[i] ;//将堆记录和当前未经排序子序列H.red[1..i]中最后一个记录相互交换
            HeapAdjust(H,1,i-1) ;
        }
    }
    c.算法分析:
        空间复杂度为O(1)。建立堆的时间为O(n),之后有n-1次向下调整的操作,
        每次调整时间为O(h)--在最好最坏平均情况下,堆排序时间复杂度为O(nlogn)。算法是不稳定的。
(7)归并排序(Merging Sort)
    a.算法思想:
        将两个或两个以上的有序表组成一个新的有序表(如 2路归并)。
    b.算法实现: 
    void Merge(RcdType SR[],RcdType &TR[],int i,int m,int n){
        for(j=m+1,k=i;i<=m && j<=n;k++){
            if(SR[i].key<SR[j].key) TR[k] = SR[i++] ;//大于小于有待考证
            else TR[k] = SR[j++] ;
        }
        if(j<=m) TR[k..n] = SR[i..m] ;//复制操作
        if(j<=n) TR[k..n] = SR[j..n] ;
    }

    void MSort(RcdType SR[],RcdType &TR1[],int s,int t){
        if(s==t) TR1[s] = SR[s] ;
        else{
            m=(s+t)/2 ;
            MSort(SR,TR2,s,m) ;
            MSort(SR,TR2,m+1,t) ;
            MergeSort(TR2,TR1,s,m,t) ;
        }
    }
    void MergeSort(SqList &L){
        MSort(L.red,L.red,l,L.length) ;
    }
    c.算法分析:
        辅助空间刚好为n个单元,空间复杂度O(n)。时间效率,每趟归并的时间复杂度为O(n),
        共需logn(向上取整),所以算法时间复杂度为O(nlogn)。算法是稳定的。
(8)基数排序(802考纲不要求)
(9)外部排序(802考纲不要求)

八、文件

(一)了解与文件有关的基本概念

1.文件:
是由大量性质相同的记录组成的集合。
(1)按记录的类型不同分为两类:操作系统的文件和数据库文件。
(2)按文件记录的另一特性分为定长记录文件和不定长记录文件。
(3)数据库文件按记录的关键字分为单关键字和多关键字
注:习惯上称存储在主存储器(内存储器)上的记录集合为表,而存储在二级存储器(外存储器)上的记录集合为文件。
2.操作系统的文件:
仅是一维的连续的字符序列,无解释,无结构。它也是记录的集合,这个记录仅是一个字符组,用户为了存取、加工方便,
把文件中的信息划分成若干组,每一组信息称为一个逻辑记录,且可按顺序编号。
3.数据库文件:
带有结构的记录的集合。这类记录是由一个或多个数据项构成的集合,它是文件中可存取的数据的基本单位。
4.定长记录文件:
文件中每个记录含有的信息长度相同,称这类记录为定长记录,定长记录文件有这类定长记录组成。
5.不定长记录文件:
文件中含有信息长度不等的不定长记录,称之为不定长记录文件。
6.单关键字文件:
文件中记录只有唯一标识记录的主关键字。
7.多关键字文件:
文件中的记录除了含有主关键字外,还有若干个次关键字。
8.记录的逻辑结构:
指记录在用户或应用程序员面前呈现的方式,是用户对数据的表示和存取方式。
9.记录的物理结构:
数据在物理存储器上存储的方式,是数据的物理表示和组织。
10.物理记录和逻辑记录之间关系:
一个物理记录存放一个逻辑记录。
一个物理记录包含多个逻辑记录。
多个物理记录表示一个逻辑记录。
11.文件检索三种方式:
顺序存取;直接存取;按关键字存取。

(二)理解文件结构及其组织方式–顺序、索引、散列文件(HASH)

1.顺序文件:
(1)顺序文件:是记录按其在文件中的逻辑次序依次进入存储介质而建立的,即顺序文件中的记录的和逻辑记录的顺序是一致的。
若次序相继的两个物理记录在存储介质上的存储位置是相邻的,又称连续文件;若物理记录之间的次序由指针相链表示,又称串联文件。
(2)组织方式:根据记录的序号或记录的相对位置来进行存取的文件组织方式。
(3)特性:
    a.存取第i个记录,必须先搜索在它之前的第i-1个记录;
    b.插入新的记录时,只能加在文件的末尾;
    c.若要更新某个记录时,则必须将整个文件进行复制。
(4)优点:连续存取的速度快,因此主要用于顺序存储、批量修改的情况。若对应答时间要求不严格亦可直接存取。
2.索引文件
(1)索引文件:除了文件(称作数据区)本身之外,另建立一张指示逻辑记录和物理记录之间一一对应的关系的表--索引表。
这类包括文件数据区和索引表两大部分的文件叫做索引文件。
注:索引表中的每一项称为索引项。索引表中的索引项总是按关键字(或逻辑记录号)顺序排列。
(2)索引顺序文件:数据区的关键字也按关键字顺序排列。
(3)非索引顺序文件:数据区的关键字不按关键字顺序排列。
(4)索引文件的检索方式为直接存取或按关键字存取,检索过程分为两步:
    首先,先查索引表,若索引表上存在该记录,则根据索引项的指示读取外存上的记录。
    否则就说明外存上不存在该记录,也就无需访问外存。
(5)索引文件修改:
    删除一个记录时,仅删去相应的索引项;
    插入一个记录时,将记录插入在数据区的末尾,同时在索引表中插入索引项;
    更新记录时,将更新后的记录插入在数据区的末尾,同时修改索引表中相应的索引项。
(6)当记录书目比较大时,索引表也很大,以致于一块物理块容纳不下。因此,可以对索引表建立一个索引,称作查找表。检索记录时,
从查找表->索引表->读取记录。通常最高有四级索引:数据文件->索引表->查找表->第二查找表->第三查找表。
(7)稠密索引:数据文件中的记录不按关键字顺序排列,则必须对每个记录建立一个索引项。
(8)非稠密索引:数据文件中的记录关键字顺序排列,则可对一组记录建立一个索引项。
3.散列文件(HASH)–直接存取文件
(1)散列文件:利用杂凑法进行组织的文件。它类似于哈希表,
即根据文件中的关键字的特点设计的一种哈希函数和处理冲突的方法将记录散列到存储设备上,故称散列文件。
注:若干个记录组成一个存储单位,叫做桶。对散列文件,处理冲突主要用链地址法。
(2)优点:文件随即存取,不需要排序;插入、删除方便,存取速度快,不需要索引去,节省存储空间。
(3)缺点:不能进行顺序存取、只能按关键字随即存取,且询问方式限于简单询问,
并且经过多从插、删之后导致文件结构不合理,即溢出桶满而桶内多数为被删除的记录。此时亦需重组文件。
  • 3
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无情cv手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值