数据结构复习.机考篇(c++)(严蔚敏版)

1.顺序表(可用vector代替)
插入
注意点:
1.插入前要判断插入位置是否合法
2.判断位置合法时注意可插位置为size+1,因为还可以插在尾部
3.插入时将插入位置后面的元素往后移动,建议用 (i- -)

void insert(int pos, int va)
	{
		pos--;
		if (pos >= 0&& pos <= size)
		{
			for (int i = size; i > pos; i--)
			{
				data[i] = data[i - 1];
			}
			data[pos] = va;
			size++;
			show();
		}
		else
		{
			cout << "error" << endl;
		}
	}

删除、查找
删除位置时注意判断合法性(这里可删位置为size注意与插入区别)
删除对应元素时先查找该元素是否存在

void shan(int pos)
	{
		pos--;
		if (pos >= 0 && pos < size)
		{
			for (int i = pos; i < size; i++)
			{
				data[i] = data[i + 1];
			}
			size--;
			show();
		}
		else
		{
			cout << "error" << endl;
		}

	}


int find(int x)
	{
		for (auto i : data)
		{
			if (x == data[i])
				return 1;
		}
		return 0;
	}

vector版
注意传的参数第一个是迭代器


	
	
	void insert(int pos, int va)
	{
		pos--;
		auto it = data.begin();
		if (pos >= 0&& pos <= data.size())
		{
			data.insert(it+pos, va);
			show();
		}
		else
		{
			cout << "error" << endl;
		}
	}
	void shan(int pos)
	{
		pos--;
		auto it = data.begin();
		if (pos >= 0 && pos <data.size())
		{
			data.erase(it + pos);
			show();
		}
		else
		{
			cout << "error" << endl;
		}

	}
	


2.链表
插入、删除、查找
与顺序表几乎一样,只是改成指针操作,插入删除时需要找到上一个节点,注意加头节点。

#include<bits/stdc++.h>
using namespace std;
class node
{public:
	int data;
	node* next;

	node(int a, node* b)
	{
		data = a;
		next = b;
	}

};
class lian
{
	node* root;
	int size;
public:
	lian()
	{
		root = new node(-1,NULL);

	}
	void cr()
	{
		node* p = root;
		int t;
		cin >> t;
		size = t;
		while (t--)
		{
			int m;
			cin >> m;
			node* s = new node(m, NULL);
			p->next = s;
			p = p->next;
		}show();
	}
	void show()
	{
		node* p = root->next;
		while (p)
		{
			cout << p->data << " ";
			p = p->next;
		}cout << endl;
	}
	void in(int pos, int val)
	{
		pos--;
		if (pos >= 0 && pos <= size)
		{
			size++;
			node* p = root;
			while (pos--)
			{
				p = p->next;
			}
			node* s = new node(val, NULL);
			s->next = p->next;
			p->next = s;
			show();
		}
		else
		{
			cout << "error\n";
		}
	}
	void shan(int pos)
	{
		pos--;
		if (pos >= 0 && pos < size)
		{
			node* p = root;
			while (pos--)
			{
				p = p->next;
			}
			p->next = p->next->next;
			size--;
			show();
		}
		else
		{
			cout << "error\n";
		}
	}
	int find(int pos)
	{
		pos--;
		if (pos >= 0 && pos < size)
		{
			return 1;
		}
		return 0;
	}
	int getdata(int pos)
	{
		
		node* p = root;
		while (pos--)p = p->next;
		return p->data;
	}
};
int main()
{
	lian p;
	p.cr();
	int x, y;
	cin >> x >> y;
	p.in(x, y);
	cin >> x >> y;
	p.in(x, y);

	cin >> x;
	p.shan(x);
	cin >> x;
	p.shan(x);

	cin >> x;
	if(p.find(x))
	cout<<p.getdata(x)<<endl;
	else
	{
		cout << "error\n";
	}
	cin >> x;
	if (p.find(x))
		cout << p.getdata(x) << endl;
	else
	{
		cout << "error\n";
	}



}

list代码和vector一样,只是没有[]运算符,需要用*(it+x)的方法操作迭代器输出,遍历方法如下

for (auto i : p)
	{
		cout << i << " ";
	}cout << endl;

3.二叉树
创建,前中后序遍历,层次遍历,求树的深度
创建:写一个递归函数,用递归创建,注意要加&,因为要改变指针的值
层次遍历:用队列,先把树根压进去,然后每弹出一个元素就把该元素的左右孩子压入队列
四种遍历都要注意判断当前指针是否为空,在本代码中为注意加 if(gs)

/*输入数据
先输入测试次数,接着输入先序遍历
建树方法采用“先序遍历+空树用0表示”
2
AB0C00D00
ABCD00E000FG00H0I00
*/
#include<bits/stdc++.h>
using namespace std;
class node
{
public:
	node* re;
	node* ri;
	char data;

};
class shu
{
	node* root;
public:
	shu()
	{
		root = NULL;
	}
	void cr()
	{
		cr(root);
	}
	void cr(node*& gs)
	{
		char a;
		cin >> a;
		if (a == '0')
			return;
		gs = new node;
		gs->re = NULL;
		gs->ri = NULL;
		gs->data = a;
		cr(gs->re);
		cr(gs->ri);

	}
	void qian()
	{
		qian(root);
		cout << endl;
	}
	void qian(node* gs)
	{
		if (gs)
		{
			cout << gs->data;
			qian(gs->re);
			qian(gs->ri);
		}
	}
	void hou()
	{
		hou(root);
		cout << endl;
	}
	void hou(node* gs)
	{
		if (gs)
		{

			hou(gs->re);
			hou(gs->ri);
			cout << gs->data;
		}
	}
	void zx()
	{
		zx(root);
		cout << endl;
	}
	void zx(node* gs)
	{
		if (gs)
		{

			zx(gs->re);
			cout << gs->data;
			zx(gs->ri);
		}
	}
	void cc()
	{
		queue<node*> a;
		a.push(root);
		node* p = root;
		while (!a.empty())
		{
			p = a.front();
			if (p) {
				a.push(p->re);
				a.push(p->ri);
				cout << a.front()->data;
			}

			a.pop();

		}
	}
};
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		shu p;
		p.cr();
		p.cc();
		cout << endl;
	}
}

求树的深度

int hi()
	{
		return hi(root);
	}
	int hi(node* gs)
	{

		return gs == nullptr ? 0 : max(hi(gs->re), hi(gs->ri)) + 1;

	}

哈夫曼树的创建,使用优先队列,注意优先队列不要用在类中重载<的方法,因为队列中的元素是指针不是对象,比较的时候不是用该重载方法,而是直接比较指针地址。
本错误参考:https://www.cnblogs.com/Hush/archive/2004/03/19/3679.html

优先队列:(1.重新写一个类用来比较,类中重载()运算符 2.声明优先队列时,第一个参数为元素类型,第二个参数为对应元素类型的容器,第三个参数为重新写的比较类)

struct phNodeComp
{

	bool operator () (node* left, node* right)
	{

		return left->wei > right->wei;
	}
};

priority_queue<node*, vector<node*>, phNodeComp>p;

完整代码

/*输入数据
第一行测试次数

第2行:第一组测试数据的字符个数n,后跟n个字符

第3行:第一组测试数据的字符权重*/

#include<bits/stdc++.h>
using namespace std;
class node
{
public:
	int wei;
	string ma;
	char data;
	node* le;
	node* ri;


	node(int a, char b) :wei(a), data(b)
	{
		le = NULL;
		ri = NULL;
	}
	/*bool operator<(const node*& a)const
	{
		if (wei >= a->wei)
			return true;
		else
		{
			return false;
		}
	}*/
};//不要使用屏蔽代码,会有问题
struct phNodeComp
{

	bool operator () (node* left, node* right)
	{

		return left->wei > right->wei;
	}
};
class hum
{
	node* root;
public:
	hum()
	{
		root = NULL;
	}
	void cr()
	{
		int n;
		cin >> n;
		char* a = new char[n + 1];
		int* b = new int[n];
		int i;
		for (i = 0; i < n; i++)
		{
			cin >> a[i];
		}
		for (i = 0; i < n; i++)
		{
			cin >> b[i];
		}
		priority_queue<node*, vector<node*>, phNodeComp>p;
		for (i = 0; i < n; i++)
		{
			node* m = new node(b[i], a[i]);
			p.push(m);
		}
		node* s = NULL;
		while (!p.empty())
		{
			node* x = p.top();
			p.pop();

			node* y = p.top();
			p.pop();
			s = new node(x->wei + y->wei, '#');
			s->le = x;
			s->ri = y;
			if (!p.empty())
				p.push(s);

		}
		root = s;

	}
	void setma()
	{
		setma(root,"");
	}
	void setma(node*gs,string b)//设置哈夫曼编码
	{
		if(gs){
		gs->ma = b;
		setma(gs->le, b + "0");
		setma(gs->ri, b + "1");
		}
	}
	
};
int main()
{
	int t;
	cin >> t;
	while (t--) {

		hum p;
		p.cr();
		p.setma();
		
	}
}
/*
1
5 A B C D E
-100 100 66 458 6999

*/

4.图
储存方式,创建,度,遍历,最小生成树,拓扑排序,关键路径,最小路径
储存方式:二维数组,邻接表
创建:注意有向无向,无向图的话两边都要存储,如AB之间存在无向边,则A要储存该边,B也要
度:二维数组时,出度入度看对应行列和,邻接表出度方便,入度要依次遍历各表来计算

//邻接表方法:
vector<int> *a=new vector<int> [n];

遍历方法:深搜广搜,都需要一个visit数组标识该元素是否被访问
深搜:一个遍历函数,一个搜索函数,二维数组形式

void dfs()
	{
		int i;
		for (i = 0; i < size; i++)
		{
			visit[i] = 0;
		}//先重置visit数组
		for (i = 0; i < size; i++)
		{
			if (visit[i] != 1)
			{
				dfs(i);
			}
		}//遍历每一个元素,保证都被访问
		cout << endl;
	}
	void dfs(int m)
	{
		visit[m] = 1;
		cout << m << " ";
		for (int i = 0; i < size; i++)
		{//先访问第一个与该元素存在边且未被访问过的元素,递归回来后再访问其他的
			if (visit[i] != 1&&ju[m][i])
				dfs(i);
		}
	}

广搜:类似树的层次遍历,需要一个队列

void bfs()
	{
		int i,j;
		for (i = 0; i < size; i++)
		{
			visit[i] = 0;
		}//重置visit数组
		queue<int> p;
		for (i = 0; i < size; i++)
		{//遍历每一个元素,如果没访问过就压入队列
			if (visit[i] != 1)
			{
				p.push(i);
				visit[i] = 1;
			}
			while (!p.empty())
			{
				int m=p.front();
				for (j = 0; j < size; j++)
				{
					if (visit[j] != 1 && ju[m][j])
					{//把与该元素存在边的元素全部装入队列
						visit[j] = 1;
						p.push(j);
					}
				}				
				cout << p.front() << " ";
				p.pop();
			}
		}cout << endl;

}

最小生成树
prim算法:1.需要一个开始位置,2.需要一个数组空间存放各边的最短权值,3.一个标识数组表示是否访问过
大致过程:1.先用开始位置初始化low_weight数组,有边就赋给边的权值,没边就赋给无穷大
2.找到low_weight数组中的最小值的下标,标识该位置访问过
3.由该位置重新赋值low_weight数组

void prim(int  start)//起始位置
{
	int* visit;
	bool* lowwei;
	for (int i = 0; i < num; i++)
	{
		if (ju[start][i])
			lowwei[i] = ju[start][i];
		else
			lowwei[i] = inf;

	}//用开始位置初始化low_weight数组
	visit[start] = false;
	int min = inf, index, j;
	for (int i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num; j++)
		{
			if (lowwei[j] < min && visit[j])
			{
				min = lowwei[j];
				index = j;
			}
		}
		visit[index] = false;//找到low_weight数组中的最小值的下标,标识该位置访问过
		for (j = 0; j < num;j++)
		{
			if (ju[index][j] < lowwei[j] && visit[j])
			{
				lowwei[j] = ju[index][j];
			}
		}//由该位置重新赋值low_weight数组
	}
}

Kruskal算法:
1.需要优先队列,2.需要一个数组表示元素所在的树
大致思路:1.先标志元素各自为一棵树,2.选出最小的边,把边两端元素标志为一棵树(取最大值或最小值),注意要把原来和另一个元素一棵树的元素也标志一遍

void kruskal()
	{
		priority_queue<bian> s;
		node_w = new int[num];
		for (int i = 0; i < num; i++)
		{
			node_w[i] = i;

		}//初始化,让他每一个结点都为一棵树
		for (int i = 0; i < bian_num; i++)
		{
			if (node_w[s.top().a] != node_w[s.top().b])
			{//如果不是同一棵树
				
				int m, n;
				m = min(node_w[s.top().a], node_w[s.top().b]);
				n = max(node_w[s.top().a], node_w[s.top().b]);
				node_w[s.top().a] = m;
				node_w[s.top().b] = m;//标志为同一棵树,这里是取最小
				for (int i = 0; i < num; i++)
				{
					if (node_w[i] == n)
					{
						node_w[i] = m;
					}
				}//把与最大的同一棵树的也标志一遍
			}
			s.pop();
		}
	}

拓扑排序:(不唯一,通常会指定顺序)
1.一个数组存储每一个元素的入度,2.一个栈记录入度为0的元素
大致思路:1.先求入度,2.把入度为0的元素入栈,3.每出栈一个元素,把与该元素有关的入度-1,4.再把入度为0的入栈,最后如果出栈元素小于元素总数,说明有回路

void topo()
{
	int* indegree;
	stack<int> p;
	bool* visit;
	for (int i = 0; i < num; i++)
	{
		if (!indegree[i] && visit[i])
		{
			p.push(i); 
			visit[i] = false;
		}
	}
	while (!p.empty())
	{
		int m = p.top();
		for (int i = 0; i < num; i++)
		{
			if (ju[m][i])
				indegree[i]--;
		}
		for (int i = 0; i < num; i++)
		{
			if (!indegree[i] && visit[i])
			{
				p.push(i);
				visit[i] = false;
			}
		}
		p.pop();
	}
}

最短路径:
迪杰斯特拉算法:1.与普利姆算法基本相同,只是储存最小权值的数组改成了最短路径,2.需要一个起始位置

void prim(int  start)//起始位置
{
	int* visit;
	bool* lowwei;
	for (int i = 0; i < num; i++)
	{
		if (ju[start][i])
			lowwei[i] = ju[start][i];
		else
			lowwei[i] = inf;

	}//用开始位置初始化low_weight数组
	visit[start] = false;
	int min = inf, index, j;
	for (int i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num; j++)
		{
			if (lowwei[j] < min && visit[j])
			{
				min = lowwei[j];
				index = j;
			}
		}
		visit[index] = false;//找到low_weight数组中的最小值的下标,标识该位置访问过
		for (j = 0; j < num;j++)
		{
			if (ju[index][j]+lowwei[index] < lowwei[j] && visit[j])
			{
				lowwei[j] = ju[index][j]+lowwei[index];
			}
		}//由该位置重新赋值low_weight数组
	}
}

5.查找
顺序(哨兵法),折半,索引,二叉排序树,平衡树B树(有空补),哈希(线性,二次,链地址),
二叉排序树:
插入:递归过程,比节点大就滑到他的右孩子,反之左孩子
删除:删除前需要判断删除元素是否存在,分三种情况删除:1.没有左孩子,右孩子接上就好,2.没有右孩子,左孩子接上,3.两个孩子都有,找左子树里面最大的元素(称为A),用它来替代被删元素,最后接上A的左子树,这里注意如果p==q的话,说明左孩子没有右子树,这时需要接到左孩子。

void insert(node* gs, node* m)
	{

		if (gs)
		{
			if (m->data > gs->data)
			{
				if (gs->ri)
					insert(gs->ri, m);
				else
				{
					gs->ri = m;
				}

			}
			else
			{
				if (gs->le)
					insert(gs->le, m);
				else
					gs->le = m;
			}
		}


	}
void shan(node*& p)
	{
		if (!p->le)
		{
			p = p->ri;
		}
		else if (!p->ri)
		{
			p = p->le;
		}//只有一个孩子
		else
		{
			node* q = p;
			node* s = p->le;

			while (s->ri)
			{
				q = s;
				s = s->ri;
			}//q为s的前驱
			p->data = s->data;
			if (q != p)q->ri = s->le;
			else
			{
				q->le = s->le;
			}


		}
	}

6.排序
冒泡,希尔,快排,堆排,直插,归并,基数

直插(升序):1.第0个元素不动,2.从第一个元素开始,记录下该元素的值,与前面排好的元素比较,大于它的往后移一位,3.找到第一个比他小的位置,然后赋值给那个位置
希尔:看成分部直插,1.先用一个整数gap给数据分成几个部分,2.各个部分依次直插,3.gap/2直至为1

int gap = n;
		while (gap > 1)
		{
			gap /= 2;
			for (k = 0; k < gap; k++)
			{//gap个部分
				for (i = k + gap; i < n; i += gap)
				{//每个部分直插排序
					temp = a[i];
					for (j = i; j >= gap && a[j - gap] < temp; j -= gap)
					{
						a[j] = a[j - gap];
					}
					a[j] = temp;
				}
			}

快排:
1.需要递归,
大致思路:类似折半查找,1.先取出一个枢纽,一般是第一个元素,2.然后从右找比枢纽小的第一个元素,与左边交换,然后从左边找比枢纽大的第一个元素,与右边交换,重复此动作直到x==y,3.然后把枢纽值赋予a[x],这样能让枢纽值处在一个较为中间的位置,并且左边的元素都比他小,右边的元素都比他大,4.再对枢纽值左边的数据和右边的数据递归调用

void quick(int* &a, int low, int high)
{
	int pri = a[low];
	int x = low, y = high;
	while (x < y)
	{
		while (x < y && a[y] >= pri)
		{
			y--;
		}
		a[x] = a[y];
		while (x < y && a[x] <= pri) {
			x++;
		}
		a[y] = a[x];
	}
	a[x] = pri;
	if (low < high)
	{
		if (x != low)
			quick(a, low, x - 1);
		if (x != high)
			quick(a, x + 1, high);
	}

}

堆排:
1.为了方便计数,从1开始,a[0]舍去,2.先初始排序,从n/2开始是因为叶子调整不了,3.初始化后,最大值(最小值)就在堆顶了,4.每次取出堆顶,然后再次排序(从头开始,注意大小要变化),得到次大(小)值

void diupai(int n, int i, int*& a)
{//从上往下排
	int j = 2 * i;
	while (j <= n)
	{
		if (j < n && a[j] > a[j + 1])
			j++;//取左右孩子中较小的一个
		if (a[i] > a[j])
			swap(a[i], a[j]);
		i = j;
		j = 2 * i;//往下排序
	}
}
void chu()
{
	int* a, n, i;
	//diupai(n, n / 2, a);
	for (i = n / 2; i >= 1; i--)
		diupai(n, i, a);//把每个除叶子以外的元素都排一遍
		//让最小的元素到达堆顶
	
	for (i = 0; i < n - 1; i++)
	{//每次取出堆顶,求次小值
		swap(a[n - i], a[1]);
		diupai(n-i-1, 1, a);//已经取出的元素不用再排,所以大小要变化
		
	}
}

归并:
1.最外面需要一个 i 保存分组的长度,2.j表示分组个数,3.对于每两个组,left为第一个组起始位置,right为第二个,middle为一个越界标识位置,4.第一个while是依次取出两个组内较大的元素,5.第二三个while是因为可能两组元素其中一组未取完,把未取的元素加入,5.把排序结果传回原数组

int t=2;
    while(t<n)              
        t*=2;
	for (int i = 2; i < t+1; i *= 2)       
	{
		for (int j = 0; j < n; j += i)        
		{
			int left = j, right = j + i/2;    
			if(right>n)
                break;
			int middle = right -1;

			int temp_loc = left;
			while (left <= middle && right < j + i && right < n)     
			{
				temp[temp_loc++] = nums[left] > nums[right]?nums[left++]:nums[right++] ;		
			}
			while (left <= middle)                    
				temp[temp_loc++] = nums[left++];
			while (right < j + i && right < n)        
				temp[temp_loc++] = nums[right++];
			for (int m = j; m< j + i && m<n; m++)    
            {
          
                nums[m] = temp[m];
            }

代码参考:https://blog.csdn.net/weixin_40673608/article/details/85008947
基数:
1.先计算最大位数,2.需要一个空间为10的队列储存对应尾数
大致思路:

void setsize(int* a, int n)
{
	keysize = 0;
	int maxx = a[0];
	for (int i = 0; i < n; i++)
	{
		if (a[i] > maxx)
			maxx = a[i];
	}//找到最大的数
	while (maxx != 0)
	{
		keysize++;
		maxx /= 10;
	}//计算最大数的位数
}

 queue<int>b[10];
		int div = 1;
		while (keysize--)
		{
			for (i = 0; i < n; i++)
			{
				b[(a[i] / div) % 10].push(a[i]);
			}//按尾数加入队列
			
			int k = 0;
			for (int j = 0; j < 10; j++)
			{
				while (!b[j].empty())
				{
					a[k] = b[j].front();
					b[j].pop();
					k++;
				}//把这次排序的结果放入数组
			}

			div *= 10;
			
		}

6.kmp
1.需要设置next数组,看成自己与自己匹配的过程
2.可以用string自带的find函数,找到了返回匹配的位置,没找到就返回string::npos

void setn(vector<int>& next, string a)
{
	int i = 0, j = -1;
	next.push_back(-1);
	while (i < a.size() - 1)
	{
		if (j == -1 || a[i] == a[j])
		{
			j++;
			i++;
			next.push_back(j);
		}
		else
		{
			j = next[j];
		}
	}
}
int kmp(string a, string b, vector<int> next)
{
	int i = 0, j = 0;
	int as = a.size(), bs = b.size();
	while (i <as && j <bs)
	{
		if (j == -1 || a[i] == b[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];
		}
	}
	if (j >= b.size())
	{
		return i- b.size()+1;
	}//匹配成功
	else
	{
		return -1;
	}

}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值