作者: paul

LeetCode: H-Index

LeetCode: H-Index

Given an array of integers citations where citations[i] is the number of citations a researcher received for their ith paper, return the researcher’s h-index.

According to the definition of h-index on Wikipedia: The h-index is defined as the maximum value of h such that the given researcher has published at least h papers that have each been cited at least h times.

Example 1:

Input: citations = [3,0,6,1,5]
Output: 3
Explanation: [3,0,6,1,5] means the researcher has 5 papers in total and each of them had received 3, 0, 6, 1, 5 citations respectively.
Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, their h-index is 3.

Example 2:

Input: citations = [1,3,1]
Output: 1

Constraints:

  • n == citations.length
  • 1 <= n <= 5000
  • 0 <= citations[i] <= 1000

Version 1

public class Solution {
        public int HIndex(int[] citations)
    {
       var length = citations.Length;

        var index = 0;
        for (int i = 1; i <= length ; i++)
        {
            var count = citations.Count(a=>a>=i);
            if (count >= i && i > index)
                index = i;
        }

        return index;
    }

}

//testing Code
    H_Index _indexer = new H_Index();

    [Test]
    public void Test001()
    {
        _indexer.HIndex(new int []{3, 0, 6, 1, 5}).Should().Be(3);
    }
    
    [Test]
    public void Test002()
    {
        _indexer.HIndex(new int []{1, 3, 1}).Should().Be(1);
    }

直覺的思路在一開始我確實沒有看理解,所以一直沒看準一個原則就是h值必須同時要在陣列中找到至少 >= h值有h個項目。這兩個必須相等,一開始我還以為是要找出符合hindex的陣列位置。

送出後一看不得了,笑死,這複雜度是O(N2),而且以提交結果才beat 5.74%,代表一定要再想辦法優化了

第二版本

其實簡化計算規則,就是先排序由大到小,然後比對若>指數,就指數累加(=不累加)

var sorted = citations.OrderByDescending(a=>a).ToArray();

var h = 0;
foreach (var value in sorted)
{
if (value > h)
h++;
}

return h;

這個版本是Linq 對int[]的排序是O(n log n)

看看其他人的版本改寫如下(beat 95%)

C#陣列預設排序為 快速排序,也是O(nlogn),再走訪一次n個迴圈是O(n),表示方式為 O(n log n + n )直接呈現O(n log n),因為由大到小,所以一旦比對不符合就可以break掉

演算法-常見的排序演算法與其平均時間複雜度

演算法-常見的排序演算法與其平均時間複雜度

氣泡排序法(Bubble Sort)

氣泡排序法的一個常見口訣是「相鄰比較,交換位置」。每個詞都代表了氣泡排序的一個關鍵步驟:

  1. 相鄰比較(Compare Adjacent):比較相鄰的兩個元素,並根據排序規則確定它們的順序。
  2. 交換位置(Swap):如果相鄰的兩個元素的順序不符合排序規則,則交換它們的位置。
  3. 重複上述步驟,對數列中的所有元素進行相鄰比較和位置交換,直到整個數列排序完成。

平均時間複雜度為: O(n²)

選擇排序法(Selection Sort)

常見口訣是「尋找最小,交換位置」。每個詞都代表了選擇排序的一個關鍵步驟

  1. 尋找最小(Find Minimum):從未排序部分的數列中尋找最小的元素。
  2. 交換位置(Swap):將找到的最小元素與未排序部分的第一個元素交換位置。
  3. 重複上述步驟,繼續尋找最小元素並進行交換,直到整個數列排序完成。

平均時間複雜度為: O(n²)

插入排序法(Insertion Sort)

插入排序法的一個常見口訣是「一一插入,保有順序」。每個詞都代表了插入排序的一個關鍵步驟:

  1. 一一插入(Insert One by One):從未排序部分的數列中逐一選取元素,並將其插入到已排序部分的正確位置。
  2. 保有順序(Maintain Order):在插入元素時,確保已排序部分的元素仍保持升序或降序的順序。
  3. 重複上述步驟,繼續逐一插入元素,直到整個數列排序完成。
    public static void InsertionSortAlgorithm(int[] arr)
    {
        int n = arr.Length;

        for (int i = 1; i < n; i++)
        {
            int key = arr[i];
            int j = i - 1;

            while (j >= 0 && arr[j] > key)
            {
                arr[j + 1] = arr[j];
                j--;
            }

            arr[j + 1] = key;
        }
    }

平均時間複雜度為: O(n²)

合併排序法(Merge Sort)

合併排序法的一個常見口訣是「分而治之」。

合併排序的主要思想是通過分治的策略,將數列不斷分割成更小的部分,然後將這些部分按照順序合併起來,形成一個有序序列。合併排序具有穩定性和較好的時間複雜度,特別適用於處理較大規模的數列排序。

    public static void MergeSortAlgorithm(int[] arr, int left, int right)
    {
        if (left < right)
        {
            int middle = (left + right) / 2;

            MergeSortAlgorithm(arr, left, middle);
            MergeSortAlgorithm(arr, middle + 1, right);

            Merge(arr, left, middle, right);
        }
    }

    private static void Merge(int[] arr, int left, int middle, int right)
    {
        int n1 = middle - left + 1;
        int n2 = right - middle;

        int[] leftArr = new int[n1];
        int[] rightArr = new int[n2];

        for (int i = 0; i < n1; i++)
        {
            leftArr[i] = arr[left + i];
        }

        for (int j = 0; j < n2; j++)
        {
            rightArr[j] = arr[middle + 1 + j];
        }

        int k = left;
        int p = 0;
        int q = 0;

        while (p < n1 && q < n2)
        {
            if (leftArr[p] <= rightArr[q])
            {
                arr[k] = leftArr[p];
                p++;
            }
            else
            {
                arr[k] = rightArr[q];
                q++;
            }

            k++;
        }

        while (p < n1)
        {
            arr[k] = leftArr[p];
            p++;
            k++;
        }

        while (q < n2)
        {
            arr[k] = rightArr[q];
            q++;
            k++;
        }
    }

平均時間複雜度為: O(n log n)

快速排序法(Quick Sort)

常見口訣是「選基、劃分、遞迴」。每個詞都代表了快速排序的一個關鍵步驟

  1. 選基(Choose Pivot):從數列中選擇一個基準點(pivot)。
  2. 劃分(Partition):將數列劃分為兩個部分,一部分是小於等於基準點的元素,另一部分是大於基準點的元素。
  3. 遞迴(Recursion):對劃分後的兩個部分進行遞迴排序,重複上述步驟,直到排序完成。
    public static void QuickSortAlgorithm(int[] arr, int left, int right)
    {
        if (left < right)
        {
            int partitionIndex = Partition(arr, left, right);

            QuickSortAlgorithm(arr, left, partitionIndex - 1);
            QuickSortAlgorithm(arr, partitionIndex + 1, right);
        }
    }

    private static int Partition(int[] arr, int left, int right)
    {
        int pivot = arr[right];
        int i = left - 1;

        for (int j = left; j < right; j++)
        {
            if (arr[j] < pivot)
            {
                i++;
                Swap(arr, i, j);
            }
        }

        Swap(arr, i + 1, right);
        return i + 1;
    }

    private static void Swap(int[] arr, int i, int j)
    {
        (arr[i], arr[j]) = (arr[j], arr[i]);
    }

平均時間複雜度為: O(n log n)


其他少聽/用過的排序

希爾排序法(Shell Sort)

插入排序法的改良,幾乎所有狀況都能比O(n²)來得快

堆積排序法(Heap Sort)

平均時間複雜度為: O(n log n)

桶排序法(Bucket Sort)

非比較排序,平均時間複雜度為: O(n+k)

基數排序法(Radix Sort)

同屬非比較排序

LeetCode – Majority Element (Easy)

LeetCode – Majority Element (Easy)

Given an array nums of size n, return the majority element.

The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array.

Example 1:

Input: nums = [3,2,3]
Output: 3

Example 2:

Input: nums = [2,2,1,1,1,2,2]
Output: 2

Constraints:

  • n == nums.length
  • 1 <= n <= 5 * 104
  • -109 <= nums[i] <= 109

Follow-up: Could you solve the problem in linear time and in O(1) space?

我的第一版本:


public class Solution {
    public int MajorityElement(int[] nums) {
        if (nums.Length == 1)
            return nums[0];
            
        var max = nums.Length / 2;
        var result = 0;
        Dictionary<int, int> counter = new Dictionary<int, int>();
        foreach(var n in nums)
        {
            if (counter.ContainsKey(n))
            {
                counter[n]++;
                if (counter[n] > max)
                {
                    max = counter[n];
                    result = n;
                }
                    
            }
            else
            {
                counter.Add(n, 1);
            }
        }

        return result;
    }
}

直覺想 就用 計數器去實作了,但這樣的算法
這麼做的時間複雜度為O(n),空間複雜度亦為O(n)

以下 都不是我寫的,純粹學習

看到一種一行的寫法,其意涵為majority element一定會通過中間的位置。那定義就會叫過半數吧XD

public class Solution 
{
    public int MajorityElement(int[] nums) 
	=>nums.GroupBy(x => x).Select(x => new { x.Key, c = x.Count()}).OrderByDescending(x => x.c).FirstOrDefault().Key;
}

寫法2

public class Solution {
    public int MajorityElement(int[] nums) {
        //return nums.ToList().GroupBy(x => x).OrderByDescending(g=>g.Count()).Select(y=>y.Key).FirstOrDefault();
        Array.Sort(nums);
        return nums[nums.Length/2];
    }
}

Time complexity:
O(nlog(n))

  • Space complexity:
    No extra space is used

最想瞭解:

public class Solution {
    public int MajorityElement(int[] nums) {
        int count = 0;
        int? candidate = null;

        foreach (int num in nums) {
            if (count == 0) candidate = num;
            count += (num == candidate) ? 1 : -1;
        }

        return candidate.Value;
    }
}
  • Time complexity:
    O(n)
  • Space complexity:
    O(1)

ChatGPT版本

using System;

class Program
{
    static int MajorityElement(int[] nums)
    {
        int count = 0;
        int candidate = 0;

        foreach (int num in nums)
        {
            if (count == 0)
                candidate = num;

            if (num == candidate)
                count++;
            else
                count--;
        }

        return candidate;
    }

    static void Main(string[] args)
    {
        int[] nums = { 2, 2, 1, 1, 1, 2, 2 };
        int majority = MajorityElement(nums);

        Console.WriteLine("Majority element: " + majority);
    }
}

說明如下:
Boyer-Moore投票算法(Boyer-Moore Voting Algorithm)是一種用於在一個具有大量元素的列表中查找多數元素的有效方法。該算法的時間複雜度為O(n),其中n是列表的大小。該算法是由Robert S. Boyer和J Strother Moore於1981年提出的。

算法的基本思想是假設列表中的一個元素為多數元素,然後進行遍歷,對於每個元素,如果與候選元素相同,就將候選元素的計數器加1,否則減1。如果計數器為0,則將候選元素更換為當前元素。最後剩下的候選元素就是多數元素。

Reference:

https://leetcode.com/problems/majority-element/description/?envType=study-plan-v2&envId=top-interview-150

https://ithelp.ithome.com.tw/articles/10213285

LeetCode 20大挑戰- 清單-置頂

LeetCode 20大挑戰- 清單-置頂

新增HackerRank筆記

  • New Year Chaos(Link)

Finished

  1. LRU Cache (Done) Link
    • List, dictionary, linked list
  2. Median of Two Sorted Arrays Link
    • Array merge, sorting
    • 困難的是如何達成O(log(m+n))
  3. Palindrome Number Link
    • string function
  4. Majority Element Link
  5. H-Index Link
  6. Two-Sum Link
    • HashTable
  7. Task Scheduler Link
  8. Three-Sum Link
    • Two Pointer
  9. Reverse Interger Link

Selected

  • Container With Most Water
    • Array, Two Pointer, Greedy
  • Reverse Nodes in k-Group(Hard) – Problem
    • Linked List, Recursion
  • Regular Expression Matching – Problem
    • String, Dynamic Programming, Recursion
  • Longest Substring Without Repeating Characters – Problem
    • String, hashtable , Sliding window
  • Substring with Concatenation of All Words
    • Sliding Window , string, HashTable
  • Minimum Path Sum
    • Array, DP, Matrix
  • Best Time to Buy and Sell Stock III (Best Time to Buy and Sell Stock IV)
    • Array, DP

LeetCode暖身-Palindrome Number

LeetCode暖身-Palindrome Number

Given an integer x, return true if x is a 

palindrome, and false otherwise.

Example 1:

Input: x = 121
Output: true
Explanation: 121 reads as 121 from left to right and from right to left.

Example 2:

Input: x = -121
Output: false
Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.

Example 3:

Input: x = 10
Output: false
Explanation: Reads 01 from right to left. Therefore it is not a palindrome.

Constraints:

  • -231 <= x <= 231 - 1
My Answer:
public class Solution {
    public bool IsPalindrome(int x) {
        string xStr = x.ToString();

        string reverseXStr = new string(xStr.Reverse().ToArray());

        return xStr == reverseXStr;
    }
}

註: 一時間會想直接拆字串一正一反比對,後來想一下好像有一個reverse的function可以用
LeetCode Median of Two Sorted Arrays

LeetCode Median of Two Sorted Arrays

看到Hard就來看看

Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.

The overall run time complexity should be O(log (m+n)).

Example 1:

Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.

Example 2:

Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.50000
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.

Constraints:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106
直覺寫法
public class Solution {
    public double FindMedianSortedArrays(int[] nums1, int[] nums2) {
                var merged = nums1.Concat(nums2);

        var mergedArray = merged.OrderBy(a => a).ToArray();
        
        if (mergedArray.Length % 2 > 0)
        {
            var median = mergedArray[(mergedArray.Length - 1) / 2];
            return median;
        }
        else
        {
            var medianFirst = mergedArray[((mergedArray.Length) / 2)-1];
            var medianLast = mergedArray[((mergedArray.Length) / 2)];
            return ((double)medianFirst + (double)medianLast) / 2;
        }
    }
}

結果就這樣過了,到底為什麼是Hard?

看了某些人也是這樣寫
Complexity:
Time complexity: The time complexity is dominated by the sorting operation, which takes O(N log N) time, where N is the total number of elements in nums1 and nums2.
Space complexity: The space complexity is determined by the size of the combined list nums, which is O(N), where N is the total number of elements in nums1 and nums2.

public class Solution {
    public double FindMedianSortedArrays(int[] nums1, int[] nums2) {
         List<int> nums = nums1.ToList();
            nums.AddRange(nums2.ToList());
            nums.Sort();
            if(nums.Count % 2 == 1)
                return nums[nums.Count/2];

            return (nums[nums.Count/2] + nums[nums.Count/2 - 1])/2.0;
    }
}

所以這題的難度感覺是如何 說明其時間複雜度吧XD

LeetCode LRU快取實作 C#(Least Recently Used Cache)

LeetCode LRU快取實作 C#(Least Recently Used Cache)

7月開始新計畫: Leet Code 2日一題(1、3丶5) 各找一個c#適合解決的LeetCode 題目來保持暑期醒腦

題目:https://leetcode.com/problems/lru-cache/

wiki: https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU

Implement the LRUCache class:

  • LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
  • int get(int key) Return the value of the key if the key exists, otherwise return -1.
  • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.

The functions get and put must each run in O(1) average time complexity.

Example 1:

Input
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]

Explanation

LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, 1); // cache is {1=1} lRUCache.put(2, 2); // cache is {1=1, 2=2} lRUCache.get(1); // return 1 lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3} lRUCache.get(2); // returns -1 (not found) lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3} lRUCache.get(1); // return -1 (not found) lRUCache.get(3); // return 3 lRUCache.get(4); // return 4

Constraints:

  • 1 <= capacity <= 3000
  • 0 <= key <= 104
  • 0 <= value <= 105
  • At most 2 * 105 calls will be made to get and put.

LinkedList第一版本:

namespace LeetCode2023_Paul.LRUCache;

public class LRUCache
{
    private Node? _rootNode;
    private int _capacity;


    public LRUCache(int capacity)
    {
        _rootNode = new Node();
        _capacity = capacity;
    }

    private (int, Node) SearchKey(Node? currentNode, int key)
    {
        if (currentNode == null)
        {
            return (-1, null);
        }

        return currentNode.Key != key ? SearchKey(currentNode.Next, key) : (currentNode.Value, currentNode);
    }

    private int CountNode(Node? currentNode)
    {
        if (currentNode == null)
        {
            return 0;
        }

        return 1 + CountNode(currentNode.Next);
    }

    public int Get(int key)
    {
        var (value, foundNode) = SearchKey(_rootNode, key);

        if (value != -1)
        {
            if (foundNode.Key != _rootNode.Key)
                RemoveNode(foundNode);
            AddNode(foundNode);
        }

        return value;
    }

    private void removeLast(Node currentNode)
    {
        if (currentNode.Next != null && currentNode.Next.Next == null)
        {
            currentNode.Next = null;
        }
        else if (currentNode.Next != null)
        {
            removeLast(currentNode.Next);
        }
    }

    public void Put(int key, int value)
    {
        var (foundValue, foundNode) = SearchKey(_rootNode, key);

        if (foundValue == -1)
        {
            if (CountNode(_rootNode.Next) == _capacity)
            {
                //remove last
                removeLast(_rootNode);
            }

            Node newRootNode = null;
            newRootNode = new Node() {Key = key, Value = value, Next = null};
            AddNode(newRootNode);
        }
        else
        {
            RemoveNode(foundNode);
            foundNode.Value = value;
            foundNode.Prev = null;
            AddNode(foundNode);
        }
    }


    private void AddNode(Node node)
    {
        var after = _rootNode.Next;
        _rootNode.Next = node;

        node.Prev = _rootNode;
        node.Next = after;
        if (after != null)
            after.Prev = node;
    }


    private void RemoveNode(Node node)
    {
        Node before = node.Prev;
        Node after = node.Next;
        before.Next = after;
        if (after != null)
            after.Prev = before;
    }
}

public class Node
{
    public int Key { get; set; }
    public int Value { get; set; }

    public Node? Prev { get; set; }
    public Node? Next { get; set; }
}

使用LinkedList的寫法後(突然發現腦袋真的要很有結構跟圖像化的能力XD),奮戰許久終於通過了測試(可能我資質太差)

結果要提交的時候,壓測悲劇出現XD。這個 我們常常開發的時候會遇到的情況真的滿類似的

先求有,就上線,殊不知一個高峰或是沒想到情況就是會這樣發生了..,然後導致程式 crash或lock, timeout了. 。很有既視感 。

結果Leetcode網址直接給我噴 Time Limit Exceeded XD

後來仔細參考了一下定義。重新 調整了一下 C#的寫法,後來看了一些人的作法,覺得確實可以用很多c#的物件特性直接解決,改寫如下 :

using System;
using System.Collections.Generic;
using System.Linq;

namespace LeetCode2023_Paul.LRUCacheV1;

public class LRUCacheV2
{

    private int _capacity;
    private List<int> _keys;
    private Dictionary<int, int> _cache;

    public LRUCacheV2(int capacity)
    {
        _keys = new List<int>();
        _cache = new Dictionary<int, int>();
        _capacity = capacity;
    }

    public int Get(int key)
    {
        var value = -1;
        if (_cache.ContainsKey(key))
        {
            value = _cache[key]; 
            //有被抓到的key重新 add到後面,實現愈少用到的key會浮在第一個
            _keys.Remove(key);
            _keys.Add(key);
        }
        return value;
    }
    
    public void Put(int key, int value) {
        if (_cache.ContainsKey(key))
        {
            _keys.Remove(key);
            _keys.Add(key);
            _cache[key] = value;
        }
        else
        {
            if (_cache.Keys.Count == _capacity)
            {
                //因為最少用到的Key總是在第一個,所以cache數滿了的話,就移掉第一個(這邊跟NodeList不太一樣,但是利用List的特性可以作一樣效果
                //LRU的意思就是Least Recently Used, 相反則是LFU Frequently Used
                var lruKey =  _keys.FirstOrDefault();
                _keys.Remove(lruKey);
                _cache.Remove(lruKey);
            }

            _keys.Add(key);
            _cache.Add(key, value);
        }
    }
}

哈,簡單很多,這邊C#實作上幾點重點:

Dictionary是無序的排列集合,不可以用來作dequeue(像他的last,first)會有問題

利用 List<int>作有序的管理,然後Dictionary的Key作KeyValue的Mapping

這樣是最快的,也不用自已串LinkedList作遞迴,非常的方便~~

與其使用 LInkedList在每次抓 node的時候作movement,不如直接remove重加來的直覺~!

終於為部落格加上SSL設定(劇情急轉直下)

終於為部落格加上SSL設定(劇情急轉直下)

我的網站在http的世界中許久 ,這陣子終於想來弄SSL環境了(因為 老婆要寫網址XD) 先前我記得是卡在PH …

以上就是庫存頁檔救回的一小段字 XD

無意見發現多年前設定的mariadb,密碼一點都沒有安全性,結果就被破解了

苦腦許久,發現我在 6月初有備份image,至少還救的回2022年以前的文章 Orz

目前我先 救到這邊…

後續故事跟被入侵綁架的故事再補上….

以下是phpmyadmin被瘋逛try 密碼的logs(從docker logs來的)

資料庫還給我留下警語

GCP平台 VM STARTUP SCRIPT 進行UFW DISABLE 記錄

GCP平台 VM STARTUP SCRIPT 進行UFW DISABLE 記錄

這陣子老婆想要寫網誌 (親子遊日後的心血來潮)

無奈各大平台包含xuite,以前的無名收的收,國外的新平台,又覺得好像不熟悉所以腦筋動到自架平台 ,剛好我有這個wordpress養在那邊

不過,因為想說要有一些公開內容,所以還是專業一點弄一下ssl

SSL申請事小,結果我在try ssh跟firewall簡稱(FW)的時候,為了檢查vm是否有fw

所以apt安裝了ufw,殊不知是惡夢的開始

先前因為常常用 ssh連線,不過這次我索性使用了gcp web SSH連過去,想說,應該是滿無腦的

也確實方便,webssh還整合了上傳下載功能,所以我可以很方便的上傳我將要用的SSL證書跟private key

(但上傳下載也其實就是ssh的指令啦)

結果我一開始裝完ufw後 ,一開始也好好的,繼續去檢查gcloud(不是vm哦)虛擬網路的防火牆

後來一重開VM後,就突然 webssh不進去了…,因為同時在調整gcp ssl的防火牆設定,所以我”以為”

我動了什麼設定造成了這個結果(盲點)

註:這段真的超笨的,一直鑽牛角尖在 外層的fw上,還加了全通的rule也不行..

後來我才知道 ,就是vm我因為裝了ufw,所以自己也有一層防火牆被打開了,所以外殼的就無法進入這個vm

這就有點像在vm中移除了網路界面卡,無法再透過網路的方式連進去了,以往有主機,還可以實體操作

之所以把gcp的設定問題排除主要也是我重新生成了一個新的vm,其實舊的image我也生成過,但生成後,重新掛”新”的獨立網路 還是連不進去,所以我用新的VM加新的網路後,用 我認知的外層 fw設定下,嗯可以,ssh的進去

所以我覺得應該還是在vm自已本身身上,猜想到我曾安裝ufw這一點後,我就試著設定ufw disable的語法到VM的開機指令去…想說碰碰運氣

在 VM的執行個體 編輯視窗..可以找到以下欄位

重啟動vm後 ,果然連進去了..恩 …自己搞自己..結案

註∶其實為了fw去安裝 ufw造成這個風波,在cloud的環境中,若能確認是內網的話(不對外),其實可以依賴google cloud的網路跟防火牆的設定就好(有界面萬歲),vm就分工上可以單純一點處理自己該做的事~就好