数据结构笔记

数据结构笔记

绪论

概念范围:数据>数据元素>数据项

性质相同的数据元素集合叫做数据对象

数据元素时数据的基本单位

数据项是是数据的不可分割的最小单位

逻辑结构: 1.集合结构 2.线性结构 3.树结构 4.图结构或网状结构

逻辑结构也可分为: 线性结构非线性结构

存储结构分为 顺序存储结构链式存储结构

计算复杂度时要注意一点,如果操作的次数可以得出具体数,比如for循环里要循环100次,这可以直接看作复杂度O(1)。

在相同规模n下,复杂度O(n)的算法在时间上总是优于复杂度O(2^n)。这是正确的,n不可以认为是1,因为如果是1,复杂度就不可以写成带n的了,只有n趋于很大时才可以写带n的复杂度,否则这个复杂度表达甚至不具有普适性。

算法原地工作指算法运行所需要的辅助空间相当于输入是一个常数。

顺序存储结构中数据元素之间的逻辑关系是由 存储位置 表示的

链式存储结构中数据元素之间的逻辑关系是由 指针 表示的

有序表和顺序表是两个东西,有序表是逻辑结构中的,顺序表是与存储结构有关的术语,同理,链表也是和存储结构有关的术语。

抽象数据类型的三个组成部分: 数据对象,数据关系和基本操作

算法是解决问题的有限运算序列,而不是解决问题的计算方法,因为计算方法可能是需要无线长的时间的,这也就不可称之为算法。

高效性不是达到所需要的时间性能。而是指设计合理,执行效率高,达到时间性能即可这种片面的视角是无法准确描述高效性的。

空间复杂度是指在算法运行时需要的辅助运行空间,比如将数组a进行reverse。如果你用了辅助数组b,且b的大小是n,那么空间复杂度就是n了,如果你直接对数组a两头开始对元素进行交换,也就是说没有用额外的空间,那么空间复杂度就是O(1).

线性表

线性表的定义和特点

线性表的数据元素虽然不同,但同一线性表必定具有相同的特性,即属于同一数据对象,相邻数据元素存在序偶关系。

非空的线性表或线性结构的特点

1.第一个元素和最后一个的元素唯一

2.除了第一个之外,结构中的每个数据元素均只有一个前驱。除了最后一个之外,结构中的每个数据元素均只有一个后继。

线性表的顺序存储结构是一种随机存取的存储结构

单链表的存储密度一定小于1,因为 存储密度=单链表数据项所占空间/结点所占空间,结点所占空间由数据项所占空间存放后继结点地址的链域,存放后继结点地址的链域一定存在,故存储密度小于1。

创建一个包括n个节点的有序单链表的时间复杂度是 O(n^2)

所有数据通过指针的链接而组成单链表这是错误的,应该是所有的结点通过指针的链接而组成单链表

线性表的顺序表示和实现

定义顺序表的线性存储结构
typedef struct
{
    char no[20]//图书ISBN
    char name[50];//图书名字
    float price;   //图书价格
}ElemType;
typedef struct
{
    ElemType *elem; //存储空间的基地址
    int length;
基本操作

1.初始化

Status InitList(SqList &L)
{
    L.elem=new ElemType[MAXSIZE];//分配内存;
    if(!L.elem) exit (OVERFLOW);
    L.length=0;
    return OK;
}

2.取值

Status GetElem(SqList L,int i,ElemType &e)//取出第i个存入e
{
    if(i<1||i>n) return ERROR;
    e=L.elem[i-1];
    return OK;
}

3.查找

int LocateElem(SqList L,Elemtype e)//查找e所在的位置
{
    for(int i=0;i<L.length;i++)
    {
        if(L.elem[i]==e) return i+1;
    }
    return 0;
}

4.插入

Status ListInsert(SqList &L,int i,ElemType e)//在第i个插入e
{
    if(i<1||i>L.length+1) retrun ERROR;//这里要注意i是可以等于L.length+1,因为可以把e添到最后
    if(L.length==MAXSIZE) retrun ERROR;
    for(int j=L.length-1;j>=i-1;j--)
    {
        L.elem[j+1]=L.elem[j];
    }
    L.elem[i-1]=e;
    L.length++;
    return OK;
}

5.删除

Status ListDelete(SqList &L,int i)//删除第i个elem
{
    if(i>L.length||i<1) return ERROR;
    for(int j=i;j<L.length;j++)
    {
        L.elem[j-1]=L.elem[j];
    }
    L.length++;
    return OK;
}

线性表的链式表示和实现

定义顺序表的线性存储结构:

1.节点包括两个域:指针域数据域

2.结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻

3.线性表的链式表示又称为非顺序映像链式映像

单链表/线性链表:结点只有一个指针域的链表。

双链表:有两个指针域的链表。

循环链表:首尾相接的链表称为循环链表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OExX5QT0-1588579749514)(https://s1.ax1x.com/2020/04/11/GH87QK.md.png)]

6.链表有无头结点都是可以的。

有头结点的时候,当头节点的指针域为空时表示空表。

无头结点的时候头指针指向的节点为为空。

头节点优势:

便于首元结点的处理:首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其它位置一致,无须进行特殊处理;

便于空表和非空表的统一处理:无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。

8.链表的查找O(n)的,插入和删除O(1)的,但是如果要在单链表中进行前插或删除操作,由于要从头查找前驱结点,所耗时间复杂度为O(n)

定义:

typedef struct LNode
{
	ElemType data;//数据域
    struct LNode *next;//指针域
}LNode,*LinkList;
//这里的LNode *p 和LinkList p 意义相同。
//p是指针变量,表示结点地址。
//*p是结点变量,表示一个结点。
单链表基本操作:

1.初始化

(1)生成新结点作为头结点,用头指针L指向头结点。

(2)头节点的指针域置空。

Status InitList_L(LinkList &L)
{
    L=new LNode; //开辟一个存放结点的存储空间,返回一个指向该存储空间的地址。
    L->next=NULL; //L的指针域为空。
    return OK;
}

2.销毁链表

Status DestroyList_L(LinkList &L)
{
    LinkList p;
    while(L)
    {
        p=L;
        L=L->next;
        delete p;//删除指针p所指的内容
    }
    return OK;
}

3.清空

Status ClearList(LinkList& L)//将L重置为空表
{
	LinkList p,q;
    p=L->next;//p指向第一个结点
    while(p)  //没到表尾
    {
        q=p->next;
        delete p;
        p=q;
    }
    L->next=NULL;
    return OK;
}

4.求表长

int ListLength_L(LinkList L)
{
    LinkList p;
    p=L->next;
    int i=0;
    while(p)
    {
        i++;
        p->next;
    }
    return i;
}

5.判断表是否为空

int ListEmpty(LinkList L)
{
    if(L->next) return 0;
    else return 1;
}

6.获取第i个结点e

Status GetElem_L(LinkList L,int i,ElemType &e)
{
    LinkList p;
    p=L->next;int j=1;//初始化
    while(p&&j<i)
    {
        p=p->next;++j;
    }
    if(!p||j>i) return ERROR;//第i个结点不存在 j>i是为了防止i<=0
	ElemType e=p->data;
    return OK;
}

7.在线性表L中查找值为e的数据元素并返回地址

LNode *LocateELem_L(LinkList L,Elemtype e)
{
    p=L->next;
    while(p&&p->data!=e)
    {
        p=p->next;
    }
    return p;
}

8.在线性表L中查找值为e的数据元素并返回位置序号

int LocateElem_L(LinkList L,ElemType e)   
{
    p=p->next;
    int j=0;
    while(p&&p->data!=e)   
    {
        j++;
        p=p->next;
    }
    return j;
}

9.插入结点到第i个结点的位置上,即ai-1和ai之间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SDwPbjLH-1588579749516)(https://s1.ax1x.com/2020/04/11/GqZmQJ.png)]

(1)找到ai-1存储位置p

(2)生成一个新结点s

(3)将新结点s的数据域置为x

(4)新结点s的指针域指向结点ai

(5)令结点p的指针域指向新结点s

Status ListInsert_L(LinkList &L,int i,ElemType e)
{
    LinkList p=L;j=0;
    while(p&&j<i-1) {p=p->next;++j;}
    if(!p||j>i-1) return ERROR;
    s=new LNode;
    s->data=e;
    s->next=p->next;
    p->next=s;
    return OK;
}

10.删除第i个结点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Frf2R9ka-1588579749518)(https://s1.ax1x.com/2020/04/12/GqUFG4.png)]

(1)找到ai-1存储位置p

(2)保存要删除的结点的值

(3)令p->next指向ai的直接后继结点

(4)释放结点ai的空间

Status ListDelete_L(LinkList &L,int i,ElemType &e)
{
    LinkList p=L;int j=0;
    while(p->next&&j<i-1)
    {
        j++;
        p=p->next;
    }
    if(!(p->next)||j>i-1) return ERROR;
    LinkList q=p->next;
    p->next=q->next;
    e=q->data;//保存删除节点的数据域;
    delete q;
    return OK;
}

11.单链表的建立:前插法

GqUDzj.png

void CreatList_L(LinkList &L,int n)
{
    L=new LNode;
    L->next=NULL;//建立一个带头结点的单链表,L为头节点的地址
    for(int i=1;i<=n;i++)
    {
        LinkList p=new LNode;
        cin>>p->data;
        p->next=L->next;L->next=p;//插入表头
    }
}

12.单链表的建立:尾插法

void CreateList_L(LinkList &L,int n)
{
    L=new LNode;
    L->next=NULL;
    LinkList r=L;//尾指针r指向头节点
    for(int i=0;i<n;i++)
    {
        LinkList p=new LNode;
        cin>>p->data;
        p->next=NULL;r->next=p;//插入到尾部,记得插入的时候要先把p的next置为空
        r=p; //r指向新的尾结点
    }
}
循环链表基本操作:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SzVn3DgR-1588579749523)(https://s1.ax1x.com/2020/04/12/Gqd3DI.png)]

1.终止条件的变化:由p!=NULL变为 p!=L ,由p->next!=NULL变为p->next!=L

2.对于循环链表来说,有时不给出头指针,而给出尾指针可以更方便的找到第一个最后一个结点

3.rear->next->next就是开始结点(即首元结点)rear终端结点,终端结点下一个是头结点,头节点下一个是首元节点。

1.循环链表的合并

GqdyV0.png

LinkList Connect(LinkList Ta,LinkList Tb)
{
    LinkList p=Ta->next;//p存表头
    Ta->next=Tb->next->next;//Tb表头连接Ta表头
    delete Tb->next;//释放Tb表头结点
    Tb->next=p;//修改指针
    return Tb;
}
双向链表基本操作:

1.定义:

Gqvm80.png

typedef struct DuLNode
{
    ElemType data;
    struct DuLNode *prior;
    struct DuLNode *next;
}DuLNode,*DuLinkList;

2.插入

GqxIOO.png

**执行顺序:**123随便换顺序,4是最后一个不能变。

Status ListInsert_DuL(DuLinkList &L,int i,ElemType e)
{
    DuLinkList p=L->next;int j=0;
    while(p&&j<i)
    {
        j++;
        p=p->next;
    }
    if(!p||j>i) return ERROR;
    s=new DuLNode;
    s->data=e;
    s->next=p;
    s->prior=p->prior;
    p->prior->next=s;
    p->prior=s;
}

3.删除

Status ListDelete_Dul(DuLinkList &L,int i,ElemType e)
{
    DuLinkList p=GetElem(L,i);
    if(!p) return ERROR;
    p->prior->next=p->next;
    p->next->prior=p->prior;
    delete p;
    return OK;
}

线性表的进阶操作

线性表的合并:

比如 A=(7,5,3,11) B=(2,6,3) 合并后 A=(7,5,3,11,2,6). 去掉重复元素,且保持相对位置。

①分别获取LA的表长m和LB的表长n。

②从LB中第一个数据元素开始,循环n次执行以下操作。

​ 从LB中查找第i个数据元素赋值给e;

​ 在LA中查找元素e,如果不存在,则将e插在表LA的最后。

void MergeList(List &LA,List LB)//把B合并到A上
{
	int m=ListLength(LA),n=ListLength(LB);
    for(int i=1;i<=n;i++)
    {
        List e=GetElem(LB,i);
        if(!LocateElem(LA,e))  ListInsert(LA,++m,e);//如果LA中不存在e,则直接插入到LA后方。
    }
    
}
有序表的合并:

A=(3,5,8,11) B=(2,6,8,9,11,15,20) 合并后---->C=(2,3,5,6,8,8,9,11,11,15,20)

步骤:

①创建一个表长尾m+n的空表LC。

②指针pc初始化,指向LC的第一个元素。

③指针pa和pb初始化,分别指向LA和LB的第一个元素。

④当指针pa和pb均未到达相应表尾时,则依次比较pa和pb所指向的元素值,从LA或LB中摘取元素值较小的结点插入LC的最后。

⑤如果pb已到达LB的表尾,一次将LA的剩余元素插入LC的最后。

⑥如果pa已到达LA的表尾,一次将LB的剩余元素插入LC的最后。

1.顺序表的有序合并:

void MergeList_Sq(SqList LA,SqList LB,SqList &LC)
{
    LC.Length=LB.Length+LA.Length;
    pc=LC.elem;  pb=LB.elem;  pa=LA.elem;
    pa_last=LA.elem+LA.length-1;  pb_last=LB.elem+LB.length-1;
    while((pa<=pa_last)&&(pb<=pb_last))
    {
        if(*pa<=*pb) *pc++=*pa++;
        else *pc++=*pb++;
    }
    while(pa<=pa_last) *pc++=*pa++;
    while(pb<=pb_last) *pc++=*pb++;
}

2.链式表的有序合并:

void MergeList_L(LinkList &LA,LinkList &LB,LinkList &LC)
{
    pa=LA->next;  pb=LB->next;  
    LC=LA;//用LA的头节点作为LC的头节点。
    pc=LC;//pc指向LC
    pc=LA->next;
    while((pa!=nullptr)&&(pb!=nullptr))
    {
        if(pa->next<=pb->next) {pc->next=pa; pc=pc->next; pa=pa->next;}
        else {pc->next=pb;pc=pc->next; pb=pb->next;}
    }
    pc->next=pa?pa:pb;//pa为空的话,pc->next=pb,否则为pa。
    delete LB;
}

为什么只delete LB?

LA和LB的头指针都是用了所谓的「哨兵节点」,它们本身是空的节点,所以合并时并不参与。用哨兵节点做头指针是为了简化边界条件。
两个链表合并完之后,LA(LC)是新链表的头指针,仍是一个哨兵节点。这时原本用作LB头指针的这个哨兵节点就没有用了,所以要释放掉。

如果你创建一个链表的时候不使用哨兵节点做头指针,那合并完之后就不可以释放掉LB的头指针。

栈和队列

栈和队列的定义和特点:

1**. 栈**:后进先出 队列:先进先出

2.[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fG8hZLPw-1588579749528)(https://s1.ax1x.com/2020/04/13/GjVPxS.png)]

顺序栈:基本操作

1.定义:

#define MAXSIZE 100
typedef struct
{
    SElemType *base;
    SElemType *top;
    int stacksize;
}SqStack;

2.初始化:

Status InitStack(SqStack &S)
{
    S.base=new SElemType[MAXSIZE];
    if(!S.base) return OVERFLOW;
    S.top=S.base;
    S.stackSize = MAXSIZE;
}

3.判空

bool StackEmpty(SqStack S)
{
    if(S.top==S.base) return true;
    else return false; 
}

4.求顺序栈长度

int StackLength(SqStack S)
{
    return S.top-S.base;
}

5.清空顺序栈

Status ClearStack(Stack S)
{
    if(S.base) S.top=S.base;
    return OK;
}

6.销毁顺序栈

Status DestroyStack(SqStack &S)
{
    if(S.base)
    {
        delete S.base;
        S.stacksize=0;
        S.base=S.top=NULL
    }
}

7.进栈

Status Push(SqStack &S,SElemType e)
{
    if(S.top-S.base==S.stacksize)//栈满
       	return ERROR;
    *S.top++=e;
    return OK; 
}

8.出栈

Status Pop(SqStack &S,SElemType &e)
{
    if(S.top==S.base)//栈空
        return ERROR;
    e=*--S.top;
    return OK;
}

9.取栈顶元素

Status GetTop(SqStack &S,SElemType &e)
{
    if(S.top==S.base) return ERROR;//栈空
    e=*(S.top-1);
    return OK;
}

链栈:基本操作

1.定义

typedef struct StackNode
{
    SElemType data;
    struct StackNode *next;
}StackNode,*LinkStack;

LinkStack S;

2.初始化

void InitStack(LinkStack &S)
{
    S=NULL;
}

3.判断链栈是否为空

Status StackEmpty(LinkStack S)
{
    if(S=NULL) return ture;
    else return false;
}

4.进栈

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d29pqRts-1588579749529)(https://s1.ax1x.com/2020/04/13/GjlxO0.png)]

Status Push(LinkStack &S,SElemType e)
{
    p=new StackNode;
    if(!p) exit(OVERFLOW);
    p->data=e;p->next=S;S=p;
    return OK;
}

5.出栈

Status Pop(LinkStack &S,SElemType &e)
{
    if(S==NULL) return ERROR;
    e=S->data;p=S;S=S->next;
    delete p;
    return OK;
}

6.取栈顶元素

SElemType GetTop(LinkStack S)
{
    if(S==NULL) exit(1);
    else return S->data;
}

队列(顺序):基本操作

1.队列的定义

#define M 100
Typedef struct
{
    QElemType *base;
    int front;
    int rear;
}SqQueue;

2.一些问题

front=rear=0;//初始情况
front==rear//空队标志
base[rear++]=x;//入队
x=base[front++];//出队

但以上操作会有些问题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJ7Q28Xf-1588579749530)(https://s1.ax1x.com/2020/04/17/JEOmvV.png)]

解决方案:利用循环队列

base[0]接在base[M-1]之后
若rear+1=M
则rear=0;
入队操作
base[rear]=x;
rear=(rear+1)%M;

出队操作
x=base[front];
front=(front+1)%M;

队空:
front==rear

队满:
(rear+1)%M==front

3.队列初始化

Status InitQueue(SqQueue &Q)
{
    Q.base=new QElemType[MAXQSIZE];
    if(!Q.base) exit(OVERFLOW);
    Q.front=Q.rear=0;
    return OK;
}

4.求循环队列长度

int QueueLength(SqQueue Q)
{
    return (Q.rear-Q.front+MAXQSIZE)%M;//记得取模
}

5.循环队列入队

Status EnQueue(SqQueue &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;
}

6.循环队列出队

Status DeQueue(SqQueue &Q,QElemType e)
{
    if(Q.rear==Q.front) return ERROR;
    e=Q.base[Q.front];
    Q.front=(Q.rear+1)%MAXQSIZE;
    return OK;
}

队列(链):基本操作

1.定义

typedef struct QNode
{
    QElemType data;
    struct Qnode *next;
}Qnode,*QueuePtr;

typedef struct
{
    QueuePtr front;
    QueuePtr rear;
}LinkQueue;

2.初始化

Status InitQueue (LinkQueue &Q)
{
    Q.front=Q.rear=(QueuePtr) malloc(sizeof(QNode)); 
    if(!Q.front) exit(OVERFLOW);//申请内存失败
   	Q.front->next=NULL;//头结点指向空,即首元结点不存在
    return OK;
}

3.销毁链队列

Status DestroyQueue (LinkQueue &Q){
   while(Q.front)
   {
      Q.rear=Q.front->next;//头节点指向尾结点
      free(Q.front);
      Q.front=Q.rear; 
   }    
   return OK;
}

4.判空

Status QueueEmpty(LinkQueue Q)
{
    return (Q.front==Q.rear);//头结点和尾结点相同,即初始为空的状态。
}

5.求链队列头元素

Status GetHead(LinkQueue Q,QElemType &e)
{
    if(Q.front==Q.rear) return ERROR;//队列为空 返回error
    e=Q.front->next->data;//头结点指向的下一个结点为首元结点,首元结点中的data即为头元素。
    return OK;
}

6.入队

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7gQrv5uG-1588579749532)(https://s1.ax1x.com/2020/04/20/JQfHtH.png)]

Status EnQueue(LinkQueue &Q,QElemType e)
{
    QueuePtr p=(QueuePtr)malloc(sizeof(QNode));//申请内存空间
    if(!p) exit(OVERFLOW);//申请内存失败
    p->data=e;p->next=NULL;//data
    Q.rear->next=p;
}

7.出队

JQheBT.png

Status DeQueue(LinkQueue &Q,QElemType &e)
{
    if(Q.front==Q.rear) return ERROR;
    QueuePtr p=Q.front->next;
    Q.front->next=p->next;
    if(Q.rear==p) Q.rear=Q.front;
    delete p;
    return OK;
}

串(String)

存储

顺序存储

#define MAXSIZE 255//串的最大长度
typedef struct
{
    char ch[MAXSIZE+1];
    int length;
}SString;

堆存储

typedef struct
{
    char *ch;
    int length;
}SString;

链式存储

#define CHUNKSIZE 80
typedef struct Chunk
{
    char ch[CHUNKSIZE];
    struct Chunk *next;
}Chunk;

typedef struct
{
    Chunk *head,*tail;//串的头指针和尾指针
    int curlen;//当前长度
}LString;

BF算法

其实就是暴力匹配

int Index_BF(SString S,SString T,int pos)//返回模式T在 主串S中第pos个字符开始 第一次出现的位置。
{
    int i=pos,j=1;
    while(i<=S.length&&j<=T.length)
    {
        if(S.ch[i]==T.ch[j]){i++;j++;}
        else
        {
            i=i-j+2;
            j=1;
        }
    }
    if(j>T.length) return (i-T.length);
    else return 0;//匹配失败返回0 
}

KMP算法

**next[j]:**当前模式种第j个字符与主串中相应字符失配时,在模式中需要重新和主串中该字符进行比较的字符的位置。

n e x t [ j ] = { 0 j=1( t 1 与 s i 比较不等时,下一步进行 t 1 与 s i + 1 的比较) m a x 模式串前后缀最长公共序列的长度 1 k=1(不存在相同的字串,下一步进行 t 1 与 s i 的比较) next[j]= \begin{cases} 0& \text{j=1($t_1$与$s_i$比较不等时,下一步进行$t_1$与$s_{i+1}$的比较)}\\ max& \text{模式串前后缀最长公共序列的长度}\\1& \text{k=1(不存在相同的字串,下一步进行$t_1$与$s_i$的比较)} \end{cases} next[j]=0max1j=1(t1si比较不等时,下一步进行t1si+1的比较)模式串前后缀最长公共序列的长度k=1(不存在相同的字串,下一步进行t1si的比较)

int index_kmp(SString S,SString T,int pos)
{
    int i=pos,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;//匹配失败
}

如何求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];
    }
}

如何求nextval函数?

void get_nextval(SString T,int nextval[])
{
    int i=1,j=0;
    next[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];
            next[i]=j;
        }
        else j=nextval[j];
    }
}

中相应字符失配时,在模式中需要重新和主串中该字符进行比较的字符的位置。

n e x t [ j ] = { 0 j=1( t 1 与 s i 比较不等时,下一步进行 t 1 与 s i + 1 的比较) m a x 模式串前后缀最长公共序列的长度 1 k=1(不存在相同的字串,下一步进行 t 1 与 s i 的比较) next[j]= \begin{cases} 0& \text{j=1($t_1$与$s_i$比较不等时,下一步进行$t_1$与$s_{i+1}$的比较)}\\ max& \text{模式串前后缀最长公共序列的长度}\\1& \text{k=1(不存在相同的字串,下一步进行$t_1$与$s_i$的比较)} \end{cases} next[j]=0max1j=1(t1si比较不等时,下一步进行t1si+1的比较)模式串前后缀最长公共序列的长度k=1(不存在相同的字串,下一步进行t1si的比较)

int index_kmp(SString S,SString T,int pos)
{
    int i=pos,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;//匹配失败
}

如何求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];
    }
}

如何求nextval函数?

void get_nextval(SString T,int nextval[])
{
    int i=1,j=0;
    next[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];
            next[i]=j;
        }
        else j=nextval[j];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值