-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
116 lines (63 loc) · 93.3 KB
/
atom.xml
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>TanAn</title>
<link href="https://shiququ.github.io/atom.xml" rel="self"/>
<link href="https://shiququ.github.io/"/>
<updated>2022-07-23T23:21:53.840Z</updated>
<id>https://shiququ.github.io/</id>
<author>
<name>TanAn</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>使用Redis构建文章投票网站</title>
<link href="https://shiququ.github.io/2022/07/24/2022072401/"/>
<id>https://shiququ.github.io/2022/07/24/2022072401/</id>
<published>2022-07-23T16:17:31.319Z</published>
<updated>2022-07-23T23:21:53.840Z</updated>
<content type="html"><![CDATA[<p>功能介绍:发布文章,对文章投票,对文章进行分组,具有文章评分功能。</p><span id="more"></span><p>为了能够产生一个随着时间流逝而不断减少的评分,程序需要根据文章发布时间来计算,具体的计算方法为:将文章的得到的支持票数乘以一个常量,然后加上文章的发布时间,得到的结果就是文章的评分。我们使用Unix时间作为发布时间来计算文章的评分。</p><p>当然,以下功能有一些问题,比如没有事务,缓存失效等,但是请暂时忽略这些问题。</p><h2 id="定义常量"><a href="#定义常量" class="headerlink" title="定义常量"></a>定义常量</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//为了尽量节约内存,设置投票截至时间,一周之后不允许投票</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="variable">ONE_WEEK_IN_SECONDS</span> <span class="operator">=</span> <span class="number">7</span> * <span class="number">86300</span>;</span><br><span class="line"><span class="comment">//设置票数的权重常量</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">double</span> <span class="variable">VOTE_SCORE</span> <span class="operator">=</span> <span class="number">432</span>;</span><br><span class="line"><span class="comment">//设置每一页的文章数量</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="variable">ARTICLES_PER_PAGE</span> <span class="operator">=</span> <span class="number">25</span>;</span><br><span class="line"><span class="comment">//获取Redis连接</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">Jedis</span> <span class="variable">jedis</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Jedis</span>(<span class="string">"111.111.111.111"</span>, <span class="number">6379</span>);</span><br></pre></td></tr></table></figure><p>这里的权重常量是432,是十分巧妙地,假如每一篇文章有200票的评分,我们认为它是好的文章,就将它排在前面一天,一天是86400秒,那么86400/200=432,而如果这篇文章的票数是200票,那么下一天的文章就默认具有了86400的评分,所以自然会将昨天的200票的文章从前面挤下去,相当于文章每有200票的评分,就可以在热榜上多停留一天,并且会自动从热榜下去。</p><h2 id="实现发布文章功能"><a href="#实现发布文章功能" class="headerlink" title="实现发布文章功能"></a>实现发布文章功能</h2><p>我们获取到作者,文章标题,文章链接,将文章的信息作为hash存储,</p><p>设置作者为默认支持文章,将作者id加入到当前文章的支持者集合中</p><p>设置支持者集合的过期时间,一周后不允许投票,节省内存</p><p>将文章的评分计算出并加入到所有文章评分的集合中</p><p>返回文章id</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 文章发布</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> user 用户id;如 1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> title 文章标题;如 使用Redis实现文章投票网站</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> link 文章链接</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 文章id</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">post_article</span><span class="params">(String user, String title, String link)</span> {</span><br><span class="line"><span class="comment">//生成文章id,文章统一使用article:为前缀,当然你也可以使用article. 又或 article/ ,不过建议使用article: 因为官方也是使用:</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">article_id</span> <span class="operator">=</span> String.valueOf(jedis.incr(<span class="string">"article:"</span>));</span><br><span class="line"> <span class="comment">//生成文章的投票集合</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">voted</span> <span class="operator">=</span> <span class="string">"voted:"</span> + article_id;</span><br><span class="line"> <span class="comment">//将作者添入其中,默认作者是支持状态</span></span><br><span class="line"> jedis.sadd(voted, user);</span><br><span class="line"> <span class="comment">//设置过期时间,超过一周之后变不允许投票</span></span><br><span class="line"> jedis.expire(voted, ONE_WEEK_IN_SECONDS);</span><br><span class="line"><span class="comment">//获取当前的unix时间</span></span><br><span class="line"> <span class="type">Long</span> <span class="variable">now</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line"> HashMap<String, String> hashMap = <span class="keyword">new</span> <span class="title class_">HashMap</span><>();</span><br><span class="line"> <span class="comment">//设置文字标题</span></span><br><span class="line"> hashMap.put(<span class="string">"title"</span>, title);</span><br><span class="line"> <span class="comment">//文章链接</span></span><br><span class="line"> hashMap.put(<span class="string">"link"</span>, link);</span><br><span class="line"> <span class="comment">//发布作者</span></span><br><span class="line"> hashMap.put(<span class="string">"poster"</span>, user);</span><br><span class="line"> <span class="comment">//发布Unix时间</span></span><br><span class="line"> hashMap.put(<span class="string">"time"</span>, String.valueOf(now));</span><br><span class="line"> <span class="comment">//初始票数,作者的支持票</span></span><br><span class="line"> hashMap.put(<span class="string">"votes"</span>, <span class="string">"1"</span>);</span><br><span class="line"> <span class="comment">//文章的hash存储</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">article</span> <span class="operator">=</span> <span class="string">"article:"</span> + article_id;</span><br><span class="line"> jedis.hmset(article, hashMap);</span><br><span class="line"> hashMap.clear();</span><br><span class="line"> </span><br><span class="line"> HashMap<String, Double> hashMap1 = <span class="keyword">new</span> <span class="title class_">HashMap</span><String, Double>();</span><br><span class="line"> hashMap1.put(article, now + VOTE_SCORE);</span><br><span class="line"> <span class="comment">//文章的评分存储,zset类型的score:键存储所有的文章评分</span></span><br><span class="line"> jedis.zadd(<span class="string">"score:"</span>, hashMap1);</span><br><span class="line"> hashMap1.clear();</span><br><span class="line"> hashMap1.put(article, Double.valueOf(now));</span><br><span class="line"> <span class="comment">//文章的发布时间存储,zset类型的time:键存储所有的文章评分</span></span><br><span class="line"> jedis.zadd(<span class="string">"time:"</span>, hashMap1);</span><br><span class="line"> <span class="comment">//返回文章id,如 article:1</span></span><br><span class="line"> <span class="keyword">return</span> article_id;</span><br><span class="line"> }</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="实现文章投票"><a href="#实现文章投票" class="headerlink" title="实现文章投票"></a>实现文章投票</h2><p>接收当前用户的id,文章id,支持或者反对</p><p>获取当前时间,通过文章的hash获取文章的发布时间,判断是否超过一周</p><p>没有超过一周,</p><p>判断用户是否已经投过票了,进行相应的处理,支持或者反对,修改score:有序集合中文章评分,修改文章hash的票数</p><p>修改文章的支持者或反对者集合中的用户id</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 对文章进行投票</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> user 投票用户id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> article 文章id,如 article:1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> st 投票,传入1,支持,-1反对</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">article_vote</span><span class="params">(String user, String article, <span class="type">int</span> st)</span> {</span><br><span class="line"> <span class="type">LocalDateTime</span> <span class="variable">time</span> <span class="operator">=</span> LocalDateTime.now();</span><br><span class="line"> <span class="comment">//获取当前的Unix时间</span></span><br><span class="line"> <span class="type">int</span> <span class="variable">cutoff</span> <span class="operator">=</span> time.getSecond() - ONE_WEEK_IN_SECONDS;</span><br><span class="line"> <span class="comment">//如果发布超过一周,结束投票</span></span><br><span class="line"> <span class="keyword">if</span> (jedis.zscore(<span class="string">"time:"</span>, article) < cutoff) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//分割字符串获取文章真实id,如1</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">article_id</span> <span class="operator">=</span> article.split(<span class="string">":"</span>)[<span class="number">1</span>];</span><br><span class="line"> <span class="comment">//判断支持,反对</span></span><br><span class="line"> <span class="keyword">if</span> (st < <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">//反对</span></span><br><span class="line"> <span class="comment">//"voted:" + article_id ,如voted:1,表示支持article:1的用户id的集合</span></span><br><span class="line"> <span class="comment">//"novoted:" + article_id ,如novoted:1,表示反对article:1的用户id的集合</span></span><br><span class="line"> update(<span class="string">"voted:"</span> + article_id, <span class="string">"novoted:"</span> + article_id, user, article, st);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//支持</span></span><br><span class="line"> update(<span class="string">"novoted:"</span> + article_id, <span class="string">"voted:"</span> + article_id, user, article, st);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 更新支持或反对集合,更新文章评分</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> src 源集合</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dest 目标集合</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> member set元素名称</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> article 文章id,如article:1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> st 票数,1或-1</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(String src, String dest, String member, String article, <span class="type">int</span> st)</span> {</span><br><span class="line"> <span class="comment">//将member可从src移动到dest,移动成功,那么返回1.说明该用户之前已经投票,而这次投相反的票,直接else。</span></span><br><span class="line"> <span class="keyword">if</span> (jedis.smove(src, dest, member) == <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">//这个用户之前没有投过票或投了与之前相同的票</span></span><br><span class="line"> <span class="comment">////添加成功,返回1,说明没有投过票,添加失败返回0,说明与之前的票相同,不需要修改数据</span></span><br><span class="line"> <span class="keyword">if</span>(jedis.sadd(dest, member)==<span class="number">1</span>){</span><br><span class="line"> <span class="comment">//修改评分</span></span><br><span class="line"> jedis.zincrby(<span class="string">"score:"</span>, VOTE_SCORE * st, article);</span><br><span class="line"> <span class="comment">//修改如article:1中的票数</span></span><br><span class="line"> jedis.hincrBy(article,<span class="string">"votes"</span>,st);</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//修改评分</span></span><br><span class="line"> jedis.zincrby(<span class="string">"score:"</span>, VOTE_SCORE *<span class="number">2</span>*st, article);</span><br><span class="line"> <span class="comment">//修改如article:1中的票数</span></span><br><span class="line"> jedis.hincrBy(article,<span class="string">"votes"</span>,<span class="number">2</span>*st);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h2 id="实现按评分取出文章"><a href="#实现按评分取出文章" class="headerlink" title="实现按评分取出文章"></a>实现按评分取出文章</h2><p>获取页面与包括文字id与评分有序集合,</p><p>处理出当前页的起始序号与结束序号</p><p>获得当前页文章的有序集合</p><p>创建map数组存储结果</p><p>遍历有序集合并获取每一篇文章的hash数据,存储到map中,并将当前文章的id加入到map中</p><p>遍历时将map加入数组中,</p><p>返回数据</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 返回order中所有文章的数组,评分从大到小,并分页</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> page 页码</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> order 需要处理的有序集合,包括文字id与评分</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> ArrayList<Map<String, String>> <span class="title function_">get_articles</span><span class="params">(<span class="type">int</span> page, String order)</span> {</span><br><span class="line"> <span class="comment">//起始文章序号</span></span><br><span class="line"> <span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> (page - <span class="number">1</span>) * ARTICLES_PER_PAGE;</span><br><span class="line"> <span class="comment">//结束文章序号</span></span><br><span class="line"> <span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> start + ARTICLES_PER_PAGE - <span class="number">1</span>;</span><br><span class="line"> <span class="comment">//zrevrange,按score从大到小返回结果,左闭右开</span></span><br><span class="line"> <span class="comment">//这里你可能有些疑问,为什么无序的set集合可以存储有序的zset返回的值呢,下面我再解释</span></span><br><span class="line"> Set<String> ids = jedis.zrevrange(order, start, end);</span><br><span class="line"> <span class="comment">//存储结果集</span></span><br><span class="line"> ArrayList<Map<String, String>> articles = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> <span class="keyword">for</span> (String id : ids) {</span><br><span class="line"> <span class="comment">//取出文章的hash信息</span></span><br><span class="line"> Map<String, String> article_data = jedis.hgetAll(id);</span><br><span class="line"> <span class="comment">//加入文章的id,如articles:1,因为我们在之前发布文章时并没有存储id,而是使用id作为了hash结果的键</span></span><br><span class="line"> article_data.put(<span class="string">"id"</span>, id);</span><br><span class="line"> <span class="comment">//加入到ArrayList</span></span><br><span class="line"> articles.add(article_data);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//返回结果集</span></span><br><span class="line"> <span class="keyword">return</span> articles;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h3 id="无序的set可以存储有序的zset返回的值"><a href="#无序的set可以存储有序的zset返回的值" class="headerlink" title="无序的set可以存储有序的zset返回的值"></a>无序的set可以存储有序的zset返回的值</h3><p>我们可以看一下源码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SetFromList</span><E> <span class="keyword">extends</span> <span class="title class_">AbstractSet</span><E> <span class="keyword">implements</span> <span class="title class_">Serializable</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> -<span class="number">2850347066962734052L</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">transient</span> List<E> list;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="title function_">SetFromList</span><span class="params">(List<E> list)</span> {</span><br><span class="line"> <span class="keyword">if</span> (list == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>(<span class="string">"list"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">this</span>.list = list;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>虽然SetFromList继承AbstractSet抽象类,但是真正存储数据的是List,而List是有序的。</p><p>因此这里只是一个套壳的Set,至于为什么这么做,我不知道。</p><h2 id="对文章进行分组"><a href="#对文章进行分组" class="headerlink" title="对文章进行分组"></a>对文章进行分组</h2><p>创建分组的set,将文章id加入或移出相应的分组即可</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 对文章进行添加关键字或移除关键字</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> article_id 文章id,如1</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> to_add 需要添加的关键字</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> to_remove 需要删除的关键字</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">add_remove_groups</span><span class="params">(String article_id, String[] to_add, String[] to_remove)</span> {</span><br><span class="line"> <span class="comment">//拼接处文章的hash键</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">articl</span> <span class="operator">=</span> <span class="string">"article:"</span> + article_id;</span><br><span class="line"> <span class="comment">//将articl加入相关分组</span></span><br><span class="line"> <span class="keyword">for</span> (String s : to_add) {</span><br><span class="line"> jedis.sadd(<span class="string">"group:"</span> + s, articl);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//将articl移出不相关分组</span></span><br><span class="line"> <span class="keyword">for</span> (String s : to_add) {</span><br><span class="line"> jedis.srem(<span class="string">"group:"</span> + s, articl);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h2 id="实现分组文章按评分取出"><a href="#实现分组文章按评分取出" class="headerlink" title="实现分组文章按评分取出"></a>实现分组文章按评分取出</h2><p>为了能够对群组文章进行分页和排序,网站需要将同一个分组里面的所有文章按照评分存储到一个有序集合中,ZINTERSTORE命令可以接收多个集合或多个有序集合作为输入,找出所有同时存在与集合和有序集合地成员,并以几种不同地方式合这些成员的分值,所有集合成员的分值都是1,请注意,不包含有序集合。</p><p>因为如果群组中的文章非常多,那么执行该命令会花费较长时间,因此我们将计算结果缓存60秒</p><p>因为存在缓存,所以先查看是否存在缓存,如果不存在,将scor:有序集合与分组集合进行ZINTERSTORE处理,设置过期时间,将得到的结果调用get_articles函数进行分页返回即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** </span></span><br><span class="line"><span class="comment"> * 返回群组文章按评分排序后的结果</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> group 组别</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> page 页码</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> order 默认 "score:" 因为这里面存储有所有文章的评分</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> ArrayList<Map<String, String>> <span class="title function_">get_group_articles</span><span class="params">(String group, <span class="type">int</span> page, String order)</span> {</span><br><span class="line"> <span class="comment">//拼接存储结果的有序集合的键</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> order + group;</span><br><span class="line"> <span class="comment">//如果不存在该元素</span></span><br><span class="line"> <span class="keyword">if</span> (!jedis.exists(key)) {</span><br><span class="line"> <span class="comment">//结果分数取相同元素的最大值,其他类型还有MIN,SUM</span></span><br><span class="line"> <span class="type">ZParams</span> <span class="variable">aggregate</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ZParams</span>().aggregate(ZParams.Aggregate.MAX);</span><br><span class="line"> <span class="comment">//处理集合获取结果,</span></span><br><span class="line"> jedis.zinterstore(key, aggregate, <span class="string">"group"</span> + group, order);</span><br><span class="line"> <span class="comment">//设置超时</span></span><br><span class="line"> jedis.expire(key,<span class="number">60</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//调用get_articles进行key有序集合中元素的分页。</span></span><br><span class="line"> <span class="keyword">return</span> get_articles(page, key);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>功能介绍:发布文章,对文章投票,对文章进行分组,具有文章评分功能。</p></summary>
<category term="Redis" scheme="https://shiququ.github.io/categories/Redis/"/>
<category term="Redis" scheme="https://shiququ.github.io/tags/Redis/"/>
</entry>
<entry>
<title>关于Redis在windows上运行及fork函数问题</title>
<link href="https://shiququ.github.io/2022/07/23/2022072301/"/>
<id>https://shiququ.github.io/2022/07/23/2022072301/</id>
<published>2022-07-23T02:55:49.164Z</published>
<updated>2022-07-23T04:25:47.445Z</updated>
<content type="html"><![CDATA[<p>Redis在将数据库进行持久化操作时,需要fork一个进程,但是windows并不支持fork<span id="more"></span>,导致在持久化操作期间,Redis必须阻塞所有的客户端直至持久化操作完成。微软的一些工程师花费时间在解决在windows环境下Redis无法进行后台保存,并决定使用线程代替fork产生的子进程来对硬盘执行写操作,但这给分支只提供了源码并没有提供预编译二进制文件,并且微软不保证它能否用于生产环境。</p><p>上面这段是我摘抄于黄健宏老师翻译的《Redis实战》这本书的。</p><p>下面我解释一下fork函数的一些问题</p><ul><li>fork函数</li></ul><p>fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程。</p><p>下面的侧重点在于fork与递归的区分。</p><h1 id="创建fork-test1-c"><a href="#创建fork-test1-c" class="headerlink" title="创建fork_test1.c"></a>创建fork_test1.c</h1><p> 我们可以touch fork_test1.c,</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><sys/types.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> **argv)</span> {</span><br><span class="line"> <span class="type">pid_t</span> pid = fork();</span><br><span class="line"> <span class="keyword">if</span> (pid==<span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"我是父进程,pid是: %d\n"</span>,getpid());</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (pid > <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"我是子进程,pid是: %d\n"</span>,getpid());</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error while forking\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 使用cc fork_test.c进行编译。</p><p> 执行可执行文件a.out</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">./a.out</span><br><span class="line">我是子进程,pid是: 1492</span><br><span class="line">我是父进程,pid是: 1493</span><br></pre></td></tr></table></figure><h1 id="为什么没有死递归"><a href="#为什么没有死递归" class="headerlink" title="为什么没有死递归"></a>为什么没有死递归</h1><p> 观察执行结果会发现的子进程的返回值是0,至于为什么是0,暂不讨论。</p><p> 我好奇的是程序为什么没有进行死递归,因为我认为程序会复制一份重新运行。</p><p> 经过一番查阅后发现,因为,fork是复制父进程的数据段,堆,栈等,因此,父进程与子进程执行程序位置也是相同的,函数fork完之后,父进程与子进程同时执行到到了第一个if语句,因此子进程并不会重新执行fork()函数,因此没有递归。</p><p> 验证一下</p><p> 我们可以touch fork_test2.c,</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stdlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><unistd.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><sys/types.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> **argv)</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"abcd"</span>);</span><br><span class="line"> <span class="type">pid_t</span> pid = fork();</span><br><span class="line"> <span class="keyword">if</span> (pid==<span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"我是父进程,pid是: %d\n"</span>,getpid());</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (pid > <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"我是子进程,pid是: %d\n"</span>,getpid());</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Error while forking\n"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(EXIT_FAILURE);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 编译</p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cc fork_test2.c</span><br></pre></td></tr></table></figure><p> 执行</p> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./a.out</span><br></pre></td></tr></table></figure><p> 你会发现程序没有死递归。</p><h1 id="上面难道是错的"><a href="#上面难道是错的" class="headerlink" title="上面难道是错的"></a>上面难道是错的</h1><p> 但是观察结果你会发现为什么”abcd”输出了两次,不是两个进程都执行到了第一个if语句吗?为什么子进程也会输出。</p><p> 上面讲的并没有错,你应该注意到,fork()的复制就包括输出缓冲区。</p><p> 而程序并不会立即把输出的数据显示到屏幕上,而是先存储在输出缓冲区中,当满足一定条件时才显示出来。</p><h1 id="输出缓冲区类型"><a href="#输出缓冲区类型" class="headerlink" title="输出缓冲区类型"></a>输出缓冲区类型</h1><p> 缓冲区的类型:</p><p> 缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。</p><p> 1、全缓冲</p><p> 在这种情况下,当填满标准I/O缓存后才进行行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。</p><p> 2、行缓冲</p><p> 在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。</p><p> 3、不带缓冲</p><p> 也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。</p><p> 所以,因为我们的fork_test2.c没有触发任何刷新缓冲区的操作,因此复制的时候子进程的缓冲区中也会有”abcd”,所以就会输出两次。</p><h1 id="如何触发缓冲区刷新"><a href="#如何触发缓冲区刷新" class="headerlink" title="如何触发缓冲区刷新"></a>如何触发缓冲区刷新</h1><p> 那么我们就触发缓冲区刷新的操作,如何触发呢?</p><blockquote><p>1、遇到\n<br>2、程序结束 <br>3、遇到输入语句<br>4、当缓冲区满时<br>5、fflush(stdout) 手动刷新</p></blockquote><p> 我们尝试用第一种方法修改代码,</p> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">#include<span class="operator"><</span>stdio.h<span class="operator">></span></span><br><span class="line">#include<span class="operator"><</span>stdlib.h<span class="operator">></span></span><br><span class="line">#include<span class="operator"><</span>unistd.h<span class="operator">></span></span><br><span class="line">#include<span class="operator"><</span>sys<span class="operator">/</span>types.h<span class="operator">></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> main(<span class="type">int</span> argc, <span class="type">char</span> <span class="operator">*</span><span class="operator">*</span>argv) {</span><br><span class="line"> printf("abcd\n");</span><br><span class="line"> pid_t pid <span class="operator">=</span> fork();</span><br><span class="line"> if (pid<span class="operator">=</span><span class="operator">=</span><span class="number">0</span>) {</span><br><span class="line"> printf("我是父进程,pid是: %d\n",getpid());</span><br><span class="line"> exit(<span class="number">0</span>);</span><br><span class="line"> } <span class="keyword">else</span> if (pid <span class="operator">></span> <span class="number">0</span>) {</span><br><span class="line"> printf("我是子进程,pid是: %d\n",getpid());</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> printf("Error while forking\n");</span><br><span class="line"> exit(EXIT_FAILURE);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="拨云见日"><a href="#拨云见日" class="headerlink" title="拨云见日"></a>拨云见日</h1><p> 然后重新编译运行,你会发现”abcd”只会被输出一次,子进程复制的输出缓冲区为空。到此解释了fork·与递归的一些问题。</p><p>如果有什么问题大家可以在下面留言。</p>]]></content>
<summary type="html"><p>Redis在将数据库进行持久化操作时,需要fork一个进程,但是windows并不支持fork</summary>
<category term="Redis" scheme="https://shiququ.github.io/categories/Redis/"/>
<category term="Redis" scheme="https://shiququ.github.io/tags/Redis/"/>
<category term="C" scheme="https://shiququ.github.io/tags/C/"/>
</entry>
<entry>
<title>如何超过大多数人</title>
<link href="https://shiququ.github.io/2022/07/22/2022072202/"/>
<id>https://shiququ.github.io/2022/07/22/2022072202/</id>
<published>2022-07-22T15:10:21.324Z</published>
<updated>2022-07-25T04:01:41.740Z</updated>
<content type="html"><![CDATA[<p><img src="https://atypora.oss-cn-hangzhou.aliyuncs.com/Typora/20220722232509-152.png" style="float:right" alt="图片">当你看到这篇文章的标题,你一定对这篇文章产生了巨大的兴趣,因为你的潜意识在告诉你,这是一本人生的“武林秘籍”,而且还是左耳朵写的,</p><span id="more"></span><p>一定有干货满满,只要读完,一定可以练就神功并找到超过大多数人的快车道和捷径……然而…… 当你看到我这样开篇时,你一定会觉得我马上就要有个转折,告诉你这是不可能的,一切都需要付出和努力……然而,你错了,这篇文章还真就是一篇“秘籍”,只要你把这些“秘籍”用起来,你就一定可以超过大多数人。而且,这篇文章只有我这个“人生导师”可以写得好。毕竟,我的生命过到了十六进制2B的年纪,踏入这个社会已超过20年,舍我其谁呢?!</p><p>P.S. 这篇文章借鉴于《<a href="https://coolshell.cn/articles/4758.html">如何写出无法维护的代码</a>》一文的风格……嘿嘿</p><p><a href="https://coolshell.cn/articles/19464.html#%E7%9B%B8%E5%85%B3%E6%8A%80%E5%B7%A7%E5%92%8C%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5">相关技巧和最佳实践</a><a href="https://coolshell.cn/articles/19464.html#%E7%9B%B8%E5%85%B3%E5%8E%9F%E7%90%86%E5%92%8C%E6%80%9D%E7%BB%B4%E6%A8%A1%E5%9E%8B">相关原理和思维模型</a><a href="https://coolshell.cn/articles/19464.html#%E8%AE%A4%E7%9F%A5">认知</a><a href="https://coolshell.cn/articles/19464.html#%E7%9F%A5%E8%AF%86">知识</a><a href="https://coolshell.cn/articles/19464.html#%E6%8A%80%E8%83%BD">技能</a><a href="https://coolshell.cn/articles/19464.html#%E9%A2%86%E5%AF%BC%E5%8A%9B">领导力</a></p><h4 id="相关技巧和最佳实践"><a href="#相关技巧和最佳实践" class="headerlink" title="相关技巧和最佳实践"></a>相关技巧和最佳实践</h4><p>要超过别人其实还是比较简单的,尤其在今天的中国,更是简单。因为,你只看看中国的互联网,你就会发现,他们基本上全部都是在消费大众,让大众变得更为地愚蠢和傻瓜。<strong>所以,在今天的中国,你基本上不用做什么,只需要不使用中国互联网,你就很自然地超过大多数人了</strong>。当然,如果你还想跟他们彻底拉开,甩他们几个身位,把别人打到底层,下面的这些“技巧”你要多多了解一下。</p><p>在信息获取上,你要不断地向大众鼓吹下面的这些事:</p><ul><li>让大家都用百度搜索引擎查找信息,订阅微信公众号或是到知乎上学习知识……要做到这一步,你就需要把“百度一下”挂在嘴边,然后要经常在群或朋友圈中转发微信公众号的文章,并且转发知乎里的各种“如何看待……”这样的文章,让他们爱上八卦,爱上转发,爱上碎片。</li><li>让大家到微博或是知识星球上粉一些大咖,密切关注他们的言论和动向……是的,告诉大家,大咖的任何想法一言一行都可以在微博、朋友圈或是知识星球上获得,让大家相信,你的成长和大咖的见闻和闲扯非常有关系,你跟牛人在一个圈子里你也会变牛。</li><li>把今日头条和抖音这样的APP推荐给大家……你只需要让你有朋友成功地安装这两个APP,他们就会花大量的时间在上面,而不能自拔,要让他们安装其实还是很容易的,你要不信你就装一个试玩一会看看(嘿嘿嘿)。</li><li>让大家热爱八卦,八卦并不一定是明星的八卦,还可以是你身边的人,比如,公司的同事,自己的同学,职场见闻,社会热点,争议话题,……这些东西总有一些东西会让人心态有很多微妙的变化,甚至花大量的时间去搜索和阅读大量的观点,以及花大量时间与人辩论争论,这个过程会让人上瘾,让人欲罢不能,然而这些事却和自己没有半毛钱关系。你要做的事就是转发其中一些SB或是很极端的观点,造成大家的一睦讨论后,就早早离场……</li><li>利用爱国主义,让大家觉得不用学英文,不要出国,不要翻墙,咱们已经是强国了……这点其实还是很容易做到的,因为学习是比较逆人性的,所以,只要你鼓吹那些英文无用论,出国活得更惨,国家和民族都变得很强大,就算自己过得很底层,也有大国人民的感觉。</li></ul><p>然后,在知识学习和技能训练上,让他们不得要领并产生幻觉</p><ul><li>让他们混淆认识和知识,以为开阔认知就是学习,让他们有学习和成长的幻觉……</li><li>培养他们要学会使用碎片时间学习。等他们习惯利用碎片时间吃快餐后,他们就会失去精读一本书的耐性……</li><li>不断地给他们各种各样“有价值的学习资料”,让他们抓不住重点,成为一个微信公众号或电子书“收藏家”……</li><li>让他们看一些枯燥无味的基础知识和硬核知识,这样让他们只会用“死记硬背”的方式来学习,甚至直接让他们失去信心,直接放弃……</li><li>玩具手枪是易用的,重武器是难以操控的,多给他们一些玩具,这样他们就会对玩具玩地得心应手,觉得玩玩具就是自己的专业……</li><li>让他们喜欢直接得到答案的工作和学习方式,成为一个伸手党,从此学习再也不思考……</li><li>告诉他们东西做出来就好了,不要追求做漂亮,做优雅,这样他们就会慢慢地变成劳动密集型……</li><li>让他们觉得自己已经很努力了,剩下的就是运气,并说服他们去‘及时行乐’,然后再也找不到高阶和高效率学习的感觉……</li><li>让他们觉得“读完书”、“读过书”就行了,不需要对书中的东西进行思考,进行总结,或是实践,只要囫囵吞枣尽快读完就等同于学好了……</li></ul><p>最后,在认知和格局上,彻底打垮他们,让他们变成韭菜。</p><ul><li>让他们尽可能地用拼命和加班,尽可能的996,并告诉他们这就是通往成功的唯一路径。这样一来,他们必然会被永远困在低端成为最低的劳动力。</li><li>让他们不要看到大的形势,只看到眼前的一亩三分地,做好一个井底之蛙。其实这很简单,就是不要告诉他还有另外一种活法,不要扩大他的认识……</li><li>宣扬一夜暴富以及快速挣钱的案例,最好让他们进入“赌博类”或是“传销类”的地方,比如:股市、数字货币……要让他们相信各种财富神话,相信他们就是那个幸运儿,他们也可以成为巴菲特,可以成为马云……</li><li>告诉他们,一些看上去很难的事都是有捷径的,比如:21天就能学会机器学习,用区块链就能颠覆以及重构整个世界等等……</li><li>多跟他们讲一些小人物的励志的故事,这样让他们相信,不需要学习高级知识,不需要掌握高级技能,只需要用低等的知识和低级的技能,再加上持续不断拼命重复现有的工作,终有一天就会成功……</li><li>多让他们跟别人比较,人比人不会气死人,但是会让人变得浮躁,变得心急,变得焦虑,当一个人没有办法控制自己的情绪,没有办法让自己静下心来,人会失去耐性和坚持,开始好大喜欢功,开始装逼,开始歪门邪道剑走偏锋……</li><li>让他们到体制内的一些非常稳定的地方工作,这样他们拥有不思进取、怕承担责任、害怕犯错、喜欢偷懒、得过且过的素质……</li><li>让他们到体制外的那些喜欢拼命喜欢加班的地方工作,告诉他们爱拼才会赢,努力加班是一种福报,青春就是用来拼的,让他们喜欢上使蛮力的感觉……</li><li>告诉他们你的行业太累太辛苦,干不到30岁。让他们早点转行,不要耽误人生和青春……</li><li>当他们要做决定的时候,一定要让他们更多的关注自己会失去的东西,而不是会得到的东西。培养他们患得患失心态,让他们认识不到事物真正的价值,失去判断能力……(比如:让他们觉得跟对人拍领导的马屁忠于公司比自我的成长更有价值)</li><li>告诉他们,你现有的技能和知识不用更新,就能过好一辈子,新出来的东西没有生命力的……这样他们就会像我们再也不学习的父辈一样很快就会被时代所抛弃……</li><li>每个人都喜欢在一些自己做不到的事上找理由,这种能力不教就会,比如,事情太多没有时间,因为工作上没有用到,等等,你要做的就是帮他们为他们做不到的事找各种非常合理的理由,比如:没事的,一切都是最好的安排;你得不到的那个事没什么意思;你没有面好主要原因是那个面试官问的问题都是可以上网查得到的知识,而不没有问到你真正的能力上;这些东西学了不用很快会忘了,等有了环境再学也不迟……</li></ul><p><strong>最后友情提示一下,上述的这些“最佳实践”你要小心,是所谓,贩毒的人从来不吸毒,开赌场的人从来不赌博!所以,你要小心别自己也掉进去了!这就是“欲练神功,必先自宫”的道理。</strong></p><h4 id="相关原理和思维模型"><a href="#相关原理和思维模型" class="headerlink" title="相关原理和思维模型"></a>相关原理和思维模型</h4><p>对于上面的这些技巧还有很多很多,你自己也可以发明或是找到很多。所以,我来讲讲这其中的一些原理。</p><p>一般来说,超过别人一般来说就是两个维度:</p><ol><li><strong>在认知、知识和技能上</strong>。这是一个人赖以立足社会的能力(参看《<a href="https://coolshell.cn/articles/4235.html">程序员的荒谬之言还是至理名言?</a>》和《<a href="https://coolshell.cn/articles/2250.html">21天教你学会C++</a>》)</li><li><strong>在领导力上</strong>。所谓领导力就是你跑在别人前面,你得要有比别人更好的能力更高的标准(参看《<a href="https://coolshell.cn/articles/17583.html">技术人员发展之路</a>》)</li></ol><p>首先,我们要明白,人的技能是从认识开始,然后通过学校、培训或是书本把“零碎的认知”转换成“系统的知识”,而有要把知识转换成技能,就需要训练和实践,这样才能完成从:认识 -> 知识 -> 技能 的转换。这个转换过程是需要耗费很多时间和精力的,而且其中还需要有强大的学习能力和动手能力,这条路径上有很多的“关卡”,每道关卡都会过滤掉一大部分人。比如:对于一些比较枯燥的硬核知识来说,90%的人基本上就倒下来,不是因为他们没有智商,而是他们没有耐心。</p><h5 id="认知"><a href="#认知" class="headerlink" title="认知"></a>认知</h5><p>要在认知上超过别人,就要在下面几个方面上做足功夫:</p><p>1)<strong>信息渠道</strong>。试想如果别人的信息源没有你的好,那么,这些看不见信息源的人,只能接触得到二手信息甚至三手信息,只能获得被别人解读过的信息,这些信息被三传两递后必定会有错误和失真,甚至会被传递信息的中间人hack其中的信息(也就是“中间人攻击”),而这些找不出信息源的人,只能“被人喂养”,于是,他们最终会被困在信息的底层,永世不得翻身。(比如:学习C语言,放着原作者K&R的不用,硬要用错误百出谭浩强的书,能有什么好呢?)</p><p>2)<strong>信息质量</strong>。信息质量主要表现在两个方面,一个是信息中的燥音,另一个是信息中的质量等级,我们都知道,在大数据处理中有一句名言,叫 garbage in garbage out,你天天看的都是垃圾,你的思想和认识也只有垃圾。所以,如果你的信息质量并不好的话,你的认知也不会好,而且你还要花大量的时间来进行有价值信息的挖掘和处理。</p><p>3)<strong>信息密度</strong>。优质的信息,密度一般都很大,因为这种信息会逼着你去干这么几件事,a)搜索并学习其关联的知识,b)沉思和反省,c)亲手去推理、验证和实践……一般来说,经验性的文章会比知识性的文章会更有这样的功效。比如,类似于像 Effiective C++/Java,设计模式,Unix编程艺术,算法导论等等这样的书就是属于这种密度很大的书,而像<a href="https://medium.com/netflix-techblog">Netflix的官方blog</a>和<a href="https://www.allthingsdistributed.com/">AWS CTO的blog</a>等等地方也会经常有一些这样的文章。</p><h5 id="知识"><a href="#知识" class="headerlink" title="知识"></a>知识</h5><p>要在知识上超过别人,你就需要在下面几个方面上做足功夫:</p><p>1)<strong>知识树(图)</strong>。任何知识,只在点上学习不够的,需要在面上学习,这叫系统地学习,这需要我们去总结并归纳知识树或知识图,一个知识面会有多个知识板块组成,一个板块又有各种知识点,一个知识点会导出另外的知识点,各种知识点又会交叉和依赖起来,学习就是要系统地学习整个知识树(图)。而我们都知道,<strong>对于一棵树来说,“根基”是非常重要的,所以,学好基础知识也是非常重要的,对于一个陌生的地方,有一份地图是非常重要的,没有地图的你只会乱窜,只会迷路、练路、走冤枉路!</strong></p><p>2)<strong>知识缘由</strong>。任何知识都是有缘由的,了解一个知识的来龙去脉和前世今生,会让你对这个知识有非常强的掌握,而不再只是靠记忆去学习。靠记忆去学习是一件非常糟糕的事。而对于一些操作性的知识(不需要了解由来的),我把其叫操作知识,就像一些函数库一样,这样的知识只要学会查文档就好了。<strong>能够知其然,知其所以然的人自然会比识知识到表皮的人段位要高很多。</strong></p><p>3)<strong>方法套路</strong>。学习不是为了找到答案,而是找到方法。就像数学一样,你学的是方法,是解题思路,是套路,会用方程式解题的和不会用方程式解题的在解题效率上不可比较,而在微积分面前,其它的解题方法都变成了渣渣。<strong>你可以看到,掌握高级方法的人比别人的优势有多大,学习的目的就是为了掌握更为高级的方法和解题思路</strong>。</p><h5 id="技能"><a href="#技能" class="headerlink" title="技能"></a>技能</h5><p>要在技能上超过别人,你就需要在下面几个方面做足功夫:</p><p>1)<strong>精益求精</strong>。如果你想拥有专业的技能,你要做不仅仅是拼命地重复一遍又一遍的训练,而是在每一次重复训练时你都要找到更好的方法,总结经验,让新的一遍能够更好,更漂亮,更有效率,否则,用相同的方法重复,那你只不过在搬砖罢了。</p><p>2)<strong>让自己犯错</strong>。犯错是有利于成长的,这是因为出错会让人反思,反思更好的方法,反思更完美的方案,总结教训,寻求更好更完美的过程,是技能升级的最好的方式。尤其是当你在出错后,被人鄙视,被人嘲笑后,你会有更大的动力提升自己,这样的动力才是进步的源动力。当然,千万不要同一个错误重复地犯!</p><p>3)<strong>找高手切磋</strong>。下过棋,打个球的人都知道,你要想提升自己的技艺,你必需找高手切磋,在和高手切磋的过程中你会感受到高手的技能和方法,有时候你会情不自禁地哇地一下,我靠,还可以这么玩!</p><h5 id="领导力"><a href="#领导力" class="headerlink" title="领导力"></a>领导力</h5><p>最后一个是领导力,要有领导力或是影响力这个事并不容易,这跟你的野心有多大,好胜心有多强 ,你愿意付出多少很有关系,因为一个人的领导力跟他的标准很有关系,因为有领导力的人的标准比绝大多数人都要高。</p><p>1)<strong>识别自己的特长和天赋</strong>。首先,每个人DNA都可能或多或少都会有一些比大多数人NB的东西(当然,也可能没有),如果你有了,那么在你过去的人生中就一定会表现出来了,就是那种大家遇到这个事会来请教你的寻求你帮助的现象。那种,别人要非常努力,而且毫不费劲的事。一旦你有了这样的特长或天赋,那你就要大力地扩大你的领先优势,千万不要进到那些会限制你优势的地方。你是一条鱼,你就一定要把别人拉到水里来玩,绝对不要去陆地上跟别人拼,不断地在自己的特长和天赋上扩大自己的领先优势,彻底一骑绝尘。</p><p>2)<strong>识别自己的兴趣和事业</strong>。没有天赋也没有问题,还有兴趣点,都说兴趣是最好的老师,当年,Linus就是在学校里对minx着迷了,于是整出个Linux来,这就是兴趣驱动出的东西,一般来说,兴趣驱动的事总是会比那些被动驱动的更好。但是,这里我想说明一下什么叫“真∙兴趣”,真正的兴趣不是那种三天热度的东西,而是那种,你愿意为之付出一辈子的事,是那种无论有多大困难有多难受你都要死磕的事,这才是“真∙兴趣”,这也就是你的“野心”和“好胜心”所在,其实上升到了你的事业。相信我,绝大多数人只有职业而没有事业的。</p><p>3)<strong>建立高级的习惯和方法</strong>。没有天赋没有野心,也还是可以跟别人拼习惯拼方法的,只要你有一些比较好的习惯和方法,那么你一样可以超过大多数人。对此,在习惯上你要做到比较大多数人更自律,更有计划性,更有目标性,比如,每年学习一门新的语言或技术,并可以参与相关的顶级开源项目,每个月训练一个类算法,掌握一种算法,每周阅读一篇英文论文,并把阅读笔记整理出来……自律的是非常可怕的。除此之外,你还需要在方法上超过别人,你需要满世界的找各种高级的方法,其中包括,思考的方法,学习的方法、时间管理的方法、沟通的方法这类软实力的,还有,解决问题的方法(trouble shooting 和 problem solving),设计的方法,工程的方法,代码的方法等等硬实力的,一开始照猫画虎,时间长了就可能会自己发明或推导新的方法。</p><p>4)<strong>勤奋努力执着坚持</strong>。如果上面三件事你都没有也没有能力,那还有最后一件事了,那就是勤奋努力了,就是所谓的“一万小时定律”了(参看《<a href="https://coolshell.cn/articles/2250.html">21天教你学会C++</a>》中的十年学编程一节),我见过很多不聪明的人,悟性也不够(比如我就是一个),别人学一个东西,一个月就好了,而我需要1年甚至更长,但是很多东西都是死的,只要肯花时间就有一天你会搞懂的,耐不住我坚持十年二十年,聪明的人发明个飞机飞过去了,笨一点的人愚公移山也过得去,因为更多的人是懒人,我不用拼过聪明人,我只用拼过那些懒人就好了。</p><p>好了,就这么多,如果哪天你变得消极和不自信,你要来读读我的这篇文章,子曰:温故而知新。</p><p>(全文完)</p><p>转载于:<a href="https://coolshell.cn/articles/19464.html#%E8%AE%A4%E7%9F%A5">酷壳-CoolShell</a> 作者:陈皓</p>]]></content>
<summary type="html"><p><img src="https://atypora.oss-cn-hangzhou.aliyuncs.com/Typora/20220722232509-152.png" style="float:right" alt="图片">当你看到这篇文章的标题,你一定对这篇文章产生了巨大的兴趣,因为你的潜意识在告诉你,这是一本人生的“武林秘籍”,而且还是左耳朵写的,</p></summary>
<category term="转载" scheme="https://shiququ.github.io/categories/%E8%BD%AC%E8%BD%BD/"/>
<category term="尚方宝剑" scheme="https://shiququ.github.io/tags/%E5%B0%9A%E6%96%B9%E5%AE%9D%E5%89%91/"/>
</entry>
<entry>
<title>模拟dijkstra</title>
<link href="https://shiququ.github.io/2022/07/22/2022072201/"/>
<id>https://shiququ.github.io/2022/07/22/2022072201/</id>
<published>2022-07-22T04:09:05.834Z</published>
<updated>2022-07-25T04:01:28.396Z</updated>
<content type="html"><![CDATA[<span id="more"></span><blockquote><p>有向图</p><p>一定不能存在负权边</p><p>原因:因为在第一次发现最短距离就将其加入st集合中,已经确定了其距离</p><p>举例:无法预知未来有一个负边减少其路径长度</p><p>起点为a</p><p>a到b l = 2;</p><p>a到c l =3;</p><p>c到b l =-4;</p><p>所以模拟dijkstra()算法</p><p>得到的结果:首先确定a,然后a到b是最近的,所以再次确定b=2,然后再确定c=3</p><p>而错误之处:实际上是a到c =2,a到b是1 </p></blockquote><blockquote><p>朴素版做法 稠密图 m远小于n^2</p></blockquote><blockquote><p>朴素版做法,适用于稠密图<br>用邻接矩阵存储边,有重边与自环,无负权边<br>所以初始化邻接矩阵为最大值<br>dijkstra():<br>初始化所有点到1号点的距离是INF,dist[1]=0<br>for(循环n次){<br> 找到未在st集合中距离1号点最近的点<br> 加入到st集合中<br> 更新其他所有点到1号点的距离<br>}<br>判断:如果n号点到1号点的距离等于INF,返回-1<br>否则返回dist[n]</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"></span><br><span class="line"><span class="comment">//学习路径</span></span><br><span class="line"><span class="comment">//https://www.acwing.com/video/24/ 42:42</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> <span class="number">510</span>;<span class="comment">//点的范围上限</span></span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span> n, m;<span class="comment">//点与边的个数</span></span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span>[][] g = <span class="keyword">new</span> <span class="title class_">int</span>[N][N];<span class="comment">//权重,边长</span></span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span>[] dist = <span class="keyword">new</span> <span class="title class_">int</span>[N];<span class="comment">//1号点到每一个点的最小路径</span></span><br><span class="line"> <span class="keyword">static</span> <span class="type">boolean</span>[] st = <span class="keyword">new</span> <span class="title class_">boolean</span>[N];<span class="comment">//是否确定最小路径</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException {</span><br><span class="line"> <span class="type">BufferedReader</span> <span class="variable">in</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(System.in));</span><br><span class="line"> String[] s = in.readLine().split(<span class="string">" "</span>);</span><br><span class="line"> n = Integer.parseInt(s[<span class="number">0</span>]);</span><br><span class="line"> m = Integer.parseInt(s[<span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < N; i++) {</span><br><span class="line"> <span class="comment">//0x3f3f3f3f是一个比较大的数,但是不至于经过运算就会超出int数据范围</span></span><br><span class="line"> Arrays.fill(g[i], <span class="number">0x3f3f3f3f</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> (m-- > <span class="number">0</span>) {</span><br><span class="line"> <span class="type">int</span> a, b, c;</span><br><span class="line"> s = in.readLine().split(<span class="string">" "</span>);</span><br><span class="line"> a = Integer.parseInt(s[<span class="number">0</span>]);</span><br><span class="line"> b = Integer.parseInt(s[<span class="number">1</span>]);</span><br><span class="line"> c = Integer.parseInt(s[<span class="number">2</span>]);</span><br><span class="line"> g[a][b] = Math.min(g[a][b], c);</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> dijkstra();</span><br><span class="line"> System.out.println(t);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">dijkstra</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">//初始化1号点到每一个点的距离为正无穷</span></span><br><span class="line"> Arrays.fill(dist, <span class="number">0x3f3f3f3f</span>);</span><br><span class="line"> <span class="comment">//1号点到自己的距离为0</span></span><br><span class="line"> dist[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>;i<n;i++){</span><br><span class="line"> <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span>-<span class="number">1</span>;<span class="comment">//获得不在st中,距离最近的点</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j=<span class="number">1</span>;j<=n;j++){<span class="comment">//遍历点</span></span><br><span class="line"> <span class="comment">//不在st中,距离最近的点</span></span><br><span class="line"> <span class="keyword">if</span> (!st[j]&&(t==-<span class="number">1</span>||dist[t]>dist[j])){</span><br><span class="line"> t =j;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//将t加入到st,确定最短,逐步贪心</span></span><br><span class="line"> st[t] = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">//用t更新其他点</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j=<span class="number">1</span>;j<=n;j++){</span><br><span class="line"> dist[j] = Math.min(dist[j],dist[t] +g[t][j]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果1号点到n号的距离还是0x3f3f3f3f,说明,没有最短路,</span></span><br><span class="line"> <span class="comment">//最短路不会是INF,因为题目数据达不到INF</span></span><br><span class="line"> <span class="keyword">if</span> (dist[n]==<span class="number">0x3f3f3f3f</span>){</span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dist[n];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>堆优化做法 稀疏图 </p></blockquote><blockquote><p>因为是稀疏图,所以使用邻接表<br>初始化表头h为-1<br>读取数据,加入到邻接表中,e[idx] = b,w[idx] = w,ne[idx] = h[a],h[a] = idx++;<br>初始化所有点到1号点的距离,dist[1] = 0;<br>队列存储的是数组,有点编号和点到1号点的距离<br>堆优化版dijkstra<br>将1号点加入优先队列(最小堆)中去,<br> while(判断队列是否为空){<br> 队列弹出最小距离元素(此处比朴素版的时间复杂度低,因为使用的是最小堆,默认弹出最小值)<br> 判断该元素是否已经确认距离 确认跳过<br> 否则,加入到st集合中<br> ver = t[0]点号<br> distance=t[1]距离<br> 使用该点去更新其他点的距离int i = h[ver];i!=-1;i=ne[i];<br> int j =e[i];判断dist[j],是否最优,如果不是最优,更新dist[j],并加入到队列中<br> q.add(new int{j,dist[j]});//点号,距离<br> }<br> 判断dist[n]的距离是否等于INF,返回-1<br> 返回 dist[n]</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"><span class="keyword">import</span> java.util.PriorityQueue;</span><br><span class="line"></span><br><span class="line"><span class="comment">//学习路径</span></span><br><span class="line"><span class="comment">//https://www.acwing.com/video/24/ 42:42</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> <span class="number">1000010</span>;<span class="comment">//点的范围上限</span></span><br><span class="line"> <span class="comment">//static PriorityQueue<int[]> q = new PriorityQueue<>((a, b)->{return a[1] - b[1];});//堆</span></span><br><span class="line"> <span class="keyword">static</span> PriorityQueue<<span class="type">int</span>[]> q = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span><>((a,b)->{<span class="keyword">return</span> a[<span class="number">1</span>]-b[<span class="number">1</span>];});</span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span> n, m,idx=<span class="number">1</span>;<span class="comment">//点与边的个数</span></span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span>[] h = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span>[] e = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span>[] ne = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span>[] w = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span>[] dist = <span class="keyword">new</span> <span class="title class_">int</span>[N];<span class="comment">//1号点到每一个点的最小路径</span></span><br><span class="line"> <span class="keyword">static</span> <span class="type">boolean</span> st[] = <span class="keyword">new</span> <span class="title class_">boolean</span>[N];<span class="comment">//是否确定最小路径</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException {</span><br><span class="line"> <span class="type">BufferedReader</span> <span class="variable">in</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(System.in));</span><br><span class="line"> String[] s = in.readLine().split(<span class="string">" "</span>);</span><br><span class="line"> n = Integer.parseInt(s[<span class="number">0</span>]);</span><br><span class="line"> m = Integer.parseInt(s[<span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">while</span> (m-- > <span class="number">0</span>) {</span><br><span class="line"> <span class="type">int</span> a, b, c;</span><br><span class="line"> s = in.readLine().split(<span class="string">" "</span>);</span><br><span class="line"> a = Integer.parseInt(s[<span class="number">0</span>]);</span><br><span class="line"> b = Integer.parseInt(s[<span class="number">1</span>]);</span><br><span class="line"> c = Integer.parseInt(s[<span class="number">2</span>]);</span><br><span class="line"><span class="comment">//因为是邻接表,去除重边需要对每次加入边进行判断,太耗时间,全部加进去</span></span><br><span class="line"> add(a,b,c);</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> dijkstra();</span><br><span class="line"> System.out.println(t);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> a,<span class="type">int</span> b,<span class="type">int</span> c)</span>{</span><br><span class="line"> e[idx] = b;</span><br><span class="line"> w[idx]=c;</span><br><span class="line"> ne[idx] = h[a];</span><br><span class="line"> h[a] = idx++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">dijkstra</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">//初始化1号点到每一个点的距离为正无穷</span></span><br><span class="line"> Arrays.fill(dist, <span class="number">0x3f3f3f3f</span>);</span><br><span class="line"> <span class="comment">//1号点到自己的距离为0</span></span><br><span class="line"> dist[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> q.add(<span class="keyword">new</span> <span class="title class_">int</span>[]{<span class="number">1</span>,<span class="number">0</span>});</span><br><span class="line"> <span class="keyword">while</span> (q.size()!=<span class="number">0</span>){</span><br><span class="line"> <span class="type">int</span>[] t = q.poll();</span><br><span class="line"> <span class="type">int</span> <span class="variable">ver</span> <span class="operator">=</span> t[<span class="number">0</span>];</span><br><span class="line"> <span class="type">int</span> <span class="variable">distence</span> <span class="operator">=</span> t[<span class="number">1</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//如果已经确定了这个点,至于为什么跳过,又为什么加入到队列中</span></span><br><span class="line"> <span class="comment">//是因为可能c被a加进去队列一次,又被b加进去队列一次,会计算两次,虽然不会影响结果,但是会增加时间复杂度,所以跳过进行优化</span></span><br><span class="line"> <span class="keyword">if</span> (st[ver]) <span class="keyword">continue</span>;</span><br><span class="line"> st[ver] = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=h[ver];i;i=ne[i]){</span><br><span class="line"> <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> e[i];</span><br><span class="line"> <span class="keyword">if</span> (dist[j]>distence+w[i]){</span><br><span class="line"> dist[j] = distence+w[i];</span><br><span class="line"> <span class="comment">//为什么此处不可使用st记录队列中是否具体这项元素而进行代替上面的continue</span></span><br><span class="line"><span class="comment">//因为第一次加进去的可能不是最优解</span></span><br><span class="line"> <span class="comment">//而队列其中的数据却无法随着外面dist[j]的修改而修改</span></span><br><span class="line"> <span class="comment">//比如,在遍历a点周围的点时,将b,c加进去</span></span><br><span class="line"> <span class="comment">//然后下次遍历b时,应该再次将c加入,因为a-c>a-b + b-c</span></span><br><span class="line"> <span class="comment">//但是因为a时将c加进去了,所以此处b时并没有将此c加进去而导致下一次计算的时候会使用dist[j],来自a-c</span></span><br><span class="line"> <span class="comment">//导致后面没有使用到最优解</span></span><br><span class="line"> <span class="comment">//如果使用此方法,可以int distence = t[1];修改为</span></span><br><span class="line"> <span class="comment">//int distence = dist[ver];</span></span><br><span class="line"> q.add(<span class="keyword">new</span> <span class="title class_">int</span>[]{j,dist[j]});</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果1号点到n号的距离还是0x3f3f3f3f,说明,没有最短路,</span></span><br><span class="line"> <span class="comment">//应该少了最短路恰好是0x3f3f3f3f</span></span><br><span class="line"> <span class="keyword">if</span> (dist[n]==<span class="number">0x3f3f3f3f</span>){</span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dist[n];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><span id="more"></span>
<blockquote>
<p>有向图</p>
<p>一定不能存在负权边</p>
<p>原因:因为在第一次发现最短距离就将其加入st集合中,已经确定了其距离</p>
<p>举例:无法预知未来有一个负边减少其路径长度</p>
<p</summary>
<category term="算法" scheme="https://shiququ.github.io/categories/%E7%AE%97%E6%B3%95/"/>
<category term="算法" scheme="https://shiququ.github.io/tags/%E7%AE%97%E6%B3%95/"/>
</entry>
</feed>