Data Structure

线性表

链表

链表中对于指针的使用,修改的是其所指向的值的更改,更改后要移动指针

//waiting找到好的题目再更

  • 求合法出栈序列(卡特兰数)
    设序列长度为n
    则合法序列中 进栈次数为n,出栈次数为n
    但是在C(n,2n)中还应该使得出栈操作前栈不为空,即当前出栈次数<=入栈次数
    设出栈为-1,入栈为+1
    通过映射不合法序列使得序列变为n-1个-1,n+1个+1

映射规则为当前前缀和为-1时,将当前位置起的数字一一取反

序列① -1 -1 1 1
序列② 1 -1 1 1

那么合法序列则为

C(n,2n)-C(n-1,2n)=C(n,2n)/(n+1)
  • 中序表达式转为后序表达式
    遇到字母直接输出
    入栈条件:遇到运算符若栈为空或栈顶运算符优先级小于当前运算符
    出栈条件:若当前运算符优先级小于栈顶则栈顶出栈直到满足入栈条件
    若遇见括号’(‘则直接入栈,当遇到’)‘时将’(‘到’)'之间的符号依次出栈(包括括号)

将四则运算(中序)式子转为后序,在计算时对于括号嵌套能较好处理

在中序转为后序时,符号的优先级判断: 乘除号优先级最高则直接入栈,加减号则需要将栈排空或在遇到'('时再入栈 这里的'('相当于一个新的栈底(因为要将括号内式子先计算)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
using namespace std;

stack<char>ope;
stack<double>val;//数值类型
void PostOrder(char s[], char t[]) {
	int j = 0, len = strlen(s);
	for (int i = 0; i<len; i++) {
		if (s[i] >= '0'&&s[i] <= '9') {
			while (s[i] >= '0'&&s[i] <= '9') {
				t[j++] = s[i++];
			}
			i -= 1;
			t[j++] = '#';
		}
		else if (s[i] == '(') {
			ope.push(s[i]);
		}
		else if (s[i] == ')') {
			char k = ope.top();
			while (k != '(') {
				t[j++] = k;
				ope.pop();
				k = ope.top();
			}
			ope.pop();
		}
		else if (s[i] == '+' || s[i] == '-') {
			while (!ope.empty() && ope.top() != '(') {//此时已经对优先级进行比较
				t[j++] = ope.top();
				ope.pop();
			}
			if (s[i] == '+')ope.push('+');
			else ope.push('-');
		}
		else if (s[i] == '*')ope.push('*');
		else if (s[i] == '/')ope.push('/');
	}

	while (!ope.empty()) {
		t[j++] = ope.top();
		ope.pop();
	}
	t[j] = '\0';
}
void get_num(char t[], int& i) {
	double num = 0;
	while (t[i] >= '0'&&t[i] <= '9') {
		num *= 10;
		num += t[i] - '0';
		i++;
	}
	i -= 1;
	val.push(num);
}
void cal(char t[], int &i) {
	double num1 = val.top();
	val.pop();

	double num2 = val.top();
	val.pop();

	if (t[i] == '+') {
		num2 += num1;
	}
	else if (t[i] == '-') {
		num2 -= num1;
	}
	else if (t[i] == '*') {
		num2 *= num1;
	}
	else if (t[i] == '/') {
		num2 /= num1;
	}
	val.push(num2);
}

void result(char t[]) {
	int len = strlen(t);
	for (int i = 0; i<len; i++) {
		if (t[i] >= '0'&&t[i] <= '9') {
			get_num(t, i);
		}
		else if (t[i] == '#')continue;
		else {
			cal(t, i);
		}
	}
}
int main() {
	char s[100] = "9+(3-2*(11-5/10)*2)+3+10/2";
	char t[100];
	PostOrder(s, t);
	cout << "后缀: " << t << endl;// 9#3#2#3#5#-2#**-3#*+10#2#/+   '#'用来间隔数字 
	result(t);
	cout << val.top();
	val.pop();
	return 0;
}

队列

循环队列

插入时(rear+1)%LEN
删除时(front+1)%LEN
循环队列的插入和删除都是+1
计算长度当前LEN:(rear-front+LEN)%LEN

队列应用

对于队列简易的封装以及对于模板类简单的使用

/*  左上角开始向右下搜索
aaaaaaaaaa
aaaaaaaaaa
aa########
aaaaaaaaaa
aaaaaaaa*a
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
*/


#include<iostream>
#include<cstdio>
#define n 10 

using namespace std;

int vis[n][n] = { 0 };
char map[n][n];

template<typename ElemType>
struct queue {
	int capacity;
	int head;
	int tail;
	ElemType* items;

	queue(int c = 100) {
		capacity = c;
		head = 0;
		tail = 0;
		items = new ElemType[c];
	}
	void push(ElemType elem) {
		items[tail] = elem;
		tail++;
	}
	bool empty() {
		if (!tail)return true;
		return false;
	}
	void  pop() {
		for (int i = head; i<tail; i++)
			items[i] = items[i + 1];
		tail--;
	}
	ElemType front() {
		return items[head];
	}
};


struct node {
	int x, y;
	int cnt = 0;
};
queue<node> q;

int func(node b) {
	int xx[4] = { 0,1 };
	int yy[4] = { 1,0 };
	int flag = 0;
	node a;

	q.push(b);

	while (!q.empty() && !flag) {
		a = q.front();
		q.pop();
		int x1, y1;
		for (int i = 0; i < 2; i++) {
			x1 = a.x + xx[i], y1 = a.y + yy[i];
			if (map[x1][y1] == '*') {
				a.cnt++;
				flag = 1;
				break;
			}
			if (!vis[x1][y1] && x1<10 && y1<10 && map[x1][y1] != '#') {
				vis[x1][y1] = 1;
				q.push(node{ x1,y1,a.cnt + 1 });
			}
		}
	}
	if (flag)
		return a.cnt;
	else return -1;
}


int main() {
	memset(map, 'a', sizeof(map));
	map[4][8] = { '*' };
	for (int i = 2; i < 3; i++) {
		for (int j = 2; j < 10; j++)
			map[i][j] = '#';
	}
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cout << map[i][j];
			
		}
		cout << endl;
	}
	node b;
	b.x = b.y = b.cnt = 0;
	int cnt = func(b);
	if (cnt == -1)cout << "没有联通路径";
	else cout << "存在路径长度为 num:" << cnt << endl;
	return 0;
}
优先队列

//waiting

字符串匹配

KMP

KMP的匹配规则与暴力不同之处:
在匹配错误时暴力会直接返回到起始处的后一位字符在进行匹配
kmp则可以避免不必要的"重蹈覆辙"

其中next数组就是kmp的核心,简要理解为最长相同前后缀 子串

字符串 a b c a b
next数组 -1 0 0 1 2

其中next[0]取-1用以区别头结点和其他节点,避免计数错误

#include<cstdio>
#include<iostream>
#include<cstring>

#define maxn 100010 

using namespace std;

//注意题目数据类型,不一定是字符串!!! 
void cal_next(char*str,int nex[]) {           //算出str的前缀和后缀相同的长度 存入next中 
	nex[0] = -1;
	int i = 0, j = -1;

	while (i < strlen(p))
	{
		if (j == -1 || p[i] == p[j])
		{
			++i;
			++j;
			nex[i] = j;
		}	
		else
			j = nex[j];
	}

}

void kmp(char*str, char*ptr){
	int next[maxn];
	cal_next(ptr,next);
		int slen = strlen(str);
		int plen = strlen(ptr);
		int k = -1;
		int mark = 0;
		for (int q = 0; q < slen; q++) {
			while (k > -1 && ptr[k + 1] != str[q]) 
				k = next[k];       //字符串当前位置不相同且有相同匹配的时候,回溯 
			
			if (ptr[k + 1]==str[q]) 
				k++;     //相同则继续比较 
			if (k== plen - 1) {
				printf("%d\n", q - k + 1);     // 位置的输出,从1开始 
				  k=next[k];    //得出匹配字符串,位置回到含有相同前缀的点开始比较 
			}
		}
		//  输出子串的next 
		for (int i = 0; i <strlen(ptr); i++) 
			printf("%d ", next[i] + 1);
		printf("\n");	
}

int main() {
	char a[maxn],b[maxn];
	gets_s(a);
	gets_s(b);
	kmp(a,b);
	return 0;
	}

*由前序序列和中序序列求后序
①由先序可知第一个字母为跟节点,
②再看其在中序中的位置,以当前字母为中心,左边则为左子树所含字母,右边则为右子树所含字母
③重复上述操作直到结束(先序可确定父节点)
例题


//waiting

最短路

void minPath(ALGraph G, int start, int dis[], int path[][100]) {
	int i, j, mindist, k, t;
	Road *now;

	for (int i = 0; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			path[i][j] = -1;
		}
	}

	for (i = 0; i <G.vexnum; i++) {    //初始化最短路径数组dis,和标记此顶点是否已经找到最短路径的path[i][0],等于0表示没有找到,等于1表示找到.
		dis[i] = INT_MAX;
		path[i][0] = 0;
	}
	now = G.vertices[start].firstarc;//起点的邻接表
	while (now) {                                  //保存起点到和其相邻点的路径.
		dis[now->spotId] = now->cost;
		path[now->spotId][1] = start;
		path[now->spotId][2] = now->spotId;
		path[now->spotId][3] = -1;
		now = now->nextarc;
	}

	path[start][0] = 1;               //源点标记为为1,此顶点以后不会再用到
	cout << endl;
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) {
			cout << path[i][j] << " ";
		}
		cout << endl;
	}
	cout << "path [] []" << endl;
	for (i = 1; i <G.vexnum; i++) {                //选择最最短的路径
		mindist = INT_MAX;//从已选集合中选择未选集合 相邻的最短路径
		for (j = 0; j < G.vexnum; j++) {
			if (!path[j][0] && dis[j] < mindist) { //所选集合点到当前点的
				k = j;								
				mindist = dis[j];
			}
		}
		if (mindist == INT_MAX) {         //如果没有找到最短的路径,则说明从此源点不能到任何其他顶点,直接返回.
			return;
		}
		path[k][0] = 1;                  //标记找到最小路径的顶点,此顶点以后不会再用到.
		now = G.vertices[k].firstarc; // 假设将所选点的邻接表中的点加入集合
		while (now) {
			if (!path[now->spotId][0] && dis[now->spotId] > dis[k] + now->cost) {//当前点选择后路径小于之前的(松弛操作)

				dis[now->spotId] = dis[k] + now->cost;//更新
				t = 1;
				while (path[k][t] != -1)      //记录最新的路径
				{

					path[now->spotId][t] = path[k][t];//将已选点集合的最短路径复制到最新的点的path中
					t++;
				}

				path[now->spotId][t] = now->spotId; //记录终点
				path[now->spotId][t + 1] = -1;                       //path[i][t+1]之前的都是最短路径所要经过的顶点,从t+1这里停止,作为最后输出路径的判断条件
			}
			now = now->nextarc;
		}
	}
}
Site construction by John Doe usinghexo blog framework.
Home