IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 关于数据结构基础部分(一些算法)的汇总 -> 正文阅读

[数据结构与算法]关于数据结构基础部分(一些算法)的汇总

1.九种排序算法(八种排序+二叉排序树的中序遍历)

(1)冒泡排序,最简单的一种

//冒泡排序,复杂度为n的二次方--------------------------------------------------------
void bubble(int arr[], int length) {
	for (int i = length; i >= 1; i--) {
		for (int j = 1; j < i; j++) {
			if (arr[j] > arr[j + 1]) {
				swap(arr, arr[j], arr[j + 1]);
			}
		}
	}
}

(2)简单排序,思想是把队列中最小的元素放在最前面,然后从下一个元素开始到末尾继续进行这个操作

//简单排序--------------------------------------------------------------------------
void simpleSort(int arr[], int length) {
	int min = 0;
	for (int i = 1; i <= length; i++) {
		min = i;
		for (int j = i; j <= length; j++) {
			if (arr[j] < arr[min])min = j;
		}
		swap(arr, i, min);
	}
}

(3)插入排序,思想是一旦前面的数字小于当前数字的数值,就开始向后赋值,直到找到合适的位置进行插入

例如? ? 1?3 4 5 2-------先把2储存为temp;

? ? ? ? ? ?1 3 4 5 5------1 3 4 4 5-------1 3 3 4 5,这时候1小于temp,所以变成了1 [2] 3 4 5,排序完成

//插入排序
//如果前面的比自己大那就把数字换成前面的
//然后依次往下找,直到前面没有比自己大的了,而且此时自己后面的已经附上了自己的数值--
//这个位置就可以进行插入了
void insertSort(int arr[],int length) {
	for (int i = 2; i <= length; i++) {
		if (arr[i] < arr[i - 1]) {
			int k;   int temp = arr[i];
			for (k = i-1; k >= 1 && temp < arr[k]; k--) {
				arr[k+1] = arr[k];
			}
			arr[k+1] = temp;
		}
	}
}

(4)希尔排序,思想就是再逐个递减的increment的基础上,再次使用插入排序

//希尔排序(跳跃版本的插入排序)--------------------------------------------------
void shell(int arr[], int length) {
	int increment = length;
	do {
		increment = increment / 3 + 1;
		for (int i = increment+1; i <= length; i++) {
			if (arr[i - increment] > arr[i]) {
				int k;   int temp = arr[i];
				for (k = i-increment; k >= 1 && temp < arr[k]; k-= increment) {
					arr[k+increment] = arr[k];
				}
				arr[k + increment] = temp;
			}
		}
	} while (increment > 1);
}

(5)堆排序,某种意义上来说是树结构上的插入排序

//堆排序-------可以理解为完全二叉树中的希尔排序---------------------------------
//堆排序的大致原理为:
//根节点的两个孩子(也可能有一个,需要具体判断)
void bigstock(int arr[],int start,int end) {//这个方法的使用前提是,基本成型,或者从下网上构建
	int temp = arr[start];
	for (int i = start*2; i <= end; i *= 2) {
		if (i<end  && arr[i] < arr[i + 1])  i++;
		if (temp > arr[i])    break;
		arr[start] = arr[i];
		start = i;
	}
	arr[start] = temp;
}
void stockSort(int arr[], int length) {
	for (int i = length / 2; i > 0; i--) {//这里出现过问题,i最终是需要到1的
		bigstock(arr, i, length);
	}
	for (int i = length; i > 1; i--) {//这个1 2都行
		swap(arr, 1, i);
		bigstock(arr, 1, i-1);
	}
}

(6)计数排序,只能对纯数字生效,储存下来每个数字的出现次数,然后遍历原本的数组,按出现次数安插到新数组的位置

//计数排序(仅限于纯数字的排序,这个其实更适合第0位开头的数组)---------------------------------
void countSort(int arr[], int length, int m) {
	int equal[10] = {0,0,0,0,0,0,0,0,0,0}; 
	int less[10] = {0,0,0,0,0,0,0,0,0,0};
	for (int i = 1; i <= length; i++) 
		equal[arr[i]]++;
	for (int i = 1; i <= m; i++) 
		less[i] = less[i - 1] + equal[i - 1];
	int point = 0;
	int Brr[50];
	for (int i = 0; i <= length; i++) 
		Brr[i] = arr[i];
	for (int i = 0; i <= length; i++) {
		arr[1 + less[Brr[i]]++] = Brr[i];
	}
}

(7)快速排序:原理和前序遍历比较像

//快速排序,不被大佬建议使用的一种排序,原理是前序遍历----------------------------------------
int Quick(int arr[], int p, int q) {
	int point = 0;
	for (int i = p; i < q - 1; i++) {
		if (arr[i] <= arr[q]) {
			swap(arr, i, point);
			point++;
		}
	}
	swap(arr, q, point);
	return point;
}
void QuickSort(int arr[], int p, int q) {
	if (q > p) {
		int r = Quick(arr, p, q);
		QuickSort(arr, p, r-1);
		QuickSort(arr, r+1, q);
	}
}

(8)归并排序:原理和后序遍历很像

//归并排序,使用递归中的后序遍历,相对最稳定---------------------
void Merge(int arr[], int p,int r, int q) {
	int B[50]; int C[50];
	int pointB = 0; int pointC = 0;
	for (int i = p; i <= r; i++) {
		B[i - p] = arr[i];
	}
	for (int i = r+1; i <= q; i++) {
		C[i - r - 1] = arr[i];
	}
	B[r-p+1] = 100; C[q-r] = 100;
	for (int i = p; i <= q; i++) {
		if (B[pointB] >= C[pointC]) {
			arr[i] = C[pointC++];
		}
		else{
			arr[i] = B[pointB++];
		}
	}
}
void MergeSort(int arr, int p, int q) {
	if (q > p) {
		int r = (p + q) / 2;
		MergeSort(arr,p,r);
		MergeSort(arr,r+1,q);
	}
}

(9)二叉排序树:利用中序遍历可以读取树里的内容,详见讲解树的部分.

//再拓展出一种二叉树排序
class Node {
public:
	int num;
	Node* left=NULL;
	Node* right=NULL;
};
void createtree(Node*& root, int num) {
	if (root == NULL) {
		root = new Node;
		root->num = num;
	}
	else {
		if(num < root->num)
		createtree(root->left,  num);
		if (num > root->num)
		createtree(root->right, num);
	}
}

2.关于图有关的一部分算法

(1)关于图的储存方式,大体上可以分为三种 接邻矩阵 接邻表 边集数组

接邻矩阵:常用的两种是接邻表和权重表,接邻表没有权重,两个结点相邻时赋值1,不相邻的时候赋值为0(除非自带一共环,否则自己到自己也是0).? 而权重表是相邻则记录边的权重,不相邻记录为max,自己到自己记录为0.

下面分别为权重表和接邻表的创建储存方式

int arr[7][7];
	for (int i = 0; i <= 6; i++) {
		for (int j = 0; j <= 6; j++) {
			if (i == j)
				arr[i][j] = 0;
			else
				arr[i][j] = 100;
		}
	}
	int edgeNum, x, y, weight;    cout << "下面请输入边的数目" << endl; cin >> edgeNum;
	for (int i = 1; i <= edgeNum; i++) { cin >> x >> y>>weight; arr[x][y] = weight;  arr[y][x] = weight; }
	//权重无向图创建完成
int arr[7][7];
	for (int i = 0; i <= 6; i++) {
		for (int j = 0; j <= 6; j++) {
			arr[i][j] = 0;
		}
	}
	int edgeNum, x, y;    cout << "下面请输入边的数目" << endl; cin >> edgeNum;
	for (int i = 1; i <= edgeNum; i++) { cin >> x >> y ; arr[x][y] = 1;  arr[y][x] = 1; }

接邻表:分为普通接邻表和十字接邻表,普通接邻表的结构如下

创建一共头节点类型的数组,头节点里面可以储存相关的信息,然后每个头节点都自带一个指针指向链表的第一个.这样访问和记录数据都方便很多.

每个结点需要的数据是adj(序号)? weight(权重) next(指针域)

十字链表自带一个前驱的链表,本质上差不太多

class Node {
public:
	int num;
	int weight;
	Node* next;
	Node(int a) { num = a; };
};
class headNode {
public:
	int length = 0;
	Node* point=NULL;
	int in, out = 0;
};
class graph {
public:
	headNode arr[10];
	int NodeNum, EdgeNum = 0;
};
void createGraph(graph& Graph) {
	int edge, point,start,end,weight;
	cout << "请输入边数 和 点数" << endl;
	cin >> edge >> point;
	Graph.EdgeNum = edge;
	Graph.NodeNum = point;
	for (int i = 0; i < point; i++) {
		Graph.arr[i] = headNode();
	}
	for (int i = 1; i <= edge; i++) {
		cin >> start >> end >> weight;
		Graph.arr[start].out++;//出度+1
		Graph.arr[end].in++;//入度+1
		Node* temp = new Node(end);
		temp->weight = weight;//赋予权重
		temp->next = Graph.arr[start].point;
		Graph.arr[start].point = temp;//头插法插入
	}
}
//图结构已经储存完毕;

边集数组:边集数组是专门用于边这样子,储存方式如下

起点,终点,权重三个方向的数值

kruNode arr[10] = {
		kruNode(0,1,1),kruNode(2,4,1),kruNode(1,5,1),kruNode(4,6,1),kruNode(5,6,1),
		kruNode(0,2,2),kruNode(1,3,2),kruNode(3,4,2),kruNode(3,6,3),kruNode(1,2,3)
	};

(2)图的遍历方法,分为两种

深度遍历搜索DFS:原理类似于前序遍历,每次读取到一个没有访问过的点,就先打上标记进行输出,然后看这个点的子代进行遍历(递归操作)

int visited[50];
void DFSserver(int arr[7][7],int i) {
	if (visited[i] != 1) {
		cout << i << endl;   visited[i] = 1;
		for (int j = 0; j <= 6; j++) {
			int temp = arr[i][j];
			DFSserver(arr, temp);
		}
	}

}
void DFS() {
	int arr[7][7];
	for (int i = 0; i <= 6; i++) {
		for (int j = 0; j <= 6; j++) {
			arr[i][j] = 0;
		}
	}
	int edgeNum, x, y;    cout << "下面请输入边的数目" << endl; cin >> edgeNum;
	for (int i = 1; i <= edgeNum; i++) { cin >> x >> y ; arr[x][y] = 1;  arr[y][x] = 1; }
	for (int i = 0; i <= 6; i++) 
		visited[i] = 0;
	for (int i = 0; i <= 6; i++)
		DFSserver(arr, i);
}

BFS广度遍历搜索

原理据说是类似层序遍历,不过层序遍历我还没写过,每次读取一个点,如果这个点没有走过,就把这个点存入队列中,并标记走过.然后每次弹出一个点,输出的同时,把这个点没有经过的点也存入队列中

为了提升运算速度和防止特殊情况(比如一个图里有两个相互独立的连通分量),使用遍历操作,让每个点都充当一次队列中的第一点(最外面加的那个for循环和if判断)

//深度遍历的邻接表
//原理是依托队列进行层序遍历
void BFS() {
	int arr[7][7];
	for (int i = 0; i <= 6; i++) {
		for (int j = 0; j <= 6; j++) {
			arr[i][j] = 0;
		}
	}
	int edgeNum, x, y;    cout << "下面请输入边的数目" << endl; cin >> edgeNum;
	for (int i = 1; i <= edgeNum; i++) { cin >> x >> y; arr[x][y] = 1;  arr[y][x] = 1; }
	for (int i = 0; i <= 6; i++)
		visited[i] = 0;
	//上面是对于边和visited数组进行赋值
	queue<int> Queue;
	for (int n = 0; n <= 6; n++) {//加或者不加..顺序没有区别
		if (visited[n] != 1) {    //大概是为了节约一部分运算吧.....
			Queue.push(n);  visited[n] = 1;
			while (!Queue.empty()) {
				int temp = Queue.front();  Queue.pop();   cout << temp << endl;
				for (int i = 0; i <= 6; i++) {
					if (visited[i] != 1 && arr[temp][i] == 0) {
						Queue.push(i);  visited[i] = 1;
					}
				}
			}

		}
	}
}

(3)图的三种最短路径算法

1.迪杰斯特拉算法,原理是利用接邻矩阵进行存储,选取一个dis数值最小而且没有经过的点作为中转点进行松弛操作(dis为起点到其他点的最短路径,而且使用另外一个数组来记录某个点是不是已经经过了)

松弛操作的原理:A到B点的路途>A到中转点+中转点到B,那么A到B的距离就更新为这个

void dijiesitela() {
	//原理,每次选取入度最小的一个点以这个点作为中转点遍历其他的点
	int arr[7][7];
	for (int i = 0; i <= 6; i++) {
		for (int j = 0; j <= 6; j++) {
			if (i == j)
				arr[i][j] = 0;
			else
				arr[i][j] = 100;
		}
	}
	int edgeNum, x, y, weight;    cout << "下面请输入边的数目" << endl; cin >> edgeNum;
	for (int i = 1; i <= edgeNum; i++) { cin >> x >> y >> weight; arr[x][y] = weight;  arr[y][x] = weight; }
	int distant[7] = { 0,1,2,100,100,100,100 };//一共七个点
	int visited[7] = { 0,1,1,1,1,1,1 };
	for (int i = 1; i <= 6; i++) {
		int k = 0; int min = 100;
		for (int x = 0; x < 7; x++) {
			if (visited[x] != 0 && min > distant[x]) {
				min = distant[x]; k = x;
			}
		}
		visited[k] = 0;//代表这个点已经经过访问
		for (int x = 0; x < 7; x++) {
			if (distant[x] > distant[k] + arr[x][k]) {
				distant[x] = distant[k] + arr[x][k];
			}
		}
	}
	for (int x = 0; x < 7; x++) {
		cout << distant[x] << endl;
	}
}

2.贝尔曼福特算法

原理:利用边集数组进行储存,原理依然是每个点作为中转点松弛操作,不过这要重复n-1次进行矫正

//贝尔曼福特用的是边集数组进行处理
void bellman() {
	kruNode arr[10] = {
		kruNode(0,1,1),kruNode(2,4,1),kruNode(1,5,1),kruNode(4,6,1),kruNode(5,6,1),
		kruNode(0,2,2),kruNode(1,3,2),kruNode(3,4,2),kruNode(3,6,3),kruNode(1,2,3)
	};
	int dis[7] = { 0,100,100,100,100,100,100 };
	for (int i = 1; i <= 6; i++) {
		for (int j = 0; j <= 9; j++) {
			if (dis[arr[j].end] > dis[arr[j].start] + arr[j].weight) {
				dis[arr[j].end] = dis[arr[j].start] + arr[j].weight;
			}
		}
	}
	for (int x = 0; x < 7; x++) {
		cout << dis[x] << endl;
	}
}

3.弗洛伊德算法,纯暴力破解了就是

使用接邻矩阵(由于没有筛选机制,所以同样是重复n-1次数)

void floyd() {
	int arr[7][7];
	for (int i = 0; i <= 6; i++) {
		for (int j = 0; j <= 6; j++) {
			if (i == j)
				arr[i][j] = 0;
			else
				arr[i][j] = 100;
		}
	}
	int edgeNum, x, y, weight;    cout << "下面请输入边的数目" << endl; cin >> edgeNum;
	for (int i = 1; i <= edgeNum; i++) { cin >> x >> y >> weight; arr[x][y] = weight;  arr[y][x] = weight; }
	int dis[7] = { 0,100,100,100,100,100,100 };
	for (int i = 1; i <= 6; i++) {
		for (int x = 0; x <= 6; x++) {
			for (int y= 0; y <= 6; y++) {
				if (dis[y] > dis[x] + arr[x][y]) {
					dis[y] = dis[x] + arr[x][y];
				}
			}
		}
	}
	for (int x = 0; x < 7; x++) {
		cout << dis[x] << endl;
	}
}

(4)图的两种最小生成树方法

prim算法,原理是集团化,其中利用visited数组来判断,如果为0则代表该点已经在集合内部

?同时准备group数组,来记录集团到各个点的距离

准备adjvex数组,来记录每个点的上一个是什么

每次(这样重复六次)寻找距离最近的点k存入集团中(标记为0),然后输出adjvex[k]和k

再然后遍历其他没有经过的点,如果集团到该点的距离大于k点到该点的距离,则group数组就进行调整,同时把该点的上一级记录为k

void prim() {
	int arr[7][7];
	for (int i = 0; i <= 6; i++) {
		for (int j = 0; j <= 6; j++) {
			if (i == j)
				arr[i][j] = 0;
			else
				arr[i][j] = 100;
		}
	}
	int edgeNum, x, y, weight;    cout << "下面请输入边的数目" << endl; cin >> edgeNum;
	for (int i = 1; i <= edgeNum; i++) { cin >> x >> y>>weight; arr[x][y] = weight;  arr[y][x] = weight; }
	//权重无向图创建完成
	//前面都是图的创建,这里才是算法的核心内容
	int group[7] = { 0,1,2,100,100,100,100 };
	int adjvex[7] = { 0,0,0,0,0,0,0 };
	for (int i = 1; i <= 6; i++) {//重复n-1次数
		int min = 100; 
		int newNode = 0;
		for (int k = 0; k < 7; k++) {
			if (group[k] != 0 && group[k] < min) {
				min = group[k]; newNode = k;
			}
		}
		//NodeNew为最小的边的终点,min为最小边的值
		group[newNode] = 0;
		cout << adjvex[newNode] << "-----" << newNode << endl;
		for (int k = 0; k<7; k++) {
			if (group[k] != 0&&group[k]>arr[newNode][k]) {
				group[k]=arr[newNode][k];
				adjvex[k] = newNode;
			}
		}
	}
}

krusal算法;

prim算法是走一步看一部,krustal用空间换成时间,利用边集数组进行存储(数组按权重从低到高进行排列),然后遍历每条边的时候检验是不是构成环,如果没有构成环就输出,并且start和end的头节点关联一下

关于是否构成环的算法,就是判断两个点追根溯源,是不是同一个启示点.这里使用类似静态链表的数组,每个位置上存在自己上一位的地址.然后使用递归/循环的方法溯源 .

int sethead(int arr[],int num) {//寻找开头点
	if (arr[num] == -1) {//判定,如果再上一个点是-1,那么就可知已经溯源到了极限了
		return num;
	}
	else {
		return sethead(arr, arr[num]);
	}
}
void krustal() {
	//使用边集数组来储存图的信息最好
	kruNode arr[10] = {
		kruNode(0,1,1),kruNode(2,4,1),kruNode(1,5,1),kruNode(4,6,1),kruNode(5,6,1),
		kruNode(0,2,2),kruNode(1,3,2),kruNode(3,4,2),kruNode(3,6,3),kruNode(1,2,3)
	};
	int A, B;
	int prev[10] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1 };
	for (int i = 0; i <= 9; i++) {
		A = sethead(prev, arr[i].start);
		B = sethead(prev, arr[i].end);
		if (A != B) {
			cout << arr[i].start << "-----" << arr[i].end << endl;
			prev[B] = arr[i].start;
		}
	}
}

(5)图的关键路径算法

etv数组在拓扑排序的时候,按照拓扑正序进行存储

ltv数组按拓扑逆序进行处理

//图结构已经储存完毕;(graph为邻接表进行存储)


//关键路径算法
//受到学妹的启发,终于明白这个最短是什么意思了
//最短并非指的是从起点到终点的最短距离(事实上这个也能求,不过理论上不行)
//关键路径的时间加起来,其实是完成所有任务需要的最短时间(默认任务是可以并发完成的)
//感谢来自山西的大佬学妹
int etv[20];
int ltv[20];
int top = 0;
int Stack[20];//这是储存用到的栈,或者说是数组
void topuGraph(graph& Graph) {
	stack<int> stack2;
	for (int i = 0; i < Graph.NodeNum; i++) {
		if (Graph.arr[i].in == 0)stack2.push(i);
		etv[i] = 0;
	}
	while (!stack2.empty()) {
		int temp = stack2.top();
		stack2.pop();
		Stack[top++] = temp;
		Node* point = Graph.arr[temp].point;
		while (point) {
			if (--Graph.arr[point->num].in == 0) {
				stack2.push(point->num);
			}
			if (etv[point->num]<etv[temp]+point->weight) {//修改最早数值
				etv[point->num] = etv[temp] + point->weight;
			}
			point = point->next;
		}
	}
}
void keyRoad(graph& Graph) {
	for (int i = 0; i < Graph.NodeNum; i++) {
		ltv[i] = etv[Graph.NodeNum-1];
	}
	while (top >= 0) {
		int temp = Stack[top--];
		Node* point = Graph.arr[temp].point;
		while (point) {
			if (ltv[temp] > ltv[point->num] - point->weight) {
				ltv[temp] = ltv[point->num] - point->weight;
			}
			point = point->next;
		}
	}
	int ete, lte = 0;
	for (int i = 0; i < Graph.NodeNum; i++) {
		Node* point = Graph.arr[i].point;
		while (point) {
			ete = etv[i];
			lte = ltv[point->num]-point->weight;
			if (ete == lte) {
				cout << i << "--->" << point->num <<"("<<point->weight<<")" << endl;
			}
			point = point->next;
		}
	}
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:52:38  更:2022-09-21 00:57:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 12:20:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码