c语言基础

基本语法

  1. 预定义常量及类型

    函数结果状态代码

    #define OK 1
    #define ERROR 0
    #define OVERFLOW -2
    //Status是函数返回值类型,其值是函数结果状态代码。
    typedef int Status;
    
  2. 数据结构的表示(存储结构)类型定义( typedef )描述;数据元素类型约定为ElemType,由用户在使用该数据类型时自行定义。

  3. 基本操作的算法都用如下格式的函数来描述:

    函数类型函数名(函数参数表){
    	//算法说明
    	语句序列
    }//函数名
    

    当函数返回值为函数结果状态代码时,函数定义为Status 类型。为了便于描述算法,除了值调用方式外,增加了C++语言引用调用的参数传递方式。在形参表中,以“&”打头的参数即为引用参数。传递引用给函数与传递指针的效果是-样的,形参变化实参也发生变化,但引用使用起来比指针更加方便、高效。

  4. 内存的动态分配与释放。

    使用new和delete动态分配和释放内存空间:
    分配空间 指针变量=new 数据类型;
    释放空间 delete指针变量;
    
  5. 赋值语句:

    简单赋值 变量名 = 表达式;
    串联赋值 变量名1 = 变量名2 = ... = 变量名n = 表达式;
    成组赋值 (变量名1, ..., 变量名n) = (表达式1, ..., 表达式n);
    结构赋值 结构名1 = 结构名2;
            结构名 = (1,2,....值n);
    条件赋值 变量名 = 条件表达式?表达式T:表达式F;
    交换赋值 变量名1 <-->变量名2;
    
  6. 选择语句:

    条件语句1 if (表达式)语句;
    条件语句2 if (表达式) 语句;
    				 else语句;
    开关语句 switch (表达式)
    				{
    					case1: 语句序列1 ;break;
    					case2: 语句序列2 ;break;
    					...
    					case值n: 语句序列n ;break;
    					default: 语句序列n+1;
    				}
    
  7. 循环语句:

    for语句    for (表达式1; 条件; 表达式2)语句;
    while语句  while (条件) 语句;
    do-while语句 do {
    									语句序列;
    								} while (条件);
    
  8. 结束语句:

    函数结束语句 return 表达式;
    					 return;
    					 case 或循环结束语句 break;
    					 异常结束语句 exit (异常代码);
    
  9. 输入输出语句使用C++流式输人输出的形式:

    输入语句 cin>>变量1>>..>>变量n;
    输出语句 cout<<表达式1<<...<<表达式n;
    
  10. 基本函数:

    求最大值 Max (表达式1, ..., 表达式n)
    求最小值 Min (表达式1, ..., 表达式n)
    

线性表

线性表的顺序表示和实现

线性表的顺序存储结构:

#define MAXSIZE 100 // 顺序表可能达到的最大长度
typedef struct
{
	ElemType *elem; // 存储空间的基地址
	int length;     // 当前长度
}SqList;          // 顺序表的结构类型为SqList

初始化:

Status InitList(SqList &L)
{
	L.elem = new ElemType[MAXSIZE]; // 为顺序表分配一个大小为MAXSIZE的数组空间
	if (!L.elem) exit(OVERFLOW);
	L.length = 0;
	return OK;
{

取值:

Status GetElem(SqList L, int i, ElemType &e)
{
	if (i<1 || i>L.length) return ERROR;
	e = L.elem[i-1];
	return OK;
}

查找

int LocateElem(SqList L, ElemType e)
{
	for (int i = 0; i < L.length; i++)
		if (L.elem[i] == e) return i+1;
	return 0;
}

插入

Status ListInsert(SqList &L, int i, ElemType e)
{
	if ((i<1) || (i>L.length+1)) return ERROR;
	if (L.length == MAXSIZE) return 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;
}

删除

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

多项式的顺序存储结构:

#define MAXSIZE 100 // 多现实可能达到的最大长度
typedef struct      // 多项式非零项的定义
{
	float coef; // 系数
	int expn;   // 指数
}Polynomial;
typedef struct
{
	Polynmial *elem; // 存储空间的基地址
	int length;      // 多项式中当前项的个数
}SqList            // 多项式的顺序存储结构类型为SqList

图书表的顺序春初结构类型定义:

#define MAXSIZE 1000 
typedef struct
{
	char no[20];   // 图书ISBN
	char name[50]; // 图书名字
	float price;   // 图书价格
}Book;
typedef struct
{
	Book *elem; // 存储空间的基地址
	int length; // 图书表中当前图书个数
}SqList;      // 图书表的顺序存储结构类型位SqList

线性表的链式表示和实现

顺序表的初始化:

// 构造一个空的顺序表L
Status InitList(SqList &L) 
{
	L.elem = new ElemType[MAXSIZE]; // 为顺序表分配一个大小为MAXSIZE的数组空间
	if(!L.elem) exit(OVERFLOW);     // 存储分配失败退出
	L.length = 0;                   // 空表长度为0
	return OK;
}

顺序表的取值:

Status GetElem(SqList L, int i, ElemType &e)
{
	if (i < 1 || i > L.length) return ERROR; // 判断i值是否合理,若不合理,返回ERROR
	e = L.elem[i - 1];                       // elem[i-1]单元存储第i个数据元素
	return OK;
}

顺序表的查找:

int LocateElem(SqList L, ElemType e)
{
	for (i=0; i<L.length; i++)
		if (L.elem[i] == e) return i+1; // 查找成功,返回序号i+1
	return 0;                         // 查找失败,返回0
}

顺序表的插入:

// 在顺序表L中的第i个位置之前插入新的元素e,i值的合法范围是1<=i<=L.length+1
Status ListInsert(SqList &L, int i, ElemType e)
{
	if ((i<1) || (i>L.length+1)) return ERROR; // i值不合法
	if (L.length == MAXSIZE) return ERROR;     // 当前存储空间已满
	for (j=L.length-1; j>=1-1; j--)
		L.elem[j+1] = L.elem[j]; // 插入位置及之后的元素后移
	L.elem[i-1] = e;           // 将新元素e放入第i个位置 
	++L.length;                // 表长加1
	return OK;
}

顺序表的删除:

Status ListDelete(SqList &L, int i)
{
	if ((i<1) || (i>L.length)) return ERROR; // i值不合法
	for (j=i; j<=L.length-1; j++)
		L.elem[j-1] = L.elem[j]; // 被删除元素之后的元素前移
	--L.length;                // 表长减1
	return OK;
}

线性表的链式表示和实现

单链表的存储结构:

typedef struct LNode
{
	ElemType data;      // 结点的数据域
	struct LNode *next; // 节点的指针域
}LNode, *LinkList;    // LinkList为指向结构体LNode的指针类型

初始化:

// 构造一个空的单链表L
Status InitList(LinkList &L)
{
	L = new LNode;  // 生成新节点作为头节点,用头指针L指向头节点
	L->next = NULL; // 头节点的指针域置空
	return OK;
}

单链表的取值:

// 在带偶节点的单链表L中根据序号i获取元素的值,用e返回L中的第i个数据元素的值
Status GetElem(LinkList L, int i, ElemType &e)
{
	p = L->next; // 初始化,p指向首元节点,计数器j初值赋为1
	j = 1;       // 顺链域向后扫描,直到p为空或p指向第i个元素
	while (p&&j < i)
	{
		p = p->next; // p指向下一个节点
		++j;         // 计数器j相应加1
	}
	if (!p || j>i) return ERROR; // i值不合法i>n或i<=0
	e = p->data;                 // 取第i个节点的数据域
	return OK;
}

单链表的按值查找:

// 在带头节点的单链表L中查找值为e的元素
LNode *LocateElem(LinkList L, ElemType e)
{
	p = L->next;              // 初始化,p指向首元结点
	while (p && p->data != e) // p指向先一个节点
		p = p->next;            // 查找成功返回值为e的节点地址p,查找失败p为NULL
	return p;
}

单链表的插入

Status ListInsert(LinkList &L, int i, ElemType e)
{
	p = L;
	j = 0;
	while (p && (j < i - 1)) // 查找第i-1个结点,p指向该节点
	{
		p = p->next;
		j++;
	}
	s = new LNode;
	s->data = e;
	s->next = p->next;
	p->next = s;
	return OK;
}

单链表删除:

// 在带头结点的单链表L中,删除第i个元素
Status ListDelete(LinkList &L, int i)
{
	p = L;
	j = 0;
	while (p && (j < i - 1)) // 查找第i-1个结点,p指向该节点
	{
		p = p->next;
		++j;
	}
	if (!(p->next) || (j>i-1)) return ERROR; // 当i>n或i<1时,删除位置不合理
	q = q->next;       // 临时保存被删节点的地址以备释放
	p->next = q->next; // 改变删除结点前驱结点的指针域
	delete q;          // 释放删除结点的空间
	return OK;
}

前插法创建单链表:

void CreateList_H(LinkList &L, int n)
{
	L = new LNode;
	L->next = NULL; // 先建立一个带头结点的空链表
	for (i=0; i<n; i++)
	{
		p = new LNode;     // 生成新结点*p
		cin>>p->data;      // 输入元素值赋给新结点*p的数据域
		p->next = L->next; // 将新节点*p插入到头节点之后
		L->next = p;
	}
}

后插法创建单链表

void CreateList_R(LinkList &L, int n)
{
	L = new LNode;
	L->next = NULL; // 先建立一个带头结点的空链表   
	r = L;          // 尾指针r指向头节点
	for (i=0; i<n; ++i)
	{
		p = new LNode;  // 生成新结点*p
		cin>>p->data;   // 输入元素值赋给新结点*p的数据域
		p->next = NULL;
		r->next = p;    // 将新节点*p插入到尾节点*r之后
		r = p;          // r指向新的尾结点*p
	}
}

循环链表

循环链表的存储结构:

typedef struct DulNode
{
	ElemType data;         // 数据域
	struct DulNode *prior; // 直接前驱
	struct DulNode *next;  // 直接后驱
}

在这里插入图片描述

双向链表的插入:

Status ListInsert_DuL(DuLinkList &L, int i, ElemType e)
{
	if(!(p = GetElem_DuL(L,i))) // 在L中确定第i个元素的位置指针p
		return ERROR;      // p为NULL时,第i个元素不存在
	s = new DuLNode;     // 生成新节点*s
	s->data = e;         // 将结点*s数据域置为e
	s->prior = p->prior; // 将结点*s插入L中,对应 2.20 ①
	p->prior->next = s;  // 对应图2.20 ②
	s->next = p;         // 对应图2.20 ③
	p->prior = s;        // 对应图2.20 ④
	return OK;
}

双向链表的删除:

Status ListDelete_DuL(DuLinkList &L, int i)
{
	if (!(p = GetElem_DuL(L,i))) // 在L中确定第i哥元素的位置指针p
		return ERROR;
	p->prior->next = p->next;    // 修改被删除结点的前驱结点的后继指针,对应图2.21 ①
	p->next->prior = p->prior;   // 修改被删除结点的前驱结点的前驱指针,对应图2.21 ②
	delete p;  // 释放被删除结点的空间
	return OK;
}

算法实例

顺序有序表的合并

LA和LB合并成LC

void MergeList_Sq(SqList LA, SqList LB, SqList &LC)
{
	LC.length = LA.length + LB.length; // 新表长度为待合并两表的长度之和
	LC.elem = new ElemType[LC.length]; // 为合并后的新标分配一个数组空间
	pc = LC.elem;                      // 指针pc指向新表的第一个元素
	pa = LA.elem;
	pb = LB.elem;                      // 指针pa和pb的初值分别指向两个表的第一个元素
	pa_last = LA.elem + LA.length - 1; // 指针pa_last指向LA的最后一个元素
	pb_last = LB.elem + LB.length - 1; // 指针pb_last指向LB的最后一个元素
	while ((pa<=pa_last) && (pb<=pb_last)) // LA和LB均未到达表尾
	{
		if (*pa <= *pb) *pc++ = *pa++; // 依次“摘取”两表中值较小的结点插入到LC的最后
		else *pc++ = *pb++;
	}
	while (pa<=pa_last) *pc++=*pa++; // LB已到达表尾,依次将LA的剩余元素插入LC的最后
	while (pb<=pb_last) *pc++=*pb++; // LA已到达表尾,依次将LB的剩余元素插入LC的最后
}
链式有序表的合并
void MergeList_L(LinkList &LA, LinkList &LB, LinkList &LC)
{
	pa = LA->next; // pa和pb的初值分别指向两个表的第一个节点
	pb = LB->next; // 用LA的头节点作为LC的头节点
	pc = LC;       // pc的初值指向LC的头节点
	while (pa&&pb) // LA和LB均未到达表尾,依次“摘取”两表中值较小的结点插入到LC的最后
	{
		if (pa->data <= pb->data)
		{
			pc->next = pa; // 将pa所指结点链接到pc所指结点之后
			pc = pa;       // pc指向pa
			pa = pa->next; // pa指向下一节点
		}
		else             // “摘取”pb所指结点
		{
			pc->next = pb; // 将pb所指结点链接到pc所指结点之后
			pc = pb;       // pc指向pb
			pb = pb->next; // pb指向下一节点
		}
	}
	pc->next = pa ? pa : pb; // 将非空表的声誉段插入到pc所指节点之后
	delete LB;               // 释放LB的头节点
}
多项式相加
// 多项式加法:Pa = Pa + Pb,利用两个多项式的结点构成“和多项式”
void AddPolyn(Polynomial &Pa, Polynomia &Pb)
{
	p1 = Pa->next; // p1和p2初值分别指向Pa和Pb的首元结点
	p2 = Pb->next; 
	p3 = Pa;       // p3指向和多项式的当前节点,初值为Pa
	while (p1 && p2) // p1和p2均非空
	{
		if (p1->expn == p2->expn) // 指数相等
		{
			sum = p1->coef + p2->coef; // sum保存两项系数和
			if (sum != 0)     // 系数和不为0
			{
				p1->coef = sum; // 修改Pa当前节点的系数值为两项系数的和
				p3->next = p1;
				p3 = p1;        // 将修改后的Pa当前结点链在p3之后,p3指向p1
				p1 = p1->next;  // p1指向后一项
				r = p2; p2 = p2->next; delete r; // 删除Pb为当前结点,p2指向后一项
			}
			else
			{
				r = p1; p1 = p1->next; delete r; // 删除Pa当前节点,p1指向后一项
				r = p2; p2 = p2->next; delete r; // 删除Pb当前节点,p2指向后一项
			}
		}
		else if (p1->expn < p2->expn) // Pa当前结点的指数值小
		{
			p3->next = p1; // 将p1链在p3之后
			p3 = p1;       // p3指向p1
			p1 = p1->next; // p1指向后一项
		}
		else             // Pb当前结点的指数值小
		{
			p3->next = p2; // 将p2链在p3之后
			p3 = p2;       // p3指向p2
			p2 = p2->next; // p2指向后一项
		}
	} // end while
	pe->next = p1 ? p1 : p2; // 插入非空多项式的剩余段
	delete Pb;               // 释放Pb的头结点
}

顺序栈的存储结构

#define MAXSIZE 100 // 顺序栈存储空间的初始分配量
typedef struct
{
	SElemType *base; // 栈底指针
	SElemType *top;  // 栈顶指针
	int stcksize;    // 栈可用的最大容量
}SqStack;

顺序栈初始化

StatusInitStack(SqStack &S)
{
	S.base = new SElemType[MAXSIZE]; // 为顺序栈动态分配一个最大容量为 MAXSIzE 的数组空间
	if (!S.base) exit(OVERFLOW);     // 存储分配失败
	S.top = S.base;                  // top初始为base,空栈
	S.stacksize = MAXSIZE:           // stacksize 置为栈的最大容量 MAXSIZE
	return OK;
}

顺序栈的入栈

// 插人元素e为新的栈顶元素
StatusPush(SqStack &S, SElemType e)
{
	if(S.top-S.base == S.stacksize) return ERROR; // 栈满
	*S.top++ = e; // 元素e压人栈顶,栈顶指针加1
	return OK:
}

顺序栈的出栈

// 删除S的栈顶元素,用e返回其值
StatusPop(SqStack &S, SElemTyp e)
{
	if (S.top == S.base) return ERROR; // 栈空
	e = *--S.top; // 栈顶指针减 1,将栈顶元素賦给e
	return OK;
}

顺序栈取栈顶元素

// 返回S的栈顶元素,不修改栈顶指针
ElemTypeGetTop(SqStackS)
{
	if (S.top != S.base) // 栈非空
	return *(S.top - 1); // 返回栈顶元素的值,栈顶指针不变
}

链式栈的初始化

// 构造一个空栈S,栈顶指针置空
Status InitStack(LinkStack &S)
{
	S = NULL;
	return OK;
}

链式栈入栈

// 在栈顶插人元素 e
StatusPush (LinkStack &S, SElemType e)
{
	p = new StackNode; // 生成新结点
	p->data = e; // 将新结点数据域置为e
	p->next = S; // 将新结点插人栈顶
	S = p;       // 修改栈顶指针为p
	return OK;
}

链式栈出栈

// 删除S的栈顶元素,用e返回其值
Status Pop (LinkStack &S, SElemType &e)
{
	if(S == NULL) return ERROR; // 栈空
	e = S->data; // 将栈顶元素赋给 e
	p = S;       // 用p临时保存栈顶元素空间,以备释放
	S = S->next; // 修改栈顶指针
	delete p;    // 释放原栈顶元素的空间
	return OK;
}

链式栈取栈顶元素

// 返回S的栈顶元素,不修改栈顶指针
SElemType GetTop (LinkStack S)
{
	if (S != NULL)  // 栈非空
	return S->data; // 返回栈顶元素的值,栈顶指针不变
}

队列:

循环队列的顺序存储结构

#define MAXQSIZE 100 // 队列可能达到的最大长度
typedef struct
{
	QElemType *base; // 存储空间的基地址
	int front; // 头指针
	int rear;  // 尾指针
}

循环队列的初始化

Status InitQueue (SqQueue &Q)
{
	Q.base = new QElemType[MAXQSIZE];
	if (!Q.base) exit(OVERFLOW); // 存储分配失败
	Q.front = Q.rear = 0;
	return OK;
}

求循环队列长度

int QueueLength (SqQueue Q)
{
	return (Q.rear - Q.front + MAXSIZE) % MAXQSIZE;
}

循环队列入队

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;
}

循环队列出队

Status DeQueue (SqQueue &Q, QElemType &e)
{
	if (Q.front == Q.rear) return ERROR; // 队空
	e = Q.base[Q.front];              // 保存队头元素
	Q.front = (Q.front+1) % MAXQSIZE; // 队头指针加1
	return OK;
}

取循环队列的队头元素

SElemType GetHead (SqQueue Q)
{
	if (Q.front != Q.rear)    // 队列非空
		return Q.base[Q.front]; // 返回队头元素的值,队头指针不变
}

链队存储结构:

typedef struct QNode
{
	QElemType data;
	struct QNode *next;
}QNode, *QueuePtr;
typedef struct
{
	QueuePtr front; // 队头指针
	QueuePtr rear;  // 队尾指针
}LinkQueue

链队初始化:

status InitQueue (LinkQueue &Q)
{
	Q.front = Q.rear = new QNode; // 生成新结点作为头结点, 队头和队尾指针指向此结点
	Q.front->next = NULL;         // 头节点的指针域置空
	return OK;
}

链队入队:

Status EnQueue (LinkQueue &Q, QElemType e)
{
	p = new QNode;
	p->data = e;      // 为入队元素分配结点空间,用指针p指向
	p->next = NULL; 
	Q.rear->next = p; // 将新节点放到队尾
	Q.rear = p;       // 新节点作为新的队尾指针
	return OK;
}

链队出队:

Status DeQueue (LinkQueue &Q, QElemType &e)
{
	if (Q.front == Q.rear) return ERROR; // 若队列空,则返回ERROR
	p = Q.front->next; // p指向队头元素
	e = p->data;       // e保存队头元素的值
	Q.front->next = p->next;           // 修改头指针
	if (Q.rear == p) Q.rear = Q.front; // 最后一个元素被删,队尾指针指向头节点
	delete p; // 释放p
	return OK;
}

链队取队头元素的值:

SelemType GetHead (LinkQueue Q)
{
	if (Q.front != Q.rear) // 判空
		return Q.front->next->data; // 返回队头元素的值,队头指针不变
}

案例 - 数制的转换:

将一个十进制整数N转换为八进制数

void conversion (int N)
{
	InitStack(S); // 初始化空栈S
	while(N) // 当N非零时,循环
	{
		Push(S, N%8);
		N = N/8;
	}
	while (!StackEmpty(s)) // 当栈S非空时,循环
	{
		Pop(S, e); // 弹出栈顶元素e
		cout<<e;   // 输出e
	}
}

案例 - 括号的匹配:

// 表达式以 ‘#’ 结尾
Status Matching()
{
	InitStack(S);
	flag = 1;
	cin>>ch;
	while (!='#' && flag)
	{
		switch (ch)
		{
			case '[' || '(': // 如果是左括号,直接压入栈
				Push(S,ch);
				break;
			case ')':        // 如果是 ‘)’,则根据当前栈顶元素的值分情况考虑
				if (!StackEmpty(S) && GetTop(S) == '(') // 如果 栈非空 且 栈顶元素是 ‘(’ , 则匹配正确
					Pop(S,x);
				else flag = 0;
				break;
			case ']':        // 如果是 ‘]’,则根据当前栈顶元素的值分情况考虑
				if (!StackEmpty(S) && GetTop(S) == '[') // 如果 栈非空 且 栈顶元素是 ‘[’ , 则匹配正确
					Pop(S,x);
				else flag = 0;
				break;
		}
		cin>>ch;
	}
	if (StackEmpty(S) && flag) return true;
	else return false;
}

二叉树结点的存储表示

typedef struct BiTNode {
	TElemType data;                  // 结点数据域
	struct BiTNode *lchild, *rchild; // 左右孩子指针
}BiTNode, *BiTree

二叉树中序遍历

// 递归实现
void InOrderTraverse (BiTree T)
{
	if (T)
	{
		InOrderTraverse(T->lchild);
		cout<<T->data;
		InOrderTraverse(T->rchild);
	}
}

// 迭代实现
void InOrderTraverse (BiTree T)
{
	InitStack(S);
	p = T;
	q = new BiTNode;
	while (p || !StackEmpty(S))
	{
		if (p) // p非空
		{
			Push(S,p);   // 根指针进栈
			p = p->lchild; // 根指针进栈,遍历左子树
		}
		else   // p为空
		{
			Pop(S,q);      // 退栈
			cout<<q->data; // 访问根节点
			p = q->rchild; // 遍历右子树
		}
	} // end while
}

计算二叉树的深度

int Depth (BiTree T)
{
	if (T == NULL) return 0;
	else
	{
		m = Depth(T->lchild);
		n = Depth(T->rchild);
		if (m>n) return m+1;
		else return n+1;
	}
}

统计二叉树中节点的个数

int NodeCount (BiTree T)
{
	if (T==NULL) return 0;
	else return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;
}

B-树和B+树的区别

  1. B+树内节点不存储数据,所有 data 存储在叶节点导致查询时间复杂度固定为 log n。而B-树查询时间复杂度不固定,与 key 在树中的位置有关,最好为O(1)。
  2. B+树叶节点两两相连可大大增加区间访问性,可使用在范围查询等,而B-树每个节点 key 和 data 在一起,则无法区间查找。
  3. B+树更适合外部存储。由于内节点无 data 域,每个节点能索引的范围更大更精确

字符串匹配

BF算法(暴力)

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

KMP

来源:https://blog.csdn.net/m0_60598323/article/details/123696693

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

void my_next(int* next,int n,const char* p)
{
	int j = 0, k = -1;
	next[0] = -1;
	while(j<n)
	{
		if (k == -1 || p[j] == p[k])
		{
			next[j + 1] = k + 1;
			j++;
			k++;
		}
		else
		{
			k = next[k];
		}
	}
}

int kmp(const char* str1, const char* str2)
{
	int i = 0, j = 0;
	int len = (int)strlen(str2);
	// next数组
	int* next = (int*)malloc(len * sizeof(int));
	assert(next);
	my_next(next,len-1,str2);
	while (str2[j])
	{
		if(j==-1||str1[i] == str2[j])
		// j为-1时该位置下的i不会匹配成功,进入下一次匹配
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];//j进行回退
		}
		if (str1[i] == '\0')
		{
			free(next);
			next = NULL;
			return -1;
		}
	}
	free(next);
	next = NULL;
	return i;
}

int main()
{
	char arr[] = "abaabcdabcab";
	char brr[] = "ef";
	printf("%d\n",kmp(arr, brr));
	return 0;
}

散列表

散列表处理冲突的办法

  1. 线性探测法

    发生冲突时,从冲突地址的下一单元顺序寻找空单元

  2. 二次探测法

    d i = 1 2 , − 1 2 , 2 2 , − 2 2 , . . . , k 2 , − k 2 ( k < = m / 2 ) d_i = 1^2,-1^2,2^2,-2^2,...,k^2,-k^2(k<=m/2) di=12,12,22,22,...,k2,k2(k<=m/2)

  3. 伪随机探测法

    (插入位置 + 随机数)% 数组长度 = 要插入位置

二分查找

void Search_Bin(SSTsble ST, KeyType key)
{
	low = l; high = ST.length;
	while (low <= high)
	{
		mid = (low + high) / 2;
		if (key == ST.R[mid].key) return mid;
		else if (key > ST.R[mid].key) low = mid + 1;
		else high = mid - 1;
	}
	return 0;
}

参考:
https://blog.csdn.net/qq_53436105/article/details/127296815
https://blog.csdn.net/weixin_52811588/category_11973986.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Geek-Banana

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

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

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

打赏作者

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

抵扣说明:

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

余额充值