经典算法题每日演练——第二十题 三元组

 

       我们知道矩阵是一个非常强大的数据结构,在动态规划以及各种图论算法上都有广泛的应用,当然矩阵有着不足的地方就是空间和时间

复杂度都维持在N2上,比如1w个数字建立一个矩阵,在内存中会占用1w*1w=1亿的类型空间,这时就会遇到outofmemory。。。那么面

临的一个问题就是如何来压缩矩阵,当然压缩的方式有很多种,这里就介绍一个顺序表的压缩方式:三元组。

一:三元组

    有时候我们的矩阵中只有零星的一些非零元素,其余的都是零元素,那么我们称之为稀疏矩阵,当然没有绝对的说有多少个零元素才算稀疏。

针对上面的这个无规律的存放非零元素,三元组提出了一种方法,就是仅仅记录矩阵中的非零元素以及它的行,列以及值N(x,y,v)构成的一个三元

组,标识一个稀疏矩阵的话,还要记录该矩阵的阶数,这样我们就将一个二维的变成了一个一维,极大的压缩的存储空间,这里要注意的就是,三

元组的构建采用“行“是从上到下,“列”也是从左到右的方式构建的顺序表。

/// <summary>
        /// 三元组
        /// </summary>
        public class Unit
        {
            public int x;
            public int y;
            public int element;
        }

        /// <summary>
        /// 标识矩阵
        /// </summary>
        public class SPNode
        {
            //矩阵总行数
            public int rows;

            //矩阵总列数
            public int cols;

            //非零元素的个数
            public int count;

            //矩阵中非零元素
            public List<Unit> nodes = new List<Unit>();
        }

其实说到这里也就差不多了,我们只要知道三元组是用来做矩阵压缩的一个顺序存储方式即可,然后知道怎么用三元组表来做一些常规的矩阵

运算,好了,既然说已经做成线性存储了,那就做个“行列置换”玩玩。

二:行列置换

    做行列置换很容易,也就是交换"非零元素"的(x,y)坐标,要注意的就是,原先我们的三元组采用的是”行优先“,所以在做转置的时候需要

遵循"列优先“。

/// <summary>
        /// 行转列运算
        /// </summary>
        /// <param name="spNode"></param>
        /// <returns></returns>
        public SPNode ConvertSpNode(SPNode spNode)
        {
            //矩阵元素的x和y坐标进行交换
            SPNode spNodeLast = new SPNode();

            //行列互换
            spNodeLast.rows = spNode.cols;
            spNodeLast.cols = spNode.rows;
            spNodeLast.count = spNode.count;

            //循环原矩阵的列数 (行列转换)
            for (int col = 0; col < spNode.cols; col++)
            {
                //循环三元组行的个数
                for (int sp = 0; sp < spNode.count; sp++)
                {
                    var single = spNode.nodes[sp];

                    //找到三元组中存在的相同编号
                    if (col == single.y)
                    {
                        spNodeLast.nodes.Add(new Unit()
                        {
                            x = single.y,
                            y = single.x,
                            element = single.element
                        });
                    }
                }
            }

            return spNodeLast;
        }

最后是总的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.IO;

namespace ConsoleApplication2
{
    public class Program
    {
        public static void Main()
        {
            Martix martix = new Martix();

            //构建三元组
            var node = martix.Build();

            foreach (var item in node.nodes)
            {
                Console.WriteLine(item.x + "\t" + item.y + "\t" + item.element);
            }

            Console.WriteLine("******************************************************");

            var mynode = martix.ConvertSpNode(node);

            foreach (var item in mynode.nodes)
            {
                Console.WriteLine(item.x + "\t" + item.y + "\t" + item.element);
            }

            Console.Read();
        }
    }

    public class Martix
    {
        /// <summary>
        /// 三元组
        /// </summary>
        public class Unit
        {
            public int x;
            public int y;
            public int element;
        }

        /// <summary>
        /// 标识矩阵
        /// </summary>
        public class SPNode
        {
            //矩阵总行数
            public int rows;

            //矩阵总列数
            public int cols;

            //非零元素的个数
            public int count;

            //矩阵中非零元素
            public List<Unit> nodes = new List<Unit>();
        }

        /// <summary>
        /// 构建一个三元组
        /// </summary>
        /// <returns></returns>
        public SPNode Build()
        {
            SPNode spNode = new SPNode();

            //遵循行优先的原则
            spNode.nodes.Add(new Unit() { x = 0, y = 0, element = 8 });
            spNode.nodes.Add(new Unit() { x = 1, y = 2, element = 1 });
            spNode.nodes.Add(new Unit() { x = 2, y = 3, element = 6 });
            spNode.nodes.Add(new Unit() { x = 3, y = 1, element = 4 });

            //4阶矩阵
            spNode.rows = spNode.cols = 4;

            //非零元素的个数
            spNode.count = spNode.nodes.Count;

            return spNode;
        }

        /// <summary>
        /// 行转列运算
        /// </summary>
        /// <param name="spNode"></param>
        /// <returns></returns>
        public SPNode ConvertSpNode(SPNode spNode)
        {
            //矩阵元素的x和y坐标进行交换
            SPNode spNodeLast = new SPNode();

            //行列互换
            spNodeLast.rows = spNode.cols;
            spNodeLast.cols = spNode.rows;
            spNodeLast.count = spNode.count;

            //循环原矩阵的列数 (行列转换)
            for (int col = 0; col < spNode.cols; col++)
            {
                //循环三元组行的个数
                for (int sp = 0; sp < spNode.count; sp++)
                {
                    var single = spNode.nodes[sp];

                    //找到三元组中存在的相同编号
                    if (col == single.y)
                    {
                        spNodeLast.nodes.Add(new Unit()
                        {
                            x = single.y,
                            y = single.x,
                            element = single.element
                        });
                    }
                }
            }

            return spNodeLast;
        }
    }
}

时间: 2024-11-03 00:45:24

经典算法题每日演练——第二十题 三元组的相关文章

经典算法题每日演练——第二十二题 奇偶排序

原文:经典算法题每日演练--第二十二题 奇偶排序   这个专题因为各种原因好久没有继续下去了,MM吧...你懂的,嘿嘿,不过还得继续写下去,好长时间不写,有些东西有点生疏了, 这篇就从简单一点的一个"奇偶排序"说起吧,不过这个排序还是蛮有意思的,严格来说复杂度是O(N2),不过在多核的情况下,可以做到 N2 /(m/2)的效率,这里的m就是待排序的个数,当m=100,复杂度为N2 /50,还行把,比冒泡要好点,因为重点是解决问题的奇思妙想.     下面我们看看这个算法是怎么描述的,既

经典算法题每日演练——第二十五题 块状链表

原文:经典算法题每日演练--第二十五题 块状链表 在数据结构的世界里,我们会认识各种各样的数据结构,每一种数据结构都能解决相应领域的问题,每一种数据结构都像 是降龙十八掌中的某一掌,掌掌毙命... 当然每个数据结构,有他的优点,必然就有它的缺点,那么如何创造一种数据结构 来将某两种数据结构进行扬长避短,那就非常完美了.这样的数据结构也有很多,比如:双端队列,还有就是今天讲的 块状链表,    我们都知道 数组 具有 O(1)的查询时间,O(N)的删除,O(N)的插入...            

经典算法题每日演练——第二十三题 鸡尾酒排序

原文:经典算法题每日演练--第二十三题 鸡尾酒排序     这篇我们继续扯淡一下鸡尾酒排序,为了知道为啥取名为鸡尾酒,特意看了下百科,见框框的话,也只能勉强这么说了.   要是文艺点的话,可以说是搅拌排序,通俗易懂点的话,就叫"双向冒泡排序",我想作为码农的话,不可能不知道冒泡排序, 冒泡是一个单向的从小到大或者从大到小的交换排序,而鸡尾酒排序是双向的,从一端进行从小到大排序,从另一端进行从大 到小排序. 从图中可以看到,第一次正向比较,我们找到了最大值9.             

经典算法题每日演练——第十题 树状数组

原文:经典算法题每日演练--第十题 树状数组         有一种数据结构是神奇的,神秘的,它展现了位运算与数组结合的神奇魅力,太牛逼的,它就是树状数组,这种数据结构不是神人是发现不了的. 一:概序      假如我现在有个需求,就是要频繁的求数组的前n项和,并且存在着数组中某些数字的频繁修改,那么我们该如何实现这样的需求?当然大家可以往 真实项目上靠一靠. ① 传统方法:根据索引修改为O(1),但是求前n项和为O(n). ②空间换时间方法:我开一个数组sum[],sum[i]=a[1]+..

经典算法题每日演练——第二十四题 梳排序

这篇再看看一个经典的排序,梳排序,为什么取名为梳,可能每个梳都有自己的gap吧,大梳子gap大一点,小梳子gap小一点. 上一篇我们看到鸡尾酒排序是在冒泡排序上做了一些优化,将单向的比较变成了双向,同样这里的梳排序也是在冒泡排序上做了一些优化. 冒泡排序上我们的选择是相邻的两个数做比较,就是他们的gap为1,其实梳排序提出了不同的观点,如果将这里的gap设置为一定的大小, 效率反而必gap=1要高效的多. 下面我们看看具体思想,梳排序有这样一个1.3的比率值,每趟比较完后,都会用这个1.3去递减

经典算法题每日演练——第二十一题 十字链表

     上一篇我们看了矩阵的顺序存储,这篇我们再看看一种链式存储方法"十字链表",当然目的都是一样,压缩空间. 一:概念     既然要用链表节点来模拟矩阵中的非零元素,肯定需要如下5个元素(row,col,val,down,right),其中: row:矩阵中的行. col:矩阵中的列. val:矩阵中的值. right:指向右侧的一个非零元素. down:指向下侧的一个非零元素. 现在我们知道单个节点该如何表示了,那么矩阵中同行的非零元素的表示不就是一个单链表吗?比如如下: 那么

经典算法题每日演练——第十五题 并查集

原文:经典算法题每日演练--第十五题 并查集     这一篇我们看看经典又神奇的并查集,顾名思义就是并起来查,可用于处理一些不相交集合的秒杀. 一:场景     有时候我们会遇到这样的场景,比如:M={1,4,6,8},N={2,4,5,7},我的需求就是判断{1,2}是否属于同一个集合,当然实现方法 有很多,一般情况下,普通青年会做出O(MN)的复杂度,那么有没有更轻量级的复杂度呢?嘿嘿,并查集就是用来解决这个问题的.   二:操作   从名字可以出来,并查集其实只有两种操作,并(Union)

经典算法题每日演练——第三题 猴子吃桃

原文:经典算法题每日演练--第三题 猴子吃桃           猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾就多吃了一个.第二天早上又将剩下的桃子吃了一半,还是不过瘾又多 吃了一个.以后每天都吃前一天剩下的一半再加一个.到第10天刚好剩一个.问猴子第一天摘了多少个桃子?   分析: 这是一套非常经典的算法题,这个题目体现了算法思想中的递推思想,递归有两种形式,顺推和逆推,针对递推,只要         我们找到递推公式,问题就迎刃而解了.                令S10=1,容易看

经典算法题每日演练——第六题 协同推荐SlopeOne 算法

原文:经典算法题每日演练--第六题 协同推荐SlopeOne 算法               相信大家对如下的Category都很熟悉,很多网站都有类似如下的功能,"商品推荐","猜你喜欢",在实体店中我们有导购来为我们服务,在网络上 我们需要同样的一种替代物,如果简简单单的在数据库里面去捞,去比较,几乎是完成不了的,这时我们就需要一种协同推荐算法,来高效的推荐浏览者喜 欢的商品. 一:概念      SlopeOne的思想很简单,就是用均值化的思想来掩盖个体的打