码迷,mamicode.com
首页 > 其他好文 > 详细

数据结构基础笔记(一)【严蔚敏】

时间:2015-08-29 15:31:40      阅读:207      评论:0      收藏:0      [点我收藏+]

标签:数据结构   最小生成树   广义表   


广义表

广义表相关概念:
◆ 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[N];
#include <stdio.h>
#include <string.h>
#define N 1002
#define INFINITY 10005;

struct {
    int  adjvex;     /*   边所依附于U中的顶点   */
    int  lowcost;    /*   该边的权值   */
}closedge[N];
int array[N][N];

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[n]的各个元素的值   */
    }
    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[N];                         //记录各个节点在哪个集合中。

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;
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

数据结构基础笔记(一)【严蔚敏】

标签:数据结构   最小生成树   广义表   

原文地址:http://blog.csdn.net/barry283049/article/details/48087101

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!