烫烫烫个喵啊

老裴的博客


  • 首页

  • 全部文章

  • 分类

  • 标签

  • 关于

  • 归档

文章一对多字段的分页查询

发表于 2018-05-13 更新于 2021-07-29 分类于 数据库 阅读次数:

        在写首页的展示文章功能的时候,当然是少不了分页的。文章需要展示标签,一篇文章可能有多个标签,是一对多的关系。某一篇文章有四条数据,如图



        测试一页展示两篇文章的时候,惊奇的发现,那一页只显示了一篇文章"使用正则获取...",而且四个标签只显示了两个,再看下上面查询的结果,似乎明白了,下一页是同样也是只有一篇文章,标签是剩下的两个。

        因为一篇文章有多个标签,所以limit不能写死,如下,要不然谁知道你一页会有几篇文章呢,反正一页的标签数是肯定一样的了。

select
a.*,t.*
from article a,tag t,article_tag a_t
where a.articleId=a_t.articleId and t.tagId=a_t.tagId
order by pubdate desc limit 5,2

        这时候想到的办法是是limit需要一个公式,动态的来分页,上面语句里的5和2都应该是动态变化的,比如一页十篇文章。但问题也随之而来,我如何知道第一页应该是哪十个?这个可以根据group by (articleId) 来保证一篇文章只显示一次,但又该如何知道一篇文章有几个标签,用count?那一篇文章就要查一次,效率实在低下,这个思路就先放一放了。

        那我们就一次查询完所有文章,然后前台分批次去取数据,但想一下,如果上万条文章怎么办,这一次查询要多久....而且查了那么多数据,我根本看不到那里,白查!不可取。

        那我只能先查文章主表,查出十条记录,然后在到关系表中查到相关的标签,一条文章就要查询一次标签。效率也比较低下。项目跑起来效果的确是这样,刷新一次需要等待肉眼可见的时间,体验很差。。。但就先凑活着,我相信这绝对不是好方案,sql会有相关的知识,而我不知道罢了。

        果然两三天后,一不小心浏览到group_concat,能将多行数据整合到一行上面,配合group by 的话,这不就是我想要的吗?立刻尝试编写sql语句


select   
    a.*,group_concat(t.tagName)
    from article a,tag t,article_tag a_t
where a.articleId=a_t.articleId and t.tagId=a_t.tagId
group by articleId
order by pubdate desc



        视图多了一个长文本字段,查看一下,会发现已经把所有想要的添加进来了,并且默认使用逗号进行分割,总算找到你了,知识的广度很重要。


        下面就只要修改下mapper的XML文件,大功告成了。刷新下主页,获取文章的速度明显变快。

  <resultMap id="ArticleTagMap2" type="net.peihuan.entity.Article" extends="ResultMapWithBLOBs">
<result column="group_concat(t.tagName)" property="grouptags" jdbcType="LONGVARCHAR" />
</resultMap>
<!-- 查询所有文章基本信息及他们的标签 -->
<select id="selectAllArticlesWithTags" parameterType="map" resultMap="ArticleTagMap2">
select
a.*,group_concat(t.tagName) from
article a,tag t,article_tag a_t
where a.articleId=a_t.articleId
and t.tagId=a_t.tagId
group by articleId
order by pubdate desc
limit #{beginIndex},#{offset}
</select>


        发现mybatics自带分页功能,可以了解一下。


关于

发表于 2018-05-11 更新于 2021-07-29 分类于 杂记 阅读次数:

我的博客

        我想绝大多数写代码的都想有一个属于自己的域名,属于自己的网站,我也一样,只不过一直没有机会实现。但自从某日浏览了一个博客,嗯,这也太好看了吧?怎么做的?教练,我想学这个...恰巧这段时间刚好在学web开发,天时地利人和。

        看了下喜欢的那个博客,down下来是hexo的主题,看着一堆没见过的后缀名,和屏幕对脸懵逼,啥玩意啊,不会用。不会前端,只会简单改改html文件,于是便使用wget命令直接把整个网站拿了下来变成了静态的。前台剩下也就没什么了。

        后台使用的java开发,IDE用的idea,稍微有点jsp、servlet的底子,会写个简单的注册登录。但想用框架,花了一天时间按网上的教程搭了个ssm框架,写了个简单的demo,算是完成了。剩下的无非就是对数据库的增删改查了,富文本编辑器一开始用的百度的,但一直乱码,百度貌似也不维护了,最后换了wangeditor,非常轻量简洁,很喜欢。

        功能基本完成的差不多之后,觉得URL里面是jsp非常丑,其实前台全部使用的ajax异步获取的数据,遂全部将后缀改成html。还有一些小问题还需要慢慢解决!如有人发现了一些bug,请告知我,不胜感激。


个人

        95后,即将毕业于某小县城末流二本。

        不抽烟、不喝酒、不近女色(假的)、

        喜欢技术,热衷于制造bug!

        喜欢唱歌,可惜五音不全、喜欢跳舞,可惜肢体不协调、

        喜欢画画,又奈何是个灵魂画手、

        喜欢吹笛子,奈何连声都吹不响、

        喜欢虾jb逛,不对,喜欢旅游,但是又穷、

        能玩的起来的也就是睡睡觉、打打王者了

        攒钱,有很多想去的地方、

        搞艺术是不可能搞艺术的了,这辈子都搞不起来了,超级羡慕会唱歌跳舞画画之类的,现在只能靠搬搬代码这样子才
        能维持得了生活,github里面的老哥个个都是人才,说话又好听,我超喜欢这里的。

html使用正则获取url传的参数

发表于 2018-05-11 更新于 2021-07-29 分类于 正则表达式 阅读次数:

在搭建这个博客的时候,前台一开始都使用的jsp,使用了很多<% %>,比较难看,可读性也较差,后来逐渐全部换成了ajax获取数据,这时候觉得已经没必要使用jsp了。

使用jsp后缀也导致URL链接也非常丑(个人感觉),遂把所有jsp后缀改成html。刷新页面发现报错,

<%request.getParameter("xxx")%>

因为不是jsp,所以这种方法也行不通了。
但js里面有一个方法可以获取页面的URL地址

window.location 对象所包含的属性

属性

描述

hash从井号 (#) 开始的 URL(锚)
host主机名和当前 URL 的端口号
hostname            当前 URL 的主机名
href完整的 URL
pathname当前 URL 的路径部分
port当前 URL 的端口号
protocol当前 URL 的协议
search从问号 (?) 开始的 URL(查询部分)


我们使用

window.location.search 来获取?之后的所有参数。

window.location.search.substr(1)

使用substr(1)来去掉 ? 

reg = new RegExp("(^|&)" + name + "=([^&])(&|$)", "i");

使用正则,如下图,i表示忽略大小写

var r = window.location.search.substr(1).match(reg);

匹配之后会的r为一个数组,长度为4。第一个是整个匹配的字符串,第二个是group1,第三个是group2,第四个是group3。因为使用了捕获,如果不行使用捕获的话括号里面加个?:就可以了。如(?:^|&)。

那么r[2]就是我们想要的参数,完整函数如下

function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&])(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) return decodeURI(r[2]); return null;
}




平衡二叉树(AVL树)的实现

发表于 2018-05-09 更新于 2021-07-29 分类于 数据结构 阅读次数:

复习一下平衡二叉树,经常混以为就是最优二叉树,其实是最优二叉树是哈夫曼树,这个以后有机会再说。

平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,它须保证树的深度是O(log N)。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。 最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,可以参考Fibonacci(斐波那契)数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。

  这里主要思考的是二叉树的插入节点。查询节点和修改节点的操作都可以以时间O(logN)的复杂度执行,假如删除使用惰删除的话,那么时间也是O(logN)。所谓惰删除就是当删除一个节点时,并不是真正的进行了物理删除,只是设置了一个字段来标记这个节点是否被删除。使用惰删除是有很多好处的,物理删除可能将会像插入节点一样需要进行旋转,当然也可能不需要。但如过后期又想添加之前删除的节点,那么又只能重新插入节点,很大几率要进行旋转操作。如果使用惰删除,那么时间复杂度将大大降低,但是相应的会使用额外的存储空间。

  如果插入一个节点将导致一个树的不平衡,那么会有一个必须重新平衡的节点叫做α。由于任意节点最多有两个孩子,因此高度不平衡时,α两棵子树的高度差为2,这种不平衡可能会出现以下四种情况:

1.对α的左儿子的左子树进行一次插入。
2.对α的左儿子的右子树进行一次插入。
3.对α的右儿子的左子树进行一次插入。
4.对α的右儿子的右子树进行一次插入。

  情况1和4是对称的,情况2和3是对称的,所以实际上只有两种情况,但是编程还是要写四种情况。
  情况1和4是插入发生在“外边”的情况,左-左,右-右,这种情况通过一次单选转就可以完成调整,剩下两种情况是插入发生在“内部”的情况,左-右,右-左,这种情况需要进行双旋转来进行调整。

插入原理

单旋转

  上图是左单旋转,对k2的左儿子k1的左子树进行的一次插入,X、Y、Z分别表示表示k1、k2的儿子,可以看见X明显比Y和Z深,且比Z深2层。为恢复平衡,就应该把X上移一层,Z下移一层,这样就变成了上图中旋转后的样子,k2变成了k1的右孩子,Y变成了k2的左孩子,X和Z不变。

  右旋转类似,对k1的右孩子k2的右孩子进行了一次插入,导致Z的深度比X大2,同样是把X下移一层,Z上移一层,k1就变成了k2的左孩子,Y变成了k1的右孩子。

  个人是这样记忆的,如何找到哪个是k1哪个是k2呢?k1和k2必定有一个是不平衡的,先找到这两个节点,然后不管是左还是右旋转,都让k1小于k2,也就是大的节点是k2,小的节点是k1,k1、k2互换高度,一个上移,一个下移。

双旋转

但是单旋转无法解决另外两种情况

  我们要把Y看成是一个根和两棵子树,B和C中有一个比D深两层(除非它们都是空的),我们不能确定是哪一棵,也不需要确定是哪一棵,上图中画的时候B和C全部画的比D低一层半。整颗子树,我们看成四棵子树A、B、C、D和三个节点!k1、k2、k3。
  为了重新平衡,我们不能让k3继续当根节点,k1页不行,唯一的选择就是k2当做新的根。k1、k3分别作为k2的左右孩子,巧妙的是,A、B、C、D四棵子树依旧是按顺序排列过去的。

右-左双旋转如上,基本差不多。

总结下,遇到双旋转,先找到k1、k2、k3,三个节点最小的是k1,最大的是k2,旋转后,k1是k2左孩子,k3是k2右孩子,A、B、C、D依旧是原顺序排列。

对于插入,最多只需要一次旋转(如果双旋转也只视作一次旋转)

删除原理

AVL树的删除和BST的删除较为类似,首先找到需要删除的节点,找到之后,分为三种情况进行删除。

  • 需要删除的节点D是叶子节点,这种情况最简单,删除D之后,判断父节点DP是否需要调整,调整完成之后再调整DPP,DPPP,直到回溯到根节点。

  • 需要删除的节点D有一个孩子,将D的值改为孩子的值,然后删除叶子节点,这样就把情况转换成第一种情况了。

  • 需要删除的节点D有两个孩子,将D的值改为前驱或者后继的值,然后删除前驱或者后继的值,这样也就把情况装换成第一种情况了。

由此可见,我们只要重点讨论第一种情况就可以了,具体的删除流程如下:

  1. 删除节点D

  2. 判断删除节点的父节点DP是否满足AVL树的性质,如果不满足,则根据DP的状态进行旋转调整,并重新计算DP的高度,旋转分为四种情况,这里就不再细说。

  3. 继续向上调整DPP,检查DPP的平衡是否遭到破坏,之后再继续向上调整DPP,直到调整到根节点为止(即不断执行第三步)。实际上这里可以进行优化,在调整过程中如果经过某一次的调整,树的高度没有发生变化,那么就不必向上回溯调整了。

如何判断是否需要向上回溯调整呢?根据平衡因子。
  如果删除一个节点后,平衡因子为1/-1,说明高度未发生变化,那么就不需要向上调整了;如果删除一个节点后,因子变成0,说明树高度一定发生了变化,那么就需要向上回溯调整。如果平衡因子为2/-2,说明需要调整这棵树需要调整。

这里可以看到AVL树的可视化。

代码实现

  将一个新节点,插入到AVL树中去,递归的插入到相应的子树中后,如果树的高度不变,那么插入完成,如果出现了高度不平衡,那么我们将做适当的双旋转或者单旋转,并更新这些高度。另外一个问题就是二叉树的高度信息的存储,由于真正需要的是实际是子树高度的差,并且它一般很小,(1,0,-1等)

java的完整实现 点这里

下面给出的是C的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class AvlNode {
public:
AvlNode *left;
AvlNode *right;
int val;
int height; //这个节点的高度

AvlNode() {
left = NULL;
right = NULL;
val = -9999;
height = 0;
}
AvlNode(int v) {
left = NULL;
right = NULL;
val = v;
height = 0;
}

};
1
2
3
4
5
6
7
8
/*返回某个节点的高度*/
static int Height(AvlNode* node)
{
if (node == NULL)
return 0;
else
return node->height;
}
1
2
3
4
5
6
7
8
9
10
11
12
/*左单旋转*/
static AvlNode* SingleRotateWithLeft(AvlNode *k2)
{
AvlNode *k1 = k2->left;
k2->left = k1->right;
k1->right = k2;

k2->height = max(Height(k2->left), Height(k2->right)) + 1;
k1->height = max(Height(k1->left), k2->height) + 1;

return k1;
}
1
2
3
4
5
6
7
8
9
10
11
12
/*右单旋转*/
static AvlNode* SingleRotateWithRight(AvlNode *k1)
{
AvlNode *k2 = k1->right;
k1->right = k2->left;
k2->left = k1;

k1->height = max(Height(k1->left), Height(k1->right)) + 1;
k2->height = max(k1->height, Height(k2->right)) + 1;

return k2;
}
1
2
3
4
5
6
7
8
9
/*左-右双旋转*/
static AvlNode* DoubleRotateWithLeft(AvlNode *k3)
{
/*k1,k2先右单旋转*/
k3->left = SingleRotateWithRight(k3->left);

/*k3,k2再左单旋转*/
return SingleRotateWithLeft(k3);
}
1
2
3
4
5
6
7
8
9
/*右-左双旋转*/
static AvlNode* DoubleRotateWithRight(AvlNode *k1)
{
/*k3,k2先左单旋转*/
k1->right= SingleRotateWithLeft(k1->right);

/*k1,k2再右单旋转*/
return SingleRotateWithRight(k1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
向AVL树种插入节点
*/
AvlNode* insert(int val, AvlNode *T)
{
if (T == NULL)
{
T = new AvlNode(val);
}
/*向左孩子插入*/
else if (val < T->val)
{
T->left = insert(val, T->left);
if (Height(T->left) - Height(T->right) == 2)
{
if (val < T->left->val)
T = SingleRotateWithLeft(T);
else
T = DoubleRotateWithLeft(T);

}
}
/*向右孩子插入*/
else if (val > T->val)
{
T->right = insert(val, T->right);
if (Height(T->right) - Height(T->left) == 2)
{
if (val > T->right->val)
T = SingleRotateWithRight(T);
else
T = DoubleRotateWithRight(T);
}
}

/*计算这个节点新的高度*/
T->height = max(Height(T->left), Height(T->right)) + 1;
return T;

}

使用正则获取文章摘要

发表于 2018-05-08 更新于 2021-07-29 分类于 正则表达式 阅读次数:

在想如何获取文章摘要的时候,有若干个想法,
第一,在获取到文章后,直接截取前100个字符,但因为是富文本,存在数据库时有很多html标签,如果前一百个截断了html怎么办?或者去掉所有html标签再截取,但就失去了文本格式;
第二,在数据库中文章表中添加一个摘要字段,如果写文章时候不修改,那么就默认为第一段或者前多少个字,当然也可以手动修改,但是非常不想用这样方法。

之后,在网上看到别人想法,使用第一个<p></p>标签中的文字作为摘要。而且正常第一段文字的确是大部分技术类博客的主要内容。

第一想法就是使用正则,来找到第一个<p>,但是<p>里面可能会有张<img> 这不是我们想要的摘要,所以我们需要过滤掉<img>,边学边用正则,起初脑子里一直盯着^这个字符(取反),比如这样^(<img),或者(^<img),当然都是不对的,应当使用前瞻,我要匹配<p>且下一个字符不是<img>的,中间任意,最后以</p>结尾.

(?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置

直接给出代码

private String getDigest(String content){
//正则筛选第一个文字<p>
Pattern datePattern = Pattern.compile("<p>((?!<img).)*?<\\/p>");
Matcher dateMatcher = datePattern.matcher(content);
if(dateMatcher.find()) {
return dateMatcher.group(); //返回第一个<p>
}else
{
return "null";
}
}



1…89

烫

技术、生活随笔
85 日志
24 分类
27 标签
GitHub Weibo
Links
  • 月灵博客
  • Bobby John's Blog
  • Zunpeng' Blog
皖ICP备18010985号-1 © 2022 烫
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Muse v7.3.0