Algorithm:61. Rotate List
https://leetcode-cn.com/problems/rotate-list/
Given a linked list, rotate the list to the right by k places, where k is non-negative.
Example 1:
Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL
Example 2:
Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL
分析:鏈表問題一般有兩種思路,一是先求出鏈表長度,然後利用長度計算下標,類似於數組操作;另一種是用雙指針,可以是快慢指針,也可以是滑動窗口,其思想是把下標關係轉換成兩個指針之間的距離,然後同時前進。
下面也用這兩種思路來解答這一題。有點特別的是,當k大於數組長度時,雙指針解法仍然要計算數組長度,否則,會很低效。
這兩種解法時間複雜度都是O(n),空間複雜度都是O(1)。當k小於等於鏈表長度時,解法二應該是優於解法一的,因爲只需要遍歷一遍;當k大於鏈表長度時,兩種解法是差不多的,甚至解法一可能更快一點,因爲解法二中多了一個指針需要移動。從Leetcode的提交記錄來看,解法一更快一點,且解法一思路更清晰一些,所以個人傾向於解法一。當然了,解法二也可以像解法一一樣,先求出鏈表長度,然後取模,然後再利用雙指針移動,但感覺有點多次一舉了。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
// 解法二:雙指針。當k小於等於鏈表長度時,遍歷一遍搞定;當k大於鏈表長度時,第一次遍歷仍然要計算出鏈表長度然後將k取模運算,否則當k遠遠大於鏈表長度時,快指針會做很多次無效移動。
public ListNode rotateRight(ListNode head, int k) {
if(head == null || head.next == null || k == 0)
return head;
ListNode p = head;
ListNode q = head;
int i;
for(i=1; i<= k && p.next != null; i++, p=p.next);
if(i <= k) { // i+1 is length of list
k %= i;
p = head;
for(i=0; i<k; i++, p=p.next);
}
if(p == q)
return head;
while(p.next != null) {
p = p.next;
q = q.next;
}
ListNode temp = q.next;
q.next = null;
p.next = head;
head = temp;
return head;
}
// 解法一:先求出長度,再計算需要斷開的位置,將後半段插到前面去,返回新的鏈表頭
private ListNode solutaion_1(ListNode head, int k) {
if(head == null || k == 0)
return head;
ListNode p = head;
int size = 1;
while(p.next != null) {
p = p.next;
size++;
}
// now p is point to the end
k %= size;
if(k == 0)
return head;
ListNode q = head;
for(int i=1; i< size-k; i++, q=q.next);
// now q is point to the k+1 from the end
ListNode temp = q.next;
q.next = null;
p.next = head;
head = temp;
return head;
}
}
Review: Cloud Computing History
https://medium.com/pgs-software/cloud-computing-history-7c2c188a30a
文章介紹了雲計算的歷史和演進過程,主要分爲如下幾塊內容:
- 時間共享主機。在19世紀50年代到80年代期間,計算機很昂貴,多個用戶共用一臺計算機很普遍,那時主要是通過時間片輪轉的方式共享,類似於現在單核cpu對多線程的調度。https://en.wikipedia.org/wiki/Time-sharing
- 互聯網。19世紀50年代末出現局域網,直到90年代,Internet才誕生。互聯網的誕生催生了大量的網絡應用,也激發了對服務器和數據中心的大量需求。
- 虛擬機。虛擬機的概念其實很早就出現了,1966年IBM就在操作系統中引入了虛擬機。2013年誕生的docker,是一種操作系統級別的虛擬化產品,它的出現使得微服務架構蓬勃發展。
- SaaS, PaaS, IaaS
- 真正的雲。亞馬遜EC2、微軟Azure Virtual Machines、谷歌Google Compute Engine
- 超越。
- 商業視角。Businesses that are able to keep up with these changes stand to continually gain the most benefits compared to those who sit idly by.與無所事事的人相比,能夠跟上變化的企業將持續獲得最大收益。
Tip:使用MySQL的官方Docker鏡像如何初始化數據庫
我使用的是官方mariadb基礎鏡像mariadb:latest
,我們只要把初始化數據庫的sql文件放到 /docker-entrypoint-initdb.d/
目錄下,容器在啓動的時候就會執行這個sql。除了sql文件,同樣也支持sh腳本和sql.gz文件。
有一點需要注意,這個初始化工作只會執行一次,一旦數據庫中已經有數據了,就不會再次初始化。
上述初始化工作是在基礎鏡像的 docker-entrypoint.sh
腳本里做的,這裏面會判斷在mysql的數據目錄下是否有 /mysql
子目錄,如果有的話,就不執行初始化的邏輯:
...
DATADIR="$(_get_config 'datadir' "$@")"
if [ ! -d "$DATADIR/mysql" ]; then
...
for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done