数据结构基础笔记(一)【严蔚敏】
2015-08-29 14:23
501 查看
广义表
广义表相关概念:◆ a1(表中第一个元素)称为表头;
◆ 其余元素组成的子表称为表尾;(a2,a3,…,an)
◆ 广义表中所包含的元素(包括原子和子表)的个数称为表的长度。
◆ 广义表中括号的最大层数称为表深 (度)。
根据对表头、表尾的定义,任何一个非空广义表的表头可以是原子,也可以是子表, 而表尾必定是广义表。
只要广义表非空,都是由表头和表尾组成,即一个确定的表头和表尾就唯一确定一个广义表。
广义表数据结点定义:
typedef struct GLNode { int tag ; /* 标志域,为1:表结点;为0 :原子结点 */ union { elemtype value; /* 原子结点的值域 */ struct { struct GLNode *hp , *tp ; }ptr ; /* ptr和atom两成员共用 */ }Gdata ; } GLNode ; /* 广义表结点类型 */
二叉树
满二叉树(Full Binary Tree)一棵深度为k且有2^k-1个结点的二叉树称为满二叉树。
完全二叉树(Complete Binary Tree)
如果深度为k,由n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应,该二叉树称为完全二叉树。
完全二叉树是满二叉树的一部分,而满二叉树是完全二叉树的特例。
####遍历
前序非递归遍历
思路:
1).访问当前结点,用栈暂存当前结点的右结点【如果不为空】;
2).如果当前结点的左结点不为空,则进入继续访问,否则从栈取结点访问,即返回1继续,如果栈为空则结束遍历;
vector<int> preorderTraversal(TreeNode* root) { vector<int> ans; if (!root) return ans; stack<TreeNode*> stk_tree; while (1){ ans.push_back(root->val); if (root->right) stk_tree.push(root->right); if (root->left) root = root->left; else if (stk_tree.empty()) break; else { root = stk_tree.top(); stk_tree.pop(); } } return ans; }
中序非递归遍历
思路:
1).若p不为空,p进栈,p=p->Lchild;
2).否则(即p为空),退栈到p,访问p所指向的结点;【栈为空则结束】
3).如果p的右结点不为空,p=p->Rchild ,转1;
vector<int> inorderTraversal(TreeNode* root) { vector<int> ans; if (!root) return ans; stack<TreeNode*> s; while (root) { s.push(root); root = root->left; } while (!s.empty()) { TreeNode * t = s.top(); s.pop(); ans.push_back(t->val); if (t->right) { t = t->right; while (t){ s.push(t); t = t->left; } } } return ans; }
后序非递归遍历
思路:
这里使用flag的栈对应表示结点属性,如果flag=1表示结点为父节点,进栈后再次出栈时可以直接访问;而如果flag=0表示该节点是右子节点,需要将该节点的子节点入栈,同时它本身的flag变为1.
1).若p不为空,p进栈【flag=1】,同时如果p的右结点不为空,右结点进栈【flag=0】,进入p=p->left;
2).从栈中取值,如果flag=1,说明该节点的子节点已经访问结束,该节点可以访问;否则进入1继续。【栈为空结束】
vector<int> postorderTraversal(TreeNode* root) { vector<int> ans; if (!root) return ans; stack<TreeNode*> stk_tree; stack<int> stk_flag; while (root){ stk_tree.push(root); stk_flag.push(1); if (root->right) { stk_tree.push(root->right); stk_flag.push(0); } root = root->left; } while (!stk_tree.empty()) { root = stk_tree.top(); stk_tree.pop(); flag = stk_flag.top(); stk_flag.pop(); if (!flag) { while (root){ stk_tree.push(root); stk_flag.push(1); if (root->right) { stk_tree.push(root->right); stk_flag.push(0); } root = root->left; } } else { ans.push_back(root->val); } } return ans; }
图
在一条路径中,若没有重复相同的顶点,该路径称为简单路径;第一个顶点和最后一个顶点相同的路径称为回路(环);
对无向图G=(V,E),若任意vi,vj∈V,vi和vj都是连通的,则称图G是连通图,否则称为非连通图。若G是非连通图,则极大的连通子图称为G的连通分量;
对有向图G=(V,E),若任意vi,vj∈V,都有以vi为起点, vj 为终点以及以vj为起点,vi为终点的有向路径,称图G是强连通图,否则称为非强连通图。若G是非强连通图,则极大的强连通子图称为G的强连通分量。
最小生成树
普里姆(Prim)算法算法思想(算法复杂度O(n^2)):
1). 若从顶点v0出发构造,U={v0},TE={};
2). 先找权值最小的边(u,v),其中u∈U且v∈V-U,并且子图不构成环,则U= U∪{v},TE=TE∪{(u,v)} ;
3). 重复⑵ ,直到U=V为止。则TE中必有n-1条边, T=(U,TE)就是最小生成树。
题目链接:#1097 : 最小生成树一·Prim算法
定义一维数组保存V-U中各顶点到U中顶点具有权值最小的边【lowcost】:如果lowcost为0表示已加入到U中,否则每轮都要V-U中更新各顶点的lowcost
struct { int adjvex; /* 边所依附于U中的顶点 */ int lowcost; /* 该边的权值 */ }closedge ;
#include <stdio.h>
#include <string.h>
#define N 1002
#define INFINITY 10005;
struct { int adjvex; /* 边所依附于U中的顶点 */ int lowcost; /* 该边的权值 */ }closedge ;
int array
;
int prim(int n){
int i, j, min, v, ans = 0, idx;
//初始化数组【默认从v0开始】
for (i = 0; i < n; i++) {
closedge[i].adjvex = 0;
closedge[i].lowcost = array[i][0];
}
//n-1趟
for (j = 0; j < n - 1; j++) {
min = INFINITY;
//找到到U的最小权值的顶点
for (v = 0; v < n; v++) {
if (closedge[v].lowcost != 0 && closedge[v].lowcost < min) {
min = closedge[v].lowcost;
idx = v;
}
}
ans += min;
closedge[idx].lowcost = 0;//将该顶点加入U中
for (v = 0; v<n; v++)
if (array[v][idx]<closedge[v].lowcost) {
closedge[v].lowcost = array[v][idx];
closedge[v].adjvex = idx;
} /* 修改数组closedge
的各个元素的值 */
}
return ans;
}
int main()
{
int n, i, j;
scanf("%d", &n);
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
scanf("%d", &array[i][j]);
printf("%d\n", prim(n));
return 0;
}
注:prim算法稍加修改即可变为Dijkstra算法。【lowcost表示到各个顶点到给定单点的最短距离。】
克鲁斯卡尔(Kruskal)算法
思想:
设G=(V, E)是具有n个顶点的连通网,T=(U, TE)是其最小生成树。初值:U=V,TE={} 。c
对G中的边按权值大小从小到大依次选取。
1). 选取权值最小的边(vi,vj),若边(vi,vj)加入到TE后形成回路,则舍弃该边(边(vi,vj) ;否则,将该边并入到TE中,即TE=TE∪{(vi,vj)} 。
2). 重复1),直到TE中包含有n-1条边为止。
题目链接:#1098 : 最小生成树二·Kruscal算法
先将所有边排序,然后遍历判定是否边的两个顶点是否在一个集合,如果在则会形成回路,舍弃之;否则将改边加入TE。【判定两个点是否在一个集合用并查集算法】
#include<iostream> #include<algorithm> using namespace std; struct edge{ int u; int v; int len; }; int cmp(const edge a, const edge b) { return a.len < b.len; } #define M 1000005 #define N 100005 edge e[M]; int pos ; //记录各个节点在哪个集合中。 void init(int n) { for (int i = 1; i <= n; i++) pos[i] = i; } int find(int x) { if (x == pos[x]) return x; else { pos[x] = find(pos[x]); return pos[x]; } } int kruskal(int n, int m) { int sum = 0,count = 1; for (int i = 0; i < m; i++) { int fx = find(e[i].u); int fy = find(e[i].v); if (fx != fy) { sum += e[i].len; count++; if (count == n) break; pos[fx] = fy; } } return sum; } int main() { int n, m; while (cin >> n >> m) { init(n); //初始化并查集 for (int i = 0; i < m; i++) cin >> e[i].u >> e[i].v >> e[i].len; sort(e, e + m, cmp); cout << kruskal(n, m) << endl; } }
相关文章推荐
- 数据结构与算法分析Java版练习1.6
- 为算法考试做准备--图的实现及广度和深度优先搜索实现
- 并查集算法介绍
- 数据结构Java实现04----循环链表、仿真链表
- MySQL索引背后的数据结构及算法原理
- Windows内核编程基础篇之常见内核数据结构
- 过河卒
- 数据结构算法演示
- 数据结构之---C语言实现斐波那契查找
- 数据结构:循环队列(队列的顺序表示)
- 数据结构之---C语言实现顺序表查找(一)
- 数据结构课程主页-2014级
- 数据结构:数组的操作(C语言描述)
- Nginx高级数据结构源码分析(五)-----红黑树
- 数据结构:haffman树
- MySQL索引背后的数据结构及算法原理
- 为算法考试做准备--ArrayList实现
- 数据结构--图 的JAVA实现(下)
- 浅谈数据结构-最短路径
- 数据结构—堆的学习:堆化数组、堆的插入、堆的删除