算法学习(5) 数据结构:用数组实现单链表/栈/队列/单调栈/单栈队列/KMP/kmp统计字串

1、数组模拟单链表

例题:826. 单链表

https://www.acwing.com/problem/content/828/
算法学习(5) 数据结构:用数组实现单链表/栈/队列/单调栈/单栈队列/KMP/kmp统计字串_第1张图片

输入样例:
10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6
输出样例:
6 4 6 5
#include
using namespace std;

const int N = 100010;

// head 头结点下标,e[i]表示结点i的值
// ne[i]表示下一个结点的下标,idx表示当前已经用到了哪个点
int head, e[N], ne[N],idx=0;

//初始化
void init() {
	head = -1;
	idx = 0;
}

// 插入头节点  “H x”,表示向链表头插入一个数x。
void add_to_head(int x) {
	e[idx] = x;
	ne[idx] = head;
	head = idx;
	idx++;
}


// 插入指定结点后 “I k x”,表示在第k个输入的数后面插入一个数x(此操作中k均大于0)。
void insert(int k, int x) {
	e[idx] = x;
	ne[idx] = ne[k];
	ne[k] = idx;
	idx++;
}

// 删除k结点后一个点, “D k”,表示删除第k个输入的数后面的数(当k为0时,表示删除头结点)。
void remove(int k) {
	ne[k] = ne[ne[k]];
}

int main() {
	init();
	int m;
	cin >> m;
	while (m--) {
		char a;
		int k, x;
		cin >> a;
		if (a == 'H') {
			cin >> x;
			add_to_head(x);
		}
		else if (a == 'I') {
			cin >> k >> x;
			insert(k-1, x);
		}
		else {
			cin >> x;
			if (!x)
				head = ne[head];
			remove(x-1);
		}
	}
	for (int i = head; i != -1; i = ne[i])
		cout << e[i] << " ";
}


例题:827. 双链表

算法学习(5) 数据结构:用数组实现单链表/栈/队列/单调栈/单栈队列/KMP/kmp统计字串_第2张图片

数据范围
1≤M≤100000
所有操作保证合法。

输入样例:
10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2
输出样例:
8 7 7 3 2 9
#include
using namespace std;

const int N = 100010;

// head 头结点下标,e[i]表示结点i的值
// ne[i]表示下一个结点的下标,idx表示当前已经用到了哪个点
int head, e[N], l[N],r[N],idx=0;

// 初始化
void init() {
	// 0 表示左端点,1表示右端点
	r[0] = 1, l[1] = 0;
	idx = 2;
}

// 第k个点右边插入一个点
void IR(int k, int x) {
	e[idx] = x;

	r[idx] = r[k];
	l[idx] = k;

	l[r[k]] = idx;
	r[k] = idx;
	idx++;
}

// 第k个点左边插入一个点
void IL(int k, int x) {
	e[idx] = x;

	l[idx] = l[k];
	r[idx] = k;

	r[l[k]] = idx;
	l[k] = idx;
	idx++;
}

// 删除第k个结点
void remove(int k) {
	r[l[k]] = r[k];
	l[r[k]] = l[k];
}

//  “L x”,表示在链表的最左端插入数x。
void L(int x) {
	e[idx] = x;

	l[idx] = 0;
	r[idx] = r[0];

	r[0] = idx;
	l[r[idx]] = idx;

	idx++;
}

//“R x”,表示在链表的最右端插入数x。
void R(int x) {
	e[idx] = x;

	r[idx] = 1;
	l[idx] = l[1];
	
	r[l[idx]] = idx;
	l[1] = idx;

	idx++;
}

int main() {
	ios::sync_with_stdio(false);
	init();
	int m; cin >> m;
	string opt; 
	int k, x;
	while (m--) {
		cin >> opt;
		if(opt== "L"){
			cin >> x;
			L(x);
		}
		else if (opt == "R") {
			cin >> x;
			R(x);
		}
		else if (opt == "D") {
			cin >> k;
			remove(k+1);
		}
		else if (opt == "IL") {
			cin >> k >> x;
			IL(k+1, x);
		}
		else if (opt == "IR") {
			cin >> k >> x;
			IR(k+1, x);
		}
	}
	for (int i = r[0]; i != 1; i = r[i]) {
		cout << e[i] << " ";
	}
}
/*
(1) “L x”,表示在链表的最左端插入数x。

(2) “R x”,表示在链表的最右端插入数x。

(3) “D k”,表示将第k个插入的数删除。

(4) “IL k x”,表示在第k个插入的数左侧插入一个数。

(5) “IR k x”,表示在第k个插入的数右侧插入一个数。

输入样例:
10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2
输出样例:
8 7 7 3 2 9
*/

2、数组模拟栈

#include
using namespace std;
const int N = 10010;
int stk[N], stt=0;

//=========  栈 ==================

// 插入
void push(int x) {
	stk[++stt] = x;
}

// 弹出
void pop() {
	stt--;
}

//判断是否为空
bool isempty() {
	return stt > 0;
}

//栈顶
int top() {
	return stk[stt];
}

3、数组模拟队列

#include
using namespace std;
const int N = 10010;

int q[N], hh, tt = -1;

// 插入
void push(int x) {
	q[++tt] = x;
}
//弹出
void pop() {
	hh++;
}
// 判断是否为空
bool isEmpty() {
	return hh > tt;
}

int top() {
	return q[hh];
}

4、单调栈

算法学习(5) 数据结构:用数组实现单链表/栈/队列/单调栈/单栈队列/KMP/kmp统计字串_第3张图片

思路:

最小值和最大值分开来做,两个for循环完全类似,都做以下四步:

解决队首已经出窗口的问题;
解决队尾与当前元素a[i]不满足单调性的问题;
将当前元素下标加入队尾;
如果满足条件则输出结果;
需要注意的细节:

上面四个步骤中一定要先34,因为有可能输出的正是新加入的那个元素;
队列中存的是原数组的下标,取值时要再套一层,a[q[]];
算最大值前注意将hh和tt重置;
此题用cout会超时,只能用printf;
hh从0开始,数组下标也要从0开始。
#include
using namespace std;

const int N = 100010;
int stk[N], tt = 0;

int main() {
	cin.tie(0);	//通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
	ios::sync_with_stdio(false);
	int n; cin >> n;
	for (int i = 0; i < n; i++) {
		int  x;
		cin >> x;
		while (tt && stk[tt] >= x)
			tt--;
		if (tt)
			cout << stk[tt] << ' ';
		else
			cout << -1 << ' ';
		stk[++tt] = x;
	}
}

关于ios::sync_with_stdio(false);和 cin.tie(0)加速c++输入输出流

5、单栈队列

https://www.acwing.com/problem/content/156/
算法学习(5) 数据结构:用数组实现单链表/栈/队列/单调栈/单栈队列/KMP/kmp统计字串_第4张图片

您的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

输入格式
输入包含两行。

第一行包含两个整数n和k,分别代表数组长度和滑动窗口的长度。

第二行有n个整数,代表数组的具体数值。

同行数据之间用空格隔开。

输出格式
输出包含两个。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
#include
using namespace std;

const int N = 1e6+10;

/*
	输出格式
	输出包含两个。
	第一行输出,从左至右,每个位置滑动窗口中的最小值。
	第二行输出,从左至右,每个位置滑动窗口中的最大值。
	输入样例:
	8 3
	1 3 -1 -3 5 3 6 7
	输出样例:
	-1 -3 -3 -3 3 3
	3 3 5 5 6 7	
*/
int n, k;

int a[N], q[N];// a是真实输入的数组,q是队列
int hh, tt = -1;//队头hh、队尾tt
int main() {
	//cin >> n >> k;
	scanf_s("%d%d", &n,&k);
	for (int i = 0; i < n; i++)		
		scanf_s("%d", &a[i]);
	// 求滑动窗口内最小
	for (int i = 0; i < n; i++) {

		// 判断队头是否滑出窗口,作用:维持滑动窗口的大小
		//当队列不为空(hh <= tt) 且 当当前滑动窗口的大小(i - q[hh] + 1)>我们设定的
		//滑动窗口的大小(k),队列弹出队列头元素以维持滑动窗口的大小
		if (hh <= tt && i - k + 1 >q[hh])		hh++;

		//当队列不为空(hh <= tt) 且 当队列队尾元素>=当前元素(a[i])时,那么队尾元素
	   //就一定不是当前窗口最小值,删去队尾元素,加入当前元素(q[ ++ tt] = i)
		while (hh <= tt && a[q[tt]] >= a[i])	tt--;	// 若队尾不单调,tt减1,队列中存的是原数组的下标,取值时要再套一层,a[q[]]
		q[++tt] = i;
		 
		if (i >= k - 1) printf("%d ", a[q[hh]]);
	}

	printf("\n");	
	hh = 0, tt = -1;
	// 求滑动窗口内最大
	for (int i = 0; i < n; i++) {
		// 判断队头是否滑出窗口
		if (hh <= tt && i - k + 1 > q[hh])		hh++;

		while (hh <= tt && a[q[tt]]<=a[i])	tt--;	// 若队尾不单调,tt减1

		q[++tt] = i;

		if (i >= k - 1) printf("%d ", a[q[hh]]);
	}
}

6、字符串匹配算法–kMP

KMP字符串匹配算法1

KMP字符串匹配算法1(理论)链接:
https://www.bilibili.com/video/BV1Px411z7Yo/?spm_id_from=333.788.b_7265636f5f6c697374.5

KMP字符串匹配算法2

推荐博客学习:从头到尾彻底理解

例题:28. 实现 strStr()
算法学习(5) 数据结构:用数组实现单链表/栈/队列/单调栈/单栈队列/KMP/kmp统计字串_第5张图片

#include
#include
using namespace std;
class Solution {
public:
    int strStr(string haystack, string needle) {
        int* Next = new int[needle.length()];
        int res =  kmpSearch(haystack, needle,Next);
        delete []Next;
        return res;
    }
private:
    void getNextVal(string p,int Next[]) {
        int pLen = p.length();
        Next[0] = -1;
        int k = -1, j = 0;  
        while (j < pLen - 1) {
            // p[k] 表示前缀,p[j]表示后缀
            if (k == -1 || p[k] == p[j]) {
                ++j;
                ++k;
                if (p[j] != p[k]) {
                    Next[j] = k;
                }
                else {
                    //因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
                    Next[j] = Next[k];
                }
            }
            else {
                k = Next[k];
            }
        }
    }
    int kmpSearch(string s,string p,int  Next[]) {
        if (p.length() == 0) return 0;
        getNextVal(p, Next);
        int sLen = s.length(), pLen = p.length();
        int i = 0, j = 0;
        while (i < sLen && j < pLen) {
            //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++   
            if( -1 == j || s[i] == p[j]) {
                i++;
                j++;
            }
            else {
                //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]   
                //next[j]即为j所对应的next值      
                j = Next[j];
            }
        }
        if (j == pLen)
            return i - j;
        else
            return -1;
    }
};

算法学习(5) 数据结构:用数组实现单链表/栈/队列/单调栈/单栈队列/KMP/kmp统计字串_第6张图片

你可能感兴趣的:(数据结构)