From 5ba7024532b93dbcf742b387d4e099859d185a5e Mon Sep 17 00:00:00 2001 From: Ching Date: Mon, 7 Feb 2022 23:38:40 +0800 Subject: [PATCH] feat(content; layouts; static): migrate hexo blog. add new theme fuji. migrate hexo blog. add new theme fuji. Signed-off-by: Ching --- .gitignore | 23 +++ .gitmodules | 3 + archetypes/default.md | 6 + config.toml | 155 ++++++++++++++++++ content/archives/_index.md | 5 + .../posts/2018-05-31-bash-function-and-awk.md | 53 ++++++ content/posts/2019-11-14-AWS-KMS.md | 15 ++ content/posts/2020-03-17-leetcode-121.md | 61 +++++++ content/posts/2020-03-18-leetcode-206.md | 89 ++++++++++ content/posts/2020-03-23-leetcode-169.md | 63 +++++++ content/posts/2020-03-23-leetcode-225.md | 70 ++++++++ content/posts/2020-03-25-leetcode-409.md | 50 ++++++ content/posts/2020-03-25-leetcode-543.md | 113 +++++++++++++ content/posts/2020-03-25-leetcode-836.md | 34 ++++ content/posts/2020-03-26-leetcode-876.md | 68 ++++++++ content/posts/2020-03-29-leetcode-1013.md | 46 ++++++ content/posts/2020-03-29-leetcode-914.md | 52 ++++++ content/posts/2020-03-30-leetcode-1071.md | 60 +++++++ content/posts/2020-03-30-leetcode-999.md | 72 ++++++++ content/posts/2020-04-01-leetcode-1103.md | 56 +++++++ content/posts/2020-04-01-leetcode-1160.md | 39 +++++ ...020-04-01-leetcode-compress-string-lcci.md | 42 +++++ ...e-wei-sde-lian-xu-zheng-shu-xu-lie-lcof.md | 43 +++++ .../2020-04-09-leetcode-the-masseuse-lcci.md | 48 ++++++ .../2020-04-14-leetcode-add-two-numbers-ii.md | 116 +++++++++++++ .../2020-04-14-leetcode-design-twitter.md | 84 ++++++++++ .../posts/2020-04-16-leetcode-01-matrix.md | 48 ++++++ .../2020-04-16-leetcode-merge-intervals.md | 51 ++++++ ...0-04-16-leetcode-string-to-integer-atoi.md | 56 +++++++ .../2020-04-21-leetcode-number-of-islands.md | 50 ++++++ content/posts/Django-Manager-Method.md | 55 +++++++ content/posts/Flask-Day-1.md | 122 ++++++++++++++ content/posts/Flask-Day-2.md | 82 +++++++++ content/posts/Postgresql Partitioning.md | 43 +++++ content/posts/TastyPie-Note-1.md | 47 ++++++ content/posts/Tastypie.md | 41 +++++ content/posts/first-post.md | 9 + content/search/_index.md | 5 + deploy.sh | 1 + layouts/partials/favicon.html | 7 + static/android-chrome-144x144.png | Bin 0 -> 6947 bytes static/apple-touch-icon.png | Bin 0 -> 5554 bytes static/browserconfig.xml | 9 + static/favicon-16x16.png | Bin 0 -> 1112 bytes static/favicon-32x32.png | Bin 0 -> 1590 bytes static/favicon.ico | Bin 0 -> 15086 bytes static/mstile-150x150.png | Bin 0 -> 6507 bytes static/safari-pinned-tab.svg | 16 ++ static/site.webmanifest | 14 ++ themes/fuji | 1 + 50 files changed, 2123 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 archetypes/default.md create mode 100644 config.toml create mode 100644 content/archives/_index.md create mode 100644 content/posts/2018-05-31-bash-function-and-awk.md create mode 100644 content/posts/2019-11-14-AWS-KMS.md create mode 100644 content/posts/2020-03-17-leetcode-121.md create mode 100644 content/posts/2020-03-18-leetcode-206.md create mode 100644 content/posts/2020-03-23-leetcode-169.md create mode 100644 content/posts/2020-03-23-leetcode-225.md create mode 100644 content/posts/2020-03-25-leetcode-409.md create mode 100644 content/posts/2020-03-25-leetcode-543.md create mode 100644 content/posts/2020-03-25-leetcode-836.md create mode 100644 content/posts/2020-03-26-leetcode-876.md create mode 100644 content/posts/2020-03-29-leetcode-1013.md create mode 100644 content/posts/2020-03-29-leetcode-914.md create mode 100644 content/posts/2020-03-30-leetcode-1071.md create mode 100644 content/posts/2020-03-30-leetcode-999.md create mode 100644 content/posts/2020-04-01-leetcode-1103.md create mode 100644 content/posts/2020-04-01-leetcode-1160.md create mode 100644 content/posts/2020-04-01-leetcode-compress-string-lcci.md create mode 100644 content/posts/2020-04-09-leetcode-he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof.md create mode 100644 content/posts/2020-04-09-leetcode-the-masseuse-lcci.md create mode 100644 content/posts/2020-04-14-leetcode-add-two-numbers-ii.md create mode 100644 content/posts/2020-04-14-leetcode-design-twitter.md create mode 100644 content/posts/2020-04-16-leetcode-01-matrix.md create mode 100644 content/posts/2020-04-16-leetcode-merge-intervals.md create mode 100644 content/posts/2020-04-16-leetcode-string-to-integer-atoi.md create mode 100644 content/posts/2020-04-21-leetcode-number-of-islands.md create mode 100644 content/posts/Django-Manager-Method.md create mode 100644 content/posts/Flask-Day-1.md create mode 100644 content/posts/Flask-Day-2.md create mode 100644 content/posts/Postgresql Partitioning.md create mode 100644 content/posts/TastyPie-Note-1.md create mode 100644 content/posts/Tastypie.md create mode 100644 content/posts/first-post.md create mode 100644 content/search/_index.md create mode 100755 deploy.sh create mode 100644 layouts/partials/favicon.html create mode 100644 static/android-chrome-144x144.png create mode 100644 static/apple-touch-icon.png create mode 100644 static/browserconfig.xml create mode 100644 static/favicon-16x16.png create mode 100644 static/favicon-32x32.png create mode 100644 static/favicon.ico create mode 100644 static/mstile-150x150.png create mode 100644 static/safari-pinned-tab.svg create mode 100644 static/site.webmanifest create mode 160000 themes/fuji diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9813417 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Hugo default output directory +/public +/resources/_gen/ +/assets/jsconfig.json +hugo_stats.json + +## OS Files +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ + +# OSX +.DS_Store + +# Executable may be added to repository +hugo.exe +hugo.darwin +hugo.linux + +# Temporary lock file while building +/.hugo_build.lock diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f8abe65 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "themes/fuji"] + path = themes/fuji + url = https://github.com/amzrk2/hugo-theme-fuji.git diff --git a/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..00e77bd --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,6 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +date: {{ .Date }} +draft: true +--- + diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..e597c79 --- /dev/null +++ b/config.toml @@ -0,0 +1,155 @@ +baseURL = 'https://blog.tunpok.com/' +title = 'MarkDown' +theme = 'fuji' + +hasCJKLanguage = true +enableEmoji = true +enableRobotsTXT = true +disableKinds = [] +ignoreErrors = ["error-disable-taxonomy"] + +## Change this two to switch between different language +languageCode = "zh-cn" # For RSS, view https://www.rssboard.org/rss-language-codes +defaultContentLanguage = "zh-hans" # For HTML page, now support: en, zh-hans, zh-hant, ja, nl, pl, it + +summaryLength = 100 # Custom summary length, add in post file to custom split point +paginate = 10 + +# googleAnalytics = "UA-000000000-0" # Set your Google Analytics UA here + +[outputFormats] + [outputFormats.SearchIndex] + isPlainText = true + notAlternative = true + mediaType = "application/json" + path = "/search/" + +[outputs] + home = ["HTML", "RSS", "SearchIndex"] + +[permalinks] + post = "/:section/:filename/" # Custom post links, e.g. "/:year/:month/:title/" + +[params] + author = "Ching" # You can also set author in post front matter individually + subTitle = "「靡不有初,鲜克有终」" + defaultTheme = "auto" # default theme when first visit (auto|dark|light) + + # Source URL of the website, will appear in the footer + #sourceURL = "https://github.com/dsrkafuu/hugo-theme-fuji" + + # Use CloudFlare Workers to accelerate the Google Analytics + # If you are using this please comment the googleAnalytics above + # Check https://github.com/SukkaW/cloudflare-workers-async-google-analytics for more details + # googleAnalyticsTid = "UA-000000000-0" + # googleAnalyticsRoute = "https://*.*.workers.dev/" + + # Google AdSense + # The AdSense code will be inserted between the head tags of your site. + # googleAdsense = "0000000000000000" + + # Word counter and read time indicator in post metadata + showWordCounter = true + showReadTime = false + + # License in the footer + showLicenseInFooter = false + + # License at the end of each post + showLicense = false + showToc = true + + # Copyright + copyrightStartYear = "" + + # Open Graph & Twitter Card variables + # You can also set description and images in post front matter individually + description = "A minimal Hugo theme with nice theme color." + og = "/img/og.png" # This will use the image called og.png in static/img folder + + # Posts shown in homepage + mainSections = ["posts"] + + # Bangumi image chart id + # bgmImageChart = "000000" + + # License + license = "CC BY-NC-SA 4.0" + licenseLink = "http://creativecommons.org/licenses/by-nc-sa/4.0/" + + # Comments + # utterances, see: https://utteranc.es/ + # utterancesRepo = "*/*" + # utterancesIssueTerm = "pathname" + + # Disqus, see: https://disqus.com/admin/install/platforms/universalcode/ + # disqusShortname = "*********" + # Also use DisqusJS for accessing from Mainland China, see: https://github.com/SukkaW/DisqusJS + # If you want to set multiple api key, see theme's README for more details + # disqusJSApi = "https://*********/" + # disqusJSApikey = "**********" + + # custom lazyload placeholder + # 16:9 + lazyPlaceholder = "/assets/lazyload/dsrca_loading_480x270.svg" + # 32:9 + lazyPlaceholderRow = "/assets/lazyload/dsrca_loading_960x270.svg" + # 8:9 + lazyPlaceholderCol = "/assets/lazyload/dsrca_loading_480x540.svg" + + # Let images display in full brightness under dark mode + # disableDarkImage = true + +[markup] + [markup.goldmark] + [markup.goldmark.renderer] + unsafe = true # Enable user to embed HTML snippets in Markdown content + [markup.highlight] + codeFences = false # Disable Hugo's code highlighter + + [markup.tableOfContents] + startLevel = 2 + endLevel = 3 + +[taxonomies] + tag = "tags" + category = "categories" + +[menu] + [[menu.nav]] + name = "Home" + url = "/" + weight = 1 + [[menu.nav]] + name = "Archives" + url = "/archives/" + weight = 2 +# [[menu.nav]] +# name = "Categories" +# url = "/categories/" +# weight = 3 + [[menu.nav]] + name = "Search" + url = "/search/" + weight = 4 + [[menu.nav]] + name = "RSS" + url = "/index.xml" + weight = 5 + + [[menu.link]] + name = "hea" + url = "https://ghost.tunpok.com/" + weight = 1 +# [[menu.link]] +# name = "GitHub" +# url = "https://github.com/dsrkafuu" +# weight = 1 +# [[menu.link]] +# name = "Twitter" +# url = "https://twitter.com/dsrkafuu" +# weight = 2 +# [[menu.link]] +# name = "bilibili" +# url = "https://space.bilibili.com/19767474" +# weight = 3 diff --git a/content/archives/_index.md b/content/archives/_index.md new file mode 100644 index 0000000..cca6389 --- /dev/null +++ b/content/archives/_index.md @@ -0,0 +1,5 @@ +--- +title: "Archives" +date: 2022-02-07T22:50:44+08:00 +--- + diff --git a/content/posts/2018-05-31-bash-function-and-awk.md b/content/posts/2018-05-31-bash-function-and-awk.md new file mode 100644 index 0000000..f86d95a --- /dev/null +++ b/content/posts/2018-05-31-bash-function-and-awk.md @@ -0,0 +1,53 @@ +--- +title: bash function and awk +date: 2018-05-31 00:00:48 +tags: + - bash +--- + + + +I'll come across many hg branch-switching or log searching tasks during my work. Using bash functions and awk greatly reduce the time I spend on dealing with these tasks. So let's see what these functions can do to help me improve my productivity. + + + +#### bash functions + +Bash functions are scripts like `alias`, but can do much a lot than aliasThe first kind of usages of function is to run several scripts continuously, the same as `&&` I guess: + +```bash +function run { + source ~/Develop/django/bin/activate + ./manage.py runserver +} +``` + +I activate django virtural enviroment first and then run django serve. + +Functions, like in any other languages, can take parameters and returns a result. So I can use this ability to do a liitle more complex work: + +```bash +function ba { hgba | grep "$1" } +# hgba is an alias for hg branches +``` + +I can just type `ba release` then get current release branch info. + +----- + +Append: + +I made some updates to the `ba` function and make it auto copying the branch result to my clipboard: + +```bash + +function ba { var="$(hgba | grep $1)" && echo $var | awk -F ':' END{print} | awk -F ':' '{print $NF}' | tr -d '\n' | pbcopy && echo $var } +``` + + + +#### awk + +`awk` is a command I just know recently. I need to process a bunch of logs and analyze them. What I used to do is download the logs, grep things I want into a txt file and then process the txt file with python. + + diff --git a/content/posts/2019-11-14-AWS-KMS.md b/content/posts/2019-11-14-AWS-KMS.md new file mode 100644 index 0000000..15d86cd --- /dev/null +++ b/content/posts/2019-11-14-AWS-KMS.md @@ -0,0 +1,15 @@ +--- +title: AWS KMS +date: 2019-11-14 15:19:05 +tags: + - aws +--- + +We used to keep private credentials on production servers without any protection or encryption. Well, luckily we don't have any leak but this practice is not recommended for both security and easy of use reasons. + +Since AWS finally provides [KMS(Key Management Service)][1] in our local region, we try to encrypt every private credentials by KMS and store them on S3. + +*TBD* + + +[1]: https://www.amazonaws.cn/kms/ diff --git a/content/posts/2020-03-17-leetcode-121.md b/content/posts/2020-03-17-leetcode-121.md new file mode 100644 index 0000000..c1e7876 --- /dev/null +++ b/content/posts/2020-03-17-leetcode-121.md @@ -0,0 +1,61 @@ +--- +title: leetcode-121 +date: 2020-03-17 18:06:45 +tags: + - leetcode +categories: + - leetcode +--- + +### 121. 买卖股票的最佳时机 + +[题目](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/) + + + +原始答案: + +```python +class Solution: + def maxProfit(self, prices) -> int: + profit = 0 + if not prices: + return profit + min_buyin = prices[0] + max_sellout = prices[0] + l = len(prices) + i = 0 + for buyin in prices: + if i == l: + return profit + if min_buyin <= buyin: + max_sellout = max(prices[i:]) + p = max_sellout - min_buyin + if p > profit: + profit = p + if buyin < min_buyin: + min_buyin = buyin + i += 1 + return profit +#1880 ms 14.4 MB +``` + +主要思路是找到波谷,如果当前价格比前一天要低,则还是在去往波谷的路上;当价格比前一天高或相同时,则到达了一个波谷,计算波谷和之后的波峰的差,就是这一段的利润。将从头至尾过一次,就能找到所有波谷和其后波峰的差,返回最大的即可。但是这个明显地在重复max,时间复杂度是O(n^2),看起来就很傻逼。 + +仔细想想,其实并不需要直接找出波谷后的波峰,只要在for loop时保持波谷为最低的那个,就能算出每一个后续与波谷的差,找最大差即可。改了下代码变成这样 + +```python +class Solution: + def maxProfit(self, prices) -> int: + profit = 0 + if not prices: + return profit + min_buyin = prices[0] + for i in range(1, len(prices)): + min_buyin = min(min_buyin, prices[i-1]) + profit = max(prices[i] - min_buyin, profit) + return profit + #44 ms 14.4 MB +``` + + diff --git a/content/posts/2020-03-18-leetcode-206.md b/content/posts/2020-03-18-leetcode-206.md new file mode 100644 index 0000000..a3793bd --- /dev/null +++ b/content/posts/2020-03-18-leetcode-206.md @@ -0,0 +1,89 @@ +--- +title: leetcode-206 +date: 2020-03-18 23:33:03 +tagstags: + - leetcode: +categories: leetcode +--- + +### 206. 反转链表 + +[题目](https://leetcode-cn.com/problems/reverse-linked-list/) + + + +最简单的思路是遍历链表一个列表去做存储,通过倒序读取列表的同时改写链表。 + +```python +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + +class Solution: + def reverseList(self, head): + if not head or not head.next: + return head + nl = [] + while head.next: + nl.append(head) + head = head.next + nl.append(head) + l = len(nl) + for x in range(l): + if x == 0: + nl[x].next = None + continue + nl[x].next = nl[x-1] + if x == (l - 1): + return nl[x] +``` + +仔细想想自己又傻逼了,何必要遍历两次呢,在第一遍遍历的同时就能操作了: + +```python +class Solution: + def reverseList(self, head): + if not head or not head.next: + return head + prev_node = None + next_node = head.next + while head: + next_node = head.next + head.next = prev_node + prev_node = head + head = next_node + return prev_node +``` + +然后是递归的做法,主要思路是一直进到最深一层--也就是链表的最后一个--的时候开始返回,同时修改那一层的两个 node。一开始踩了一个坑是返回了每一个node,结果最后回到第一层的时候得到的是链表的末端,其实只需要修改链表,并不需要返回 node,所以一开始到达链表末端的时候直接返回那一个node就可以了。 + +```python +class Solution: + def reverseList(self, head): + if not head: + return head + if head.next: + ss = Solution() + last = ss.reverseList(head.next) + head.next.next = head + head.next = None + return last + return head +``` + +一开始是用list来打草稿,不过想明白递归之后就大同小异了: + +```python +def a(l:list)->list: + k=[l[0]] + + if l[1:]: + b=a(l[1:]) + b.extend(k) + else: + return [l[0]] + return b +``` + + diff --git a/content/posts/2020-03-23-leetcode-169.md b/content/posts/2020-03-23-leetcode-169.md new file mode 100644 index 0000000..130be8a --- /dev/null +++ b/content/posts/2020-03-23-leetcode-169.md @@ -0,0 +1,63 @@ +--- +title: leetcode-169 +date: 2020-03-23 23:12:57 +tagstags: + - leetcode: +categories: leetcode +--- + + +### 169. 多数元素 + +[题目](https://leetcode-cn.com/problems/majority-element/) + + + + + + + +一开始的思路是遍历一遍整个列表,用一个字典去记录每个元素出现的次数,当次数大于 $\cfrac{n}{2}$ 时就可以得出结果。 + +```python +class Solution: + def majorityElement(self, nums) -> int: + d = {} + l = len(nums) + for n in nums: + if not n in d: + d[n] = 0 + d[n] = d[n] + 1 + if d[n] > l/2: + return n +# 64 ms 15.1 MB +``` + +Python 也有专门计数的库,写起来更简单一点: + +```python +class Solution: + def majorityElement(self, nums): + counts = collections.Counter(nums) + return max(counts.keys(), key=counts.get) +# 44 ms 15.1 MB +``` + +由于要找的数出现次数大于 $\cfrac{n}{2}$,脑子里掠过一下[蒙特卡罗算法](https://zh.wikipedia.org/wiki/%E8%92%99%E5%9C%B0%E5%8D%A1%E7%BE%85%E6%96%B9%E6%B3%95),后来在官方解答中也看到类似的思路了: + +```python +class Solution: + def majorityElement(self, nums): + majority_count = len(nums)//2 + while True: + candidate = random.choice(nums) + if sum(1 for elem in nums if elem == candidate) > majority_count: + return candidate + +#作者:LeetCode-Solution +#链接:https://leetcode-cn.com/problems/majority-element/solution/duo-shu-yuan-su-by-leetcode-solution/ +#来源:力扣(LeetCode) +#著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 +``` + + diff --git a/content/posts/2020-03-23-leetcode-225.md b/content/posts/2020-03-23-leetcode-225.md new file mode 100644 index 0000000..06394a5 --- /dev/null +++ b/content/posts/2020-03-23-leetcode-225.md @@ -0,0 +1,70 @@ +--- +title: leetcode-225 +date: 2020-03-23 23:35:05 +tags: + - leetcode +categories: leetcode +--- + +### 225. 用队列实现栈 + +[题目](https://leetcode-cn.com/problems/implement-stack-using-queues/) + + + + + + + +注意栈是 FILO(First In Last Out),Python 的 list 是 FIFO(First In First Out)。 + +```python +class MyStack: + + def __init__(self): + """ + Initialize your data structure here. + """ + self.data = [] + + + def push(self, x: int) -> None: + """ + Push element x onto stack. + """ + self.data.append(x) + return + + + def pop(self) -> int: + """ + Removes the element on top of the stack and returns that element. + """ + return self.data.pop(-1) + + + def top(self) -> int: + """ + Get the top element. + """ + return self.data[-1] + + + def empty(self) -> bool: + """ + Returns whether the stack is empty. + """ + return not bool(self.data) + + + +# Your MyStack object will be instantiated and called as such: +# obj = MyStack() +# obj.push(x) +# param_2 = obj.pop() +# param_3 = obj.top() +# param_4 = obj.empty() + +#24 ms 13.5 MB +``` + diff --git a/content/posts/2020-03-25-leetcode-409.md b/content/posts/2020-03-25-leetcode-409.md new file mode 100644 index 0000000..7cbedbf --- /dev/null +++ b/content/posts/2020-03-25-leetcode-409.md @@ -0,0 +1,50 @@ +--- +title: leetcode-409 +date: 2020-03-25 21:55:38 +tags: + - leetcode +categories: leetcode +--- + +### 409. 最长回文串 + +[题目](https://leetcode-cn.com/problems/longest-palindrome/) + + + + + + + + + +一开始理解错题目了,以为是寻找字符串中的最长回文串,结果是构造。但是原理基本一样,由于回文中心对称,所以是由多个偶数个相同字母和至多一个奇数个相同字母组成。 + +这样只要数给出的字符串中有几个偶数个相同字母和几个奇数个相同字母就可以了。奇数个相同字母可以减少一个当偶数个用,最后再加回去一个。 + + + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + d = {} + for l in s: + if not l in d: + d[l] = 0 + d[l] += 1 + + i = 0 + odd = False + for k, v in d.items(): + if v % 2: + i += (v-1) + odd = True + else: + i += v + if odd: + i += 1 + return i + +#40 ms 13.6 MB +``` + diff --git a/content/posts/2020-03-25-leetcode-543.md b/content/posts/2020-03-25-leetcode-543.md new file mode 100644 index 0000000..ec3d9ab --- /dev/null +++ b/content/posts/2020-03-25-leetcode-543.md @@ -0,0 +1,113 @@ +--- +title: leetcode-543 +date: 2020-03-25 19:13:52 +tags: + - leetcode +categories: leetcode +--- + +### 543. 二叉树的直径 + + +[题目](https://leetcode-cn.com/problems/diameter-of-binary-tree/) + + + + + + +这题做出来了但是没有通过运行时间的测试,主要还是没想明白二叉树的直径到底是什么东西,用了个蠢办法。 + +```python +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution: + def diameterOfBinaryTree(self, root: TreeNode) -> int: + if not root: + return 0 + last_node = -1 + routes = [] + start = root + node_stack = [root] + while (start.left or start.right or node_stack): + if start != node_stack[-1]: + node_stack.append(start) + + if last_node == start.right: + node_stack = node_stack[:-1] + if not node_stack: + break + last_node = start + start = node_stack[-1] + continue + + if start.left and last_node != start.left: + start = start.left + last_node = start + continue + + if start.right: + start = start.right + last_node = start + continue + + routes.append(node_stack) + node_stack = node_stack[:-1] + if not node_stack: + break + last_node = start + start = node_stack[-1] + + max_l = 0 + for route in routes: + for route_ in routes: + intersection = 0 + if route != route_: + intersection = len(set(route).intersection(set(route_))) + if intersection: + intersection -= 1 + max_l = max(max_l, len(set(route)| set(route_)) - intersection) + return max_l - 1 +``` + + + +L43 之前做的是以深度优先的方式遍历一遍树,得出每个点的路径。后面的是将所有路径组合在一起得出任意两个点间的路径,算出最大长度。 + + + +其实以某个点为根节点的树的直径,就是某个节点的**左子树的深度和右子树的深度的和**,用递归来处理这个会比较容易理解 + + + +```python +class Solution(object): + def diameterOfBinaryTree(self, root): + self.ans = 1 + def depth(node): + # 访问到空节点了,返回0 + if not node: return 0 + # 左儿子为根的子树的深度 + L = depth(node.left) + # 右儿子为根的子树的深度 + R = depth(node.right) + # 计算d_node即L+R+1 并更新ans + self.ans = max(self.ans, L+R+1) + # 返回该节点为根的子树的深度 + return max(L, R) + 1 + + depth(root) + return self.ans - 1 + +作者:LeetCode-Solution +链接:https://leetcode-cn.com/problems/diameter-of-binary-tree/solution/er-cha-shu-de-zhi-jing-by-leetcode-solution/ +来源:力扣(LeetCode) +著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 +``` + + diff --git a/content/posts/2020-03-25-leetcode-836.md b/content/posts/2020-03-25-leetcode-836.md new file mode 100644 index 0000000..be4cc56 --- /dev/null +++ b/content/posts/2020-03-25-leetcode-836.md @@ -0,0 +1,34 @@ +--- +title: leetcode-836 +date: 2020-03-25 22:41:25 +tags: + - leetcode +categories: leetcode +--- + +### 836. 矩形重叠 + +[题目](https://leetcode-cn.com/problems/rectangle-overlap/) + + + + + + + +看两个矩形有没有重叠,就看两个矩形在坐标轴上的投影有没有重叠。 + + + +```python +class Solution: + def isRectangleOverlap(self, rec1, rec2) -> bool: + return ((min(rec1[2], rec2[2]) > max(rec1[0], rec2[0])) + and (min(rec1[3], rec2[3]) > max(rec1[1], rec2[1]))) + +s = Solution() +s.isRectangleOverlap(rec1 = [0,0,2,2], rec2 = [1,1,3,3]) + +#40 ms 13.7 MB +``` + diff --git a/content/posts/2020-03-26-leetcode-876.md b/content/posts/2020-03-26-leetcode-876.md new file mode 100644 index 0000000..597a437 --- /dev/null +++ b/content/posts/2020-03-26-leetcode-876.md @@ -0,0 +1,68 @@ +--- +title: leetcode-876 +date: 2020-03-26 21:18:41 +tags: + - leetcode +categories: leetcode +--- + +### 876. 链表的中间结点 + + + +[题目](https://leetcode-cn.com/problems/middle-of-the-linked-list/) + + + + + + + +思路是遍历一遍得到整个链表,讲每个 node 放进一个 list,就可以通过下标得到中间的。 + + + +```python +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + +class Solution: + def middleNode(self, head: ListNode) -> ListNode: + l = [] + n = head + while n.next: + l.append(n) + n = n.next + l.append(n) + return l[len(l)//2] + + #44 ms 13.7 MB +``` + + + +看官方解答,还有一个骚操作,通过两个速度不一样的指针,一个一次走一步,一个两次走一步,快的走到底时,慢的就在中间了。 + + + +```python +class Solution: + def middleNode(self, head: ListNode) -> ListNode: + slow = fast = head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + return slow + +作者:LeetCode-Solution +链接:https://leetcode-cn.com/problems/middle-of-the-linked-list/solution/lian-biao-de-zhong-jian-jie-dian-by-leetcode-solut/ +来源:力扣(LeetCode) +著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 +``` + + + + diff --git a/content/posts/2020-03-29-leetcode-1013.md b/content/posts/2020-03-29-leetcode-1013.md new file mode 100644 index 0000000..9025d1a --- /dev/null +++ b/content/posts/2020-03-29-leetcode-1013.md @@ -0,0 +1,46 @@ +--- +title: leetcode-1013 +date: 2020-03-29 21:09:22 +tags: + - leetcode +categories: leetcode +--- + +### 1013. 将数组分成和相等的三个部分 + +[题目](https://leetcode-cn.com/problems/partition-array-into-three-parts-with-equal-sum/) + + + + + + + +因为是整数数组,如果能均分成三份,则数组和肯定是3的倍数。然后遍历数组逐端求和使得和为 sum(A)/3。 + + + +```python +class Solution: + def canThreePartsEqualSum(self, A) -> bool: + if not A: + return False + sa = sum(A) + if sa % 3: + return False + s = sa // 3 + s1 = 0 + s2 = 0 + + + for i in range(len(A)): + s1 += A[i] + if s1 == s and (i+1) < len(A): + for j in range(len(A[i+1:])): + s2 += A[i+1+j] + if s2 == s and j+1 < len(A[i+1:]) and sum(A[i+j+2:])== s: + return True + return False +#60 ms 18.7 MB +``` + diff --git a/content/posts/2020-03-29-leetcode-914.md b/content/posts/2020-03-29-leetcode-914.md new file mode 100644 index 0000000..434f981 --- /dev/null +++ b/content/posts/2020-03-29-leetcode-914.md @@ -0,0 +1,52 @@ +--- +title: leetcode-914 +date: 2020-03-29 22:41:09 +tags: + - leetcode +categories: leetcode +--- + +### 914. 卡牌分组 + +[题目](https://leetcode-cn.com/problems/x-of-a-kind-in-a-deck-of-cards/) + + + + + + + +将大牌堆分成多个牌数量相等的小牌堆,就是求每张牌数量的公约数。先遍历一遍得到每张牌的数量,然后找出比2大的公约数即可。 + + + +```python +class Solution: + def hasGroupsSizeX(self, deck) -> bool: + dc = {} + max_d = 0 + for d in deck: + if d not in dc: + dc[d] = 0 + dc[d] += 1 + if max_d < d: + max_d = d + if max_d < dc[d]: + max_d = dc[d] + has_x = True + if max_d == 1: + max_d = 2 + + for i in range(2, max_d + 1): + has_x = True + for k,v in dc.items(): + if v % i: + has_x = False + break + if has_x and i >= 2: + return True + return False + +#56 ms 13.8 MB +``` + diff --git a/content/posts/2020-03-30-leetcode-1071.md b/content/posts/2020-03-30-leetcode-1071.md new file mode 100644 index 0000000..dad6aff --- /dev/null +++ b/content/posts/2020-03-30-leetcode-1071.md @@ -0,0 +1,60 @@ +--- +title: leetcode-1071 +date: 2020-03-30 22:03:01 +tags: + - leetcode +categories: leetcode +--- + +### 1071. 字符串的最大公因子 + +[题目](https://leetcode-cn.com/problems/greatest-common-divisor-of-strings/) + + + + + + + +如果存在这样字符串,那它最大的长度就是这两个字符串长度的最大公约数。 + + + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + if str1[0] != str2[0]: + return '' + + a = len(str1) + b = len(str2) + print(a, b) + if a < b: + str1, str2 = str2, str1 + a = len(str1) + b = len(str2) + + if not a%b: + for x in range(0, a//b): + if str1[x*b:(x+1)*b] != str2: + return '' + return str2 + else: + for x in range(b, 0, -1): + print(x) + if x==b or b%x or a%x: + continue + for y in range(0, b//x): + if str2[y*x:(y+1)*x] != str2[b-x:b]: + return '' + for y in range(0, a//x): + if str1[y*x:(y+1)*x] != str2[0:x]: + return '' + return str2[0:x] +# 44 ms 13.9 MB +``` + + + +官方解答中还给了一种巧妙的解法,如果 str1 + str2 == str2 + str1 的话,[可以证明](https://leetcode-cn.com/problems/greatest-common-divisor-of-strings/solution/zi-fu-chuan-de-zui-da-gong-yin-zi-by-leetcode-solu/)必定存在这样一个字符串,其长度为两个字符串长度的最大公约数。 + diff --git a/content/posts/2020-03-30-leetcode-999.md b/content/posts/2020-03-30-leetcode-999.md new file mode 100644 index 0000000..2a0c715 --- /dev/null +++ b/content/posts/2020-03-30-leetcode-999.md @@ -0,0 +1,72 @@ +--- +title: leetcode-999 +date: 2020-03-30 21:03:25 +tags: + - leetcode +categories: leetcode +--- + + +### 999. 可以被一步捕获的棋子数 + +[题目](https://leetcode-cn.com/problems/available-captures-for-rook/) + + + + + + + +遍历一遍找到车的坐标,然后按上下左右四个方向循环一下看碰到的第一个棋子是什么。 + + + +```python +class Solution: + def numRookCaptures(self, board) -> int: + i = j = 0 + for row in board: + if 'R' in row: + break + i += 1 + j = row.index('R') + count = 0 + # right + for x in range(j + 1, 8): + if row[x] == 'p': + count += 1 + break + if row[x] == 'B': + break + # left + for x in range(j, 0, -1): + if row[x] == 'p': + count += 1 + break + if row[x] == 'B': + break + # up + for x in range(i, 0, -1): + if board[x][j] == 'p': + count += 1 + break + if board[x][j] == 'B': + break + # down + for x in range(i+1, 8): + if board[x][j] == 'p': + count += 1 + break + if board[x][j] == 'B': + break + + return count + +#36 ms 13.6 MB + +``` + + + +问题不难,官方解答中给了一个方向数组的概念,上下左右是 (0, 1) (0, -1) (-1, 0) (1, 0),有点像向量的意思。走的路线等于方向数组乘以步数。 + diff --git a/content/posts/2020-04-01-leetcode-1103.md b/content/posts/2020-04-01-leetcode-1103.md new file mode 100644 index 0000000..80b746c --- /dev/null +++ b/content/posts/2020-04-01-leetcode-1103.md @@ -0,0 +1,56 @@ +--- +title: leetcode-1103 +date: 2020-04-01 11:22:20 +tags: + - leetcode +categories: leetcode +mathjax: true +--- + +### 1103. 分糖果 II + +[题目](https://leetcode-cn.com/problems/distribute-candies-to-people/) + + + + + + + +小学奥数题。主要思路就是等差数列求和 $\cfrac{(首项 + 末项)×项数}{2}$ 。可以用公式把每一个位置获得的总糖果数表示出来。我的方法稍微蠢了点,算了每一轮的总糖果数,其实可以直接求总共发了多少次糖果,除以每轮的人数就可以得出发了多少轮。 + + + +```python +class Solution: + def distributeCandies(self, candies: int, num_people: int): + total = 0 + i = 0 + # import ipdb; ipdb.set_trace() + while total <= candies: + t = (num_people*i)*num_people + int((1+num_people)*num_people/2) + if total + t <= candies: + total += t + i += 1 + else: + break + remaining = candies - total + print(total, remaining, i) + l = [] + for n in range(1, num_people+1): + if not total: + current_candy = n + else: + current_candy = n+i*num_people + + n_count = int((0+(i-1))*(i)/2) + print(current_candy, n_count) + if remaining >= current_candy: + l.append(n_count*num_people + n*i + current_candy) + remaining -= current_candy + else: + l.append(n_count*num_people + n*i + remaining) + remaining = 0 + return l +# 28 ms 13.7 MB, +``` diff --git a/content/posts/2020-04-01-leetcode-1160.md b/content/posts/2020-04-01-leetcode-1160.md new file mode 100644 index 0000000..8a0e121 --- /dev/null +++ b/content/posts/2020-04-01-leetcode-1160.md @@ -0,0 +1,39 @@ +--- +title: leetcode-1160 +date: 2020-04-01 00:18:48 +tags: + - leetcode +categories: leetcode +--- + + +### 1160. 拼写单词 + +[题目](https://leetcode-cn.com/problems/find-words-that-can-be-formed-by-characters/) + + + + + + + +利用列表 remove 方法,检查 chars 中是否有足够的字母拼写 word + +```python +class Solution: + def countCharacters(self, words, chars: str) -> int: + + words_ = '' + for w in words: + lchars = list(chars) + try: + for l in w: + lchars.remove(l) + except: + continue + words_ += w + + return len(words_) +# 152 ms 14.1 MB +``` + diff --git a/content/posts/2020-04-01-leetcode-compress-string-lcci.md b/content/posts/2020-04-01-leetcode-compress-string-lcci.md new file mode 100644 index 0000000..eeaa862 --- /dev/null +++ b/content/posts/2020-04-01-leetcode-compress-string-lcci.md @@ -0,0 +1,42 @@ +--- +title: leetcode-compress-string-lcci +date: 2020-04-01 15:51:22 +tags: + - leetcode +categories: leetcode +--- + +### 面试题 01.06. 字符串压缩 + +[题目](https://leetcode-cn.com/problems/compress-string-lcci/) + + + + + +遍历一遍字符串,遇到跟上一个字符不同的字符时记录上一个字符的重复长度。 + + + +```python +class Solution: + def compressString(self, S: str) -> str: + if not S: + return S + c = '' + prev = S[0] + p_len = 1 + for w in S[1:]: + if w != prev: + c += '%s%s' % (prev, p_len) + prev = w + p_len = 1 + else: + p_len += 1 + c += '%s%s' % (prev, p_len) + if len(S) > len(c): + return c + return S +# 52 ms 13.8 MB +``` + diff --git a/content/posts/2020-04-09-leetcode-he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof.md b/content/posts/2020-04-09-leetcode-he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof.md new file mode 100644 index 0000000..bcb6fc9 --- /dev/null +++ b/content/posts/2020-04-09-leetcode-he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof.md @@ -0,0 +1,43 @@ +--- +title: leetcode-he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof +date: 2020-04-09 22:14:56 +tags: + - leetcode +categories: leetcode +mathjax: true +--- + +### 面试题57 - II. 和为s的连续正数序列 + +[题目](https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/) + + + + + + + +又是小学奥数。由等差数列求和公式$\cfrac{(首项 + 末项)×项数}{2}$ 可知,当首项为 1 的时候项数最多,又由于是连续正整数,$n^2 < (1+n)×n < (n+1)^2 $,那最大的 $n$ 就不大于 $\sqrt{2×target} + 1$。 + +由小到大遍历 $n$,可以求得首项。 + +```python +import math +class Solution: + def findContinuousSequence(self, target: int): + n = int(math.sqrt(2 * target) + 1) + if n < 2: + return [] + sum_list = [] + a = 0 + for i in range(2, n+1): + a = ((2 * target) / i + 1 - i) / 2 + if a and not a % 1: + a = int(a) + s_ = [] + for j in range(0, i): + s_.append(a + j) + sum_list.append(s_) + return sorted(sum_list) + # 60 ms 13.7 MB +``` diff --git a/content/posts/2020-04-09-leetcode-the-masseuse-lcci.md b/content/posts/2020-04-09-leetcode-the-masseuse-lcci.md new file mode 100644 index 0000000..43b249d --- /dev/null +++ b/content/posts/2020-04-09-leetcode-the-masseuse-lcci.md @@ -0,0 +1,48 @@ +--- +title: leetcode-the-masseuse-lcci +date: 2020-04-09 00:35:26 +tags: + - leetcode +categories: leetcode +mathjax: true +--- + +### 面试题 17.16. 按摩师 + +[题目](https://leetcode-cn.com/problems/the-masseuse-lcci/) + + + + + + + +一开始以为是用递归,想了半天没想出来,偷看了一下答案。答案的思路跟递归类似,假设在当前 $i$ 时刻,$dp[i][0]$ 为当前预约不接的情况下最长预约时间,$dp[i][1]$ 则为接受当前预约的最长预约时间。 + + + +那很显然,由于不能接受相邻两个预约,$dp[i][1] = dp[i-1][0] + nums_i$ + +不接受当前预约的话,上一个预约接不接受都可以,$dp[i][0] = max(dp[i-1][0], dp[i-1][1])$ + +最后只要比较两种情况即可 $max(dp[i][0], dp[i][1])$ + + + +```python +class Solution: + def massage(self, nums) -> int: + if not nums: + return 0 + n = len(nums) + not_choose = 0 + choose = 0 + for n in nums: + not_choose, choose = max(not_choose, choose), not_choose+n + return max(not_choose, choose) + # 52 ms 13.6 MB +``` + + + +这种问题原来有个名字叫[动态规划](https://zh.wikipedia.org/wiki/动态规划),上面推导的方程叫[状态转移方程](https://baike.baidu.com/item/状态转移方程),可以找找资料来看一下。 diff --git a/content/posts/2020-04-14-leetcode-add-two-numbers-ii.md b/content/posts/2020-04-14-leetcode-add-two-numbers-ii.md new file mode 100644 index 0000000..724f160 --- /dev/null +++ b/content/posts/2020-04-14-leetcode-add-two-numbers-ii.md @@ -0,0 +1,116 @@ +--- +title: leetcode-add-two-numbers-ii +date: 2020-04-14 23:22:39 +tags: + - leetcode +categories: leetcode +--- + +### 445. 两数相加 II + +[题目](https://leetcode-cn.com/problems/add-two-numbers-ii/) + + + + + + + +看到顺序的链表就想到用倒序链表的方法做,折腾了半天 + + + +```python +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + +class Solution: + def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: + def _reverse(l): + if l.next: + last = _reverse(l.next) + l.next.next = l + l.next = None + return last + return l + + l1e = _reverse(l1) + l2e = _reverse(l2) + new_l = ListNode(0) + head = new_l + c = 0 + import ipdb; ipdb.set_trace() + while l1e and l2e: + new_val = l1e.val + l2e.val + if c==1: + new_val += 1 + c = 0 + if new_val >= 10: + new_val -= 10 + c = 1 + + new_l.val = new_val + next_n = None + if l1e.next and l2e.next or c: + next_n = ListNode(c) + new_l.next = next_n + new_l = next_n + l1e = l1e.next + l2e = l2e.next + if l2e: + l1e = l2e + if not l1e and c: + l1e = ListNode(0) + while l1e: + new_l.val = l1e.val + new_l.val += c + c = 0 + if new_l.val >= 10: + c = 1 + new_l.val -= 10 + l1e = l1e.next + if l1e: + new_l.next = ListNode(0) + new_l = new_l.next + else: + new_l.next = ListNode(1) + + return _reverse(head) + + # 84 ms 13.9 MB +``` + +最后面各种进位的处理应该还可以更清晰优雅一些,但是懒得搞了,感觉很蠢。翻了答案看到了小 tips,需要倒序处理的情况可以用栈。 + +```python +class Solution: + def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: + s1, s2 = [], [] + while l1: + s1.append(l1.val) + l1 = l1.next + while l2: + s2.append(l2.val) + l2 = l2.next + ans = None + carry = 0 + while s1 or s2 or carry != 0: + a = 0 if not s1 else s1.pop() + b = 0 if not s2 else s2.pop() + cur = a + b + carry + carry = cur // 10 + cur %= 10 + curnode = ListNode(cur) + curnode.next = ans + ans = curnode + return ans + +作者:LeetCode-Solution +链接:https://leetcode-cn.com/problems/add-two-numbers-ii/solution/liang-shu-xiang-jia-ii-by-leetcode-solution/ +来源:力扣(LeetCode) +著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 +``` + +不过就执行效率来看差不多。 diff --git a/content/posts/2020-04-14-leetcode-design-twitter.md b/content/posts/2020-04-14-leetcode-design-twitter.md new file mode 100644 index 0000000..41b3b06 --- /dev/null +++ b/content/posts/2020-04-14-leetcode-design-twitter.md @@ -0,0 +1,84 @@ +--- +title: leetcode-design-twitter +date: 2020-04-14 16:11:41 +tags: + - leetcode +categories: leetcode +--- + +### 355. 设计推特 + +[题目](https://leetcode-cn.com/problems/design-twitter/) + + + + + +做出来倒是很简单,由于没有并发和特别的条件,测试数据量也不大。一开始搞错了,以为传入的 `twitterId` 就是自增的 id,结果其实是每条推的内容,所以增加了一个计数器去标记 id。 + +主要的考点应该是 `多路归并` 这个东西。我用的是排序,在数据量大的时候应该会有些问题。 + + + +```python +class Twitter: + + def __init__(self): + """ + Initialize your data structure here. + """ + self.tweets = {} + self.followers = {} + self._tid = 0 + + + def postTweet(self, userId: int, tweetId: int) -> None: + """ + Compose a new tweet. + """ + if not self.tweets.get(userId): + self.tweets[userId] = [] + self.tweets[userId].append((self._tid, tweetId)) + self._tid += 1 + + + def getNewsFeed(self, userId: int) : + """ + Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. + """ + foers = self.followers.get(userId, set()) + foers = foers.union((userId,)) + tweets = [] + for fo in foers: + tweets.extend(self.tweets.get(fo, [])[-10:]) + return [tw[1] for tw in sorted(tweets, reverse=True)[:10]] + + + def follow(self, followerId: int, followeeId: int) -> None: + """ + Follower follows a followee. If the operation is invalid, it should be a no-op. + """ + if not self.followers.get(followerId): + self.followers[followerId] = set() + self.followers[followerId].add(followeeId) + + + def unfollow(self, followerId: int, followeeId: int) -> None: + """ + Follower unfollows a followee. If the operation is invalid, it should be a no-op. + """ + if not self.followers.get(followerId): + self.followers[followerId] = set() + if followeeId in self.followers[followerId]: + self.followers[followerId].remove(followeeId) + +#100 ms 19.2 MB + +# Your Twitter object will be instantiated and called as such: +# obj = Twitter() +# obj.postTweet(userId,tweetId) +# param_2 = obj.getNewsFeed(userId) +# obj.follow(followerId,followeeId) +# obj.unfollow(followerId,followeeId) +``` + diff --git a/content/posts/2020-04-16-leetcode-01-matrix.md b/content/posts/2020-04-16-leetcode-01-matrix.md new file mode 100644 index 0000000..c9d1ca9 --- /dev/null +++ b/content/posts/2020-04-16-leetcode-01-matrix.md @@ -0,0 +1,48 @@ +--- +title: leetcode-01-matrix +date: 2020-04-16 12:26:34 +tags: + - leetcode +categories: leetcode +--- + +### 542. 01 矩阵 + +[题目](https://leetcode-cn.com/problems/01-matrix/) + + + + + + + +想了两种思路 + +1. 0 位置的上下左右是 1, 上下左右中有跟 1 相邻的就是 2,以此类推,从 0 的坐标开始往上下左右四个方向扩散。如果我们把同意个距离的看作是一层,可以用一个队列依次存放每一层的坐标,直至每个坐标都被计算过。 + + ```python + class Solution: + def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]: + m, n = len(matrix), len(matrix[0]) + dist = [[0] * n for _ in range(m)] + zeroes_pos = [(i, j) for i in range(m) for j in range(n) if matrix[i][j] == 0] + # 将所有的 0 添加进初始队列中 + q = collections.deque(zeroes_pos) + seen = set(zeroes_pos) + + # 广度优先搜索 + while q: + i, j = q.popleft() + for ni, nj in [(i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1)]: + if 0 <= ni < m and 0 <= nj < n and (ni, nj) not in seen: + dist[ni][nj] = dist[i][j] + 1 + q.append((ni, nj)) + seen.add((ni, nj)) + + return dist + ``` + + + +2. 从左上角开往右下角遍历矩阵,当前坐标的距离由左和上两个位置的值确定。遍历一遍后,再反过来从右下角开始往左上角遍历,当前坐标的距离根据右和下两个位置的值确定,比较这两次得出的值中较小的一个即为该点的距离。 + diff --git a/content/posts/2020-04-16-leetcode-merge-intervals.md b/content/posts/2020-04-16-leetcode-merge-intervals.md new file mode 100644 index 0000000..2a12781 --- /dev/null +++ b/content/posts/2020-04-16-leetcode-merge-intervals.md @@ -0,0 +1,51 @@ +--- +title: leetcode-merge-intervals +date: 2020-04-16 19:22:26 +tags: + - leetcode +categories: leetcode +--- + + +### 56. 合并区间 + + + +[题目](https://leetcode-cn.com/problems/merge-intervals/) + + + + + + + +首先将区间按起点由小到大排序,这样相邻的两个就能通过终点判断是否重合。 + + + +```python +class Solution: + def merge(self, intervals): + if not intervals: + return [] + intervals.sort() + merged = [] + l = len(intervals) + m = intervals[0] + for x in range(l-1): + j = intervals[x+1] + if m[1] >= j[0]: + if m[1] <= j[1]: + m = [m[0], j[1]] + else: + continue + else: + merged.append(m) + m = j + if m: + merged.append(m) + return merged +``` + + + diff --git a/content/posts/2020-04-16-leetcode-string-to-integer-atoi.md b/content/posts/2020-04-16-leetcode-string-to-integer-atoi.md new file mode 100644 index 0000000..10edf60 --- /dev/null +++ b/content/posts/2020-04-16-leetcode-string-to-integer-atoi.md @@ -0,0 +1,56 @@ +--- +title: leetcode-string-to-integer-atoi +date: 2020-04-16 19:50:10 +tags: + - leetcode +categories: leetcode +--- + +### 8. 字符串转换整数 (atoi) + +[题目](https://leetcode-cn.com/problems/string-to-integer-atoi/) + + + + + + + +没什么好说的,注意各种情况,识别到数字之后就一直要是数字。 + + + +```python +class Solution: + def myAtoi(self, str: str) -> int: + p = '' + str = str.lstrip() + n = '' + min_int = -2**31 + max_int = 2**31-1 + isnumeric = False + for x in str: + if not isnumeric and x == '-': + p = '-' + isnumeric = True + continue + if not isnumeric and x == '+': + isnumeric = True + continue + if x.isnumeric(): + n += x + isnumeric = True + else: + break + if not n: + return 0 + if int(n) > max_int: + if p: + return min_int + else: + return max_int + p += n + return int(p) +# 32 ms 13.6 MB +``` + diff --git a/content/posts/2020-04-21-leetcode-number-of-islands.md b/content/posts/2020-04-21-leetcode-number-of-islands.md new file mode 100644 index 0000000..2edaf97 --- /dev/null +++ b/content/posts/2020-04-21-leetcode-number-of-islands.md @@ -0,0 +1,50 @@ +--- +title: leetcode-number-of-islands +date: 2020-04-21 12:55:17 +tags: + - leetcode +categories: leetcode +--- + +### 200. 岛屿数量 + + + +[题目](https://leetcode-cn.com/problems/number-of-islands/) + + + + + + + +这种矩阵题现在第一反应就是用[广度优先搜索][0]做,类似之前算和0之间的距离那题。遍历矩阵,遇到 1 就将 1 改成 0,然后广度优先搜索找出 1 相邻的所有 1,这就是一个岛屿,以此类推。 + + + +```python +import collections +class Solution: + def numIslands(self, grid) -> int: + rows = len(grid) + if not rows: + return 0 + cols = len(grid[0]) + islands = 0 + for r in range(rows): + for l in range(cols): + if grid[r][l] == '1': + islands += 1 + grid[r][l] = '0' + neighbors = collections.deque([(r, l)]) + while neighbors: + x, y = neighbors.popleft() + for x_, y_ in [[x-1, y], [x+1, y], [x, y-1], [x, y+1]]: + if 0<=x_ + app\ + static\ + templates\ + __init__.py + views.py + tmp\ + run.py +``` + +Then start to write the template (file `app/templates/index.html`): + +``` html + + + + {{ title }} - microblog + + + +

Hello, {{ user.nickname }}!

+ + + +``` + +Now let's write the view function that uses this template (file `app/views.py`): + +``` python +from flask import render_template +from app import app + +@app.route('/') +@app.route('/index') +def index(): + user = {'nickname': 'ching'} # fake user + return render_template('index.html', + title='Home', + user=user) +``` + +`render_template` function is what we import from Flask framework to render the template. It uses [Jinja2](http://jinja.pocoo.org/) templating engine. + diff --git a/content/posts/Flask-Day-2.md b/content/posts/Flask-Day-2.md new file mode 100644 index 0000000..c417a09 --- /dev/null +++ b/content/posts/Flask-Day-2.md @@ -0,0 +1,82 @@ +--- +title: Flask Day 2 +date: 2016-02-16 22:45:06 +tags: + - flask +--- + +To handle web forms we use [Flask-WTF ](http://packages.python.org/Flask-WTF). So we need to write a config file (file `config.py`): + +``` python +WTF_CSRF_ENABLED = True +SECRET_KEY = 'you-will-never-guess' +``` + +And then you need to use this config (file `app/__init__.py`): + +``` python +from flask import Flask + +app = Flask(__name__) +app.config.from_object('config') + +from app import views +``` + +Let's build a simple form (file `app/forms.app`): + +``` python +from flask.ext.wtf import Form +from wtforms import StringField, BooleanField +from wtforms.validators import DataRequired + +class LoginForm(Form): + openid = StringField('openid', validators=[DataRequired()]) + remember_me = BooleanField('remember_me', default=False) +``` + +The `DataRequired()` is a validator that checks the field is empty or not. + +After that, we need a HTML page to show the form (file `app/templates/login.html`): + +``` html + +{% extends "base.html" %} + +{% block content %} +

Sign In

+
+ {{ form.hidden_tag() }} +

+ Please enter your OpenID:
+ {{ form.openid(size=80) }}
+

+

{{ form.remember_me }} Remember Me

+

+
+{% endblock %} +``` + + + +The final step is to code a view function that renders the template and receiving data from form (file `app/views.py`): + +``` python +from flask import render_template, flash, redirect +from app import app +from .forms import LoginForm + +# index view function suppressed for brevity + +app.route('/login', methods=['GET', 'POST']) +def login(): + form = LoginForm() + if form.validate_on_submit(): + flash('Login requested for OpenID="%s", remember_me=%s' % + (form.openid.data, str(form.remember_me.data))) + return redirect('/index') + return render_template('login.html', + title='Sign In', + form=form) +``` + diff --git a/content/posts/Postgresql Partitioning.md b/content/posts/Postgresql Partitioning.md new file mode 100644 index 0000000..6d82909 --- /dev/null +++ b/content/posts/Postgresql Partitioning.md @@ -0,0 +1,43 @@ +--- +title: Postgresql Partitioning +date: 2019-03-12 00:08:48 +tags: + - psql +--- + +`Partitioning` refers to splitting what is logically one large table inot smaller physical pieces. + +Currently, PostgreSQL supports partitioning via table [inheritance](https://www.postgresql.org/docs/9.6/ddl-inherit.html). Each partition must be created as a child table of a single parent table. **The parent table itself is normally empty**; It exists just to represent the entire data set. + +There are two forms of partitioning can be implemented in PostgreSQL: + +* Range Partitioning + + ​ The table is partitioning into "range" defined by a key column or a set of columns, with no overlap between the ranges of values assigned to different partitions. eg. partition by date ranges or by identifiers. + +* List Partitioning + + ​ The table is partitioned by explicitly listing which key values appear in each partition. + +### Implementing Partitioning + +1. Create the "master" / "parent" table, from which all the partitions will inherit. + + ​ This table will not contain any data. Do not define any check on this table, unless you intend them to be applied equally to all partitions. There is no point in defining any indexes or unique constraints on it either. + +2. Create "child" tables that each inherit form the master table. Normally, these tables will not add any columns to the set inherited from the master. + +3. Add table constraints to the partition tables to define the allowed key values in each partitions. + + ​ Ensure that the constraints guarantee that there is no overlap between the key values premitted in different partitions. And there is no difference in syntax between range and list partitioning. + +4. Create indexes on column(s) for each partitions. + +5. Optionally, define a trigger or rule to redirect data inserted into the master table to the appropriate partition. + +6. Ensure hte [constraint_exclusion](https://www.postgresql.org/docs/9.6/runtime-config-query.html#GUC-CONSTRAINT-EXCLUSION) configuration parameter is not disabled in `postgresql.conf`. If it is, queries will not be optimized as desired. + +### Trigger + +As we are creating new table and hopping data insered to right partition, a trigger function and a trigger are needed. + diff --git a/content/posts/TastyPie-Note-1.md b/content/posts/TastyPie-Note-1.md new file mode 100644 index 0000000..52f0634 --- /dev/null +++ b/content/posts/TastyPie-Note-1.md @@ -0,0 +1,47 @@ +--- +title: TastyPie Note 1 +date: 2016-05-10 17:00:00 +tags: + - tastypie + - django +--- + +### Flow Through The Request/Response Cycle + +Tastypie can be thought of as a set of class-based view that provide the API functionality. All routing/middleware/response-handling aspectss are the same as a typical Django app. Where the differs is in the view itself. + +Walking through what a GET request to a list endpoint looks like: + +- The `Resource.urls` are checked by Django's url resolvers. + +- On a match for the list view, `Resource.wrap_view('dispatch_list')` is called. `wrap_view` provides basic error handling & allows for returning serilized errors. + +- Because dispatch_list was passed to `wrap_view`, `Resource.dispatch_list` is called next. This is a thin wrapper around `Resource.dispatch`. + +- `dispatch` does a bunch of havy lifting. It ensures: + + - the requested HTTP method is in `allowed_methos` (`method_check`). + - the class has a method that can handle the request(`get_list`) + - the user is authenticated(`is_authenticated`) + - the user has no exceeded their throttle(`throttle_check`). + + At this point, `dispatch` actually calls the requested method (`get_list`). + +- `get_list` does the actual work of API. It does: + + - A fetch of the available objects via `Resource.obj_get_list`. In the case of `ModelResource`, this builds the ORM filters to apply (`ModelResource.build_filters`). It then gets the `QuerySet` via `ModelResource.get_object_list` (which performs `Resource.authorized_read_list` to possibly limit the set the user can work with) and applies the built filters to it. + - It then sorts the objects based on user input (`ModelResource.apply_sorting`). + - Then it paginates the results using the supplied `Paginator` & pulls out the data to be serialized. + - The objects in the page have `full_dehydrate` applied to each of them, causing Tastypie to traslate the raw object data into the fields the endpoint supports. + - Finally, it calls `Resource.create_response`. + +- `create_response` is a shortcut method that: + + - Determines the desired response format (`Resource.determine_format`). + - Serializes the data given to it in the proper format. + - Returns a Django `HttpResponse` (200 OK) with the serialized data. + +- We bubble back up the call stack to `dispatch`. The last thing `dispatch` does is potentially store that a request occured for future throttling (`Resource.log_throttled_access`) then either returns the `HttpResponse` or wraps whatever data came back in a response (so Django doesn't freak out). + + + diff --git a/content/posts/Tastypie.md b/content/posts/Tastypie.md new file mode 100644 index 0000000..6428887 --- /dev/null +++ b/content/posts/Tastypie.md @@ -0,0 +1,41 @@ +--- +title: Tastypie +date: 2016-05-04 12:02:00 +tags: + - tastypie + - django +--- + +#### Resources in Tastypie + +Resources are the heart of Tastypie. By defining a resource we can actually convert a model into an API stream. The data is automatically converted into API response. + + + +Understanding the process of creating a resource. + +1. Import ModelResource from Tastypie. +2. Import models from services app +3. Create custom resource by inheriting ModelResource and link app model in inner Meta class of resource. + +Add API URL in the urls.py of app. + + + +#### Dehydrating the JSON data + +![flow](https://impythonist.files.wordpress.com/2016/04/tastypie_ill.png?w=800) + + + +Dehydration in Tastypie means making alterations before sending data to the client. Suppose we need to send capitalized product names instead of small letters. Now we see two kinds of dehydrate methods. + +##### Dehydrate_field method + +This `dehydrate_field` is uesd to modify field on the response JSON. + +##### Dehydrate method + +Dehydrate method is useful for aadding additional fields to bundle (response data). + +Similarly using `hydrate` method we can alter the bundle data which is generated from request at the time of PUT or POST methods. diff --git a/content/posts/first-post.md b/content/posts/first-post.md new file mode 100644 index 0000000..01aa9d0 --- /dev/null +++ b/content/posts/first-post.md @@ -0,0 +1,9 @@ +--- +title: First Post +date: 2016-02-12 18:00:00 +--- + +This is the very first post I wrote, + +with [Typora](https://www.typora.io/) & [Hexo](https://hexo.io/). + diff --git a/content/search/_index.md b/content/search/_index.md new file mode 100644 index 0000000..b741f16 --- /dev/null +++ b/content/search/_index.md @@ -0,0 +1,5 @@ +--- +title: "Search" +date: 2022-02-07T22:48:28+08:00 +--- + diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..7928468 --- /dev/null +++ b/deploy.sh @@ -0,0 +1 @@ +hugo && rsync -avz --delete public/ root@173.82.143.112:/root/deploy/tunpok-blog diff --git a/layouts/partials/favicon.html b/layouts/partials/favicon.html new file mode 100644 index 0000000..e02d348 --- /dev/null +++ b/layouts/partials/favicon.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/android-chrome-144x144.png b/static/android-chrome-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..80b1bb387736c1f9b10b28089e472a4891a7ac8b GIT binary patch literal 6947 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 CI%l^4 literal 0 HcmV?d00001 diff --git a/static/browserconfig.xml b/static/browserconfig.xml new file mode 100644 index 0000000..b3930d0 --- /dev/null +++ b/static/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/static/favicon-16x16.png b/static/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..d593b7295566c337a5d49d8997d7212f928c1fd0 GIT binary patch literal 1112 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 literal 0 HcmV?d00001 diff --git a/static/mstile-150x150.png b/static/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..aac0e640c05e21b8a490a89e8b056849cc56420a GIT binary patch literal 6507 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 literal 0 HcmV?d00001 diff --git a/static/safari-pinned-tab.svg b/static/safari-pinned-tab.svg new file mode 100644 index 0000000..3b880e3 --- /dev/null +++ b/static/safari-pinned-tab.svg @@ -0,0 +1,16 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/static/site.webmanifest b/static/site.webmanifest new file mode 100644 index 0000000..8af025f --- /dev/null +++ b/static/site.webmanifest @@ -0,0 +1,14 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-144x144.png", + "sizes": "144x144", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/themes/fuji b/themes/fuji new file mode 160000 index 0000000..bca1c22 --- /dev/null +++ b/themes/fuji @@ -0,0 +1 @@ +Subproject commit bca1c2222aef812c840cf28d23f991dc1dae1599