單向鏈結串列(Singly linked list)

整理

單向鏈結串列(Singly linked list)示意圖

每一個節點內含
next // 儲存下一個的節點
val // 數值

如果該節點next為null代表已經沒有下個節點串接。

如上圖範例:
數值為12節點的下個節點(next),接數值為99節點
數值為99節點的下個節點(next),接數值為37節點
而數值37節點的下個節點(next),表示沒有結點串接為null

Big-O
search 搜尋 O(N)

特點
只能做循序存取 (Sequential access)

C#程式碼

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

using System;

//節點
public class LinkNode
{
public int val;
public LinkNode next;
public LinkNode(int val,LinkNode next){
this.val = val;
this.next = next;
}
public LinkNode(int val){
this.val = val;
this.next = null;
}
};

public class Program
{
// SingleLinkList 串接節點
public static void Main()
{

LinkNode node1 = new LinkNode(12);
LinkNode node2 = new LinkNode(99);
LinkNode node3 = new LinkNode(37);

node1.next = node2; // 綁定node1的尾端接node2
node2.next = node3; // 綁定node2的尾端接node3


Console.WriteLine("手動印出節點:");
Console.WriteLine(node1.val); // 節點1
Console.WriteLine(node1.next.val);// 節點2 (節點1的下1個元素)
Console.WriteLine(node1.next.next.val);// 節點3 (節點1的下2個元素)
if(node1.next.next.next == null){ //節點4,尾端沒串接任何資料為null
Console.WriteLine("null");
}

Console.WriteLine("遍歷節點:");
LinkNode currentNode = node1;
while(currentNode != null){ // null表示為末端
Console.WriteLine(currentNode.val);
// 將當前節點指到下個節點
currentNode = currentNode.next;
}

Console.WriteLine("在節點1末端添加一個數值38的節點:");

LinkNode cursor = node1;
LinkNode newNode = new LinkNode(38);

// 添加節點在最後面
while(true){
if(cursor.next == null){
cursor.next = newNode; // 找到最尾端把null指派成新節點38
break;
}else{
cursor = cursor.next; //不斷往下找節點
}
}

Console.WriteLine(node1.next.next.next.val); //Q1:可取得38,這邊其實我有些不懂,是new參考的關係嗎,為什麼能夠改到node1?
}
}

結果:

1
2
3
4
5
6
7
8
9
10
11
手動印出節點:
12
99
37
null
遍歷節點:
12
99
37
在節點1末端添加一個數值38的節點:
38

目前有標記的Q1是現在不懂的地方,是new參考的關係嗎,為什麼能夠改到node1的值?
按照這篇來說,他指出C#只有call by value、call by address。
https://dotblogs.com.tw/daniel/2018/02/26/150443

1
2
3
在C#廣義來說
基本型別 Struct (int,double,float,byte ...) 可看作 傳值
一般型別 Class (自訂Class ,SqlConnectio....) 可看作 傳址 更精確來說是傳Stack的值(指向Heap的記憶體位置)

沒傳參考,只有傳值和傳址,所以Q1可能是因為把node1當時的記憶體位置指派給cursor,所以就代表著node1的位置。
而直到最後終端null時,在把new出來的節點38的位址接上去。
把記憶體位置傳到另一個記憶體位置的值上

參考資料
https://kopu.chat/2017/06/02/c-%e8%aa%9e%e8%a8%80%ef%bc%9a%e9%8f%88%e7%b5%90%e4%b8%b2%e5%88%97linked-list%e7%9a%84%e5%bb%ba%e7%ab%8b%e8%88%87%e5%88%aa%e9%99%a4/
https://en.wikipedia.org/wiki/Linked_list
https://zh.wikipedia.org/wiki/%E9%93%BE%E8%A1%A8

LeetCode - 2. add-two-number

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example 1:

Input: l1 = [2,4,3], l2 = [5,6,4]
Output: [7,0,8]
Explanation: 342 + 465 = 807.

Example 2:

Input: l1 = [0], l2 = [0]
Output: [0]

Example 3:

Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
Output: [8,9,9,9,0,0,0,1]

Constraints:

The number of nodes in each linked list is in the range [1, 100].
0 <= Node.val <= 9
It is guaranteed that the list represents a number that does not have leading zeros.

C#解答1

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
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/

public class Solution {

public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
ListNode currentL1=l1,currentL2=l2;
ListNode resultListNode = new ListNode();
ListNode resultList = resultListNode; // 為什麼可以儲存?
Boolean isAddOne = false;
while(true){
int sum = 0;
if(currentL1!= null){
sum += currentL1.val;
currentL1 = currentL1.next;
}
if(currentL2!= null){
sum += currentL2.val;
currentL2 = currentL2.next;
}

if(isAddOne){
sum+=1;
}

if(sum >= 10){
isAddOne = true;
sum = sum%10;
}else{
isAddOne = false;
}

resultListNode.next = new ListNode(sum);
resultListNode = resultListNode.next; // ?? 因C#傳址可影響到原本的resultList

if(currentL1 == null && currentL2 == null && isAddOne == false){
break;
}
}
return resultList.next;
}
}

Runtime: 96 ms, faster than 97.02% of C# online submissions for Add Two Numbers.
Memory Usage: 28.2 MB, less than 82.24% of C# online submissions for Add Two Numbers.

思路
  這題是中級題目,但對我來說其實已經蠻吃力了,在進位出807這段其實很容易,但後面那段linknode塞進去時卡很久(註解那部分),最後還是上網查了一下,資料結構真的要熟悉,以及”抽象”思考的方式,目前就是要拿筆整理,腦中還是很難構造出。
後記補充:
第18、43行是參考,當作一個cursor來遍歷。

參考
LeetCode 2. Add Two Numbers
https://skyyen999.gitbooks.io/-leetcode-with-javascript/content/questions/2md.html
https://dotblogs.com.tw/daniel/2018/02/26/150443

LeetCode - 1. Two Sum

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

You can return the answer in any order.

Example 1:

Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Output: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6
Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6
Output: [0,1]

Constraints:

2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
Only one valid answer exists.

Follow-up: Can you come up with an algorithm that is less than O(n^2) time complexity?

C#解答1

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Solution {
public int[] TwoSum(int[] nums, int target) {
for (int i = 0; i < nums.Length - 1 ; i++)
{
for(int j=i+1;j < nums.Length;j++){
if(nums[i]+nums[j] == target){
return new int[] { i,j };
}
}
}
return new int[]{};
}
}

Runtime: 316 ms, faster than 51.22% of C# online submissions for Two Sum.
Memory Usage: 32.1 MB, less than 96.22% of C# online submissions for Two Sum.

javascript解答1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
for (let i = 0; i < nums.length - 1 ; i++)
{
for(let j=i+1;j < nums.length;j++){
if(nums[i]+nums[j] === target){
return [i,j];
}
}
}
return [];
};

Runtime: 116 ms, faster than 35.30% of JavaScript online submissions for Two Sum.
Memory Usage: 39.2 MB, less than 93.93% of JavaScript online submissions for Two Sum.

思路
目前解法1是用兩個for迴圈,時間複雜度O(n^2)。
如果有問題歡迎指證!@@

延伸閱讀
[演算法]如何衡量程式的效率?——論時間複雜度(Time Complexity)

決定未來走的方向

決定未來走的方向了。

前言

目前有正職2年軟體工程師經驗,之後SOHO接了大學同學案子以及維護到現在,但疫情原因加上案源不穩下,覺得勢必要重回職場。工作到現在這段期間也學到不少事,覺得該給自己訂下一條路了--Web Developer 這條路,並以後端為主去延伸。

這是個好決定嗎?

以目前來講,前端、後端部分已有產出的經驗,所以目標開始走向後端、架構為重的開發。我不能說是最好的決定,但在摸熟了前端的大致內容,覺得在快速的變遷下還是有必要走到後端。以往後端都是偏向用laravel(因為當時微軟還沒開始開源),而現在走微軟.net core,將會是以C#為主要開發語言,以現在來說有相對完整的學習地圖,而C#也是我2015年接觸過的語言,前公司亦是用C#,所以要精進也比較容易些。

然後,下面是在工作期間曾經處理過、實際參與過的,API串接、切版、業務邏輯、設計規劃討論,前端的畫面、業務邏輯,期間使用RESTful API,後端經驗就是API、BUG修正等等。

目前完成且有實際上線的作品:

1.ERP形象頁
https://www.linkchain.tw/erp/
2.豬場e把抓
https://pigepm.coa.gov.tw/
3.農業易遊網
https://ezgo.coa.gov.tw/
4.田邊好幫手
https://m.coa.gov.tw/
5.農產品生產追溯
https://qrc.afa.gov.tw/

結論

以目前來說,就是製作一個涵蓋前、後端加上自己經驗的作品,然後一個月後再去面試。不知道結果會如何,不過是目前比較踏實的路了。現在25歲,還來得及,怕的是遲遲做不下結論。

最後,引用鋼之鍊金術師的一段話:「人沒有犧牲就什麼都得不到,為了得到什麼東西,就需要付出同等的代價。」
我已經決定了,就走下去。