Home > Reflections | ⏮️ ⏭️ 2024-06-01 🔍 Research I want to streamline my process for creating notes for YouTube videos. I name all of my note files with only lowercase letters, numbers, and dashes (/^[a-z0-9-]+/)… aka kabob-case I use QuickAdd and Templater to create new notes. I want to provide the name of a video have a function format the name according to my file naming scheme create a new file with the formatted name use the unformatted, provided name as the heading and in various metadata fields here’s one post about someone doing something kinda similar that may be helpful https://forum.obsidian.md/t/quickadd-template-filename-to-fetch-date-from-active-file/53656/4 🧠 Education 💡 What Makes Some Brains More Focused Than Others? | Marvin Chun | TEDxKFAS 🏋🏻 Practice Merge 2 sorted lists You are given the heads of two sorted linked lists list1 and list2. Merge the two lists into one sorted list. The list should be made by splicing together the nodes of the first two lists. Return the head of the merged linked list. /** * Definition for singly-linked list. * class ListNode { * val: number * next: ListNode | null * constructor(val?: number, next?: ListNode | null) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } * } */ /* Notes: - lists are sorted in ascending order - be careful managing cursors and pointers since we're mutating the input Plan: 1. Initialize empty return value 2. Set up 3 cursors, initialized to the head of each list, one for traversing the result 3. Loop as long as at least one of the cursors is not null a1. check that our cursors aren't null a2. If we don't have a return value yet, set it to the smaller of the two & increment that cursor b. choose which element is <= the other as the next item in our result c. set the next pointer in our result cursor to this node 4. return the result head Run-time complexity: O(N) - N = size of both input lists; O(N + M) Space complexity: O(1) extra space Test cases 1. [], [] 2. [1], [] 3. [1, 2], [1] */ function mergeTwoLists1(list1: ListNode | null, list2: ListNode | null): ListNode | null { let result: ListNode | null // 1. null // 2. 1 -> null // 3. 1 -> 2 -> null let c1: ListNode | null = list1 // 1. null // 2. null // 3. 1 -> null let c2: ListNode | null = list2 let cr: ListNode | null if (!c1 && !c2) { // 1. null result = null } else if (!c1) { result = c2 c2 = c2.next } else if (!c2) { // 2. 1 -> null result = c1 // 2. null c1 = c1.next } else { if (c1.val <= c2.val) { // 3. 1 -> 2 -> null result = c1 // 3. 2 -> null c1 = c1.next } else { result = c2 c2 = c2.next } } // 1. null // 2. null // 3. 1 -> 2 -> null cr = result for (; c1 || c2;) { if (!c1) { cr.next = c2 c2 = c2.next } else if (!c2) { // 3.2 cr = 1 -> 2 -> null // 3.2 result = 1 -> 1 -> 2 -> null cr.next = c1 // 3.2 c1 = null c1 = c1.next } else if (c1.val <= c2.val) { cr.next = c1 c1 = c1.next } else { // 3.1 cr = 1 -> 1 -> null cr.next = c2 // 3.1 null c2 = c2.next } // 3.1 cr = 1 -> null // 3.2 cr = null cr = cr.next } // 1. null // 2. 1 -> null // 3. 1 -> 1 -> 2 -> null return result } // simplify: combine initialization and loop function mergeTwoLists2(list1: ListNode | null, list2: ListNode | null): ListNode | null { let result: ListNode | null = null let c1: ListNode | null = list1 let c2: ListNode | null = list2 let cr: ListNode | null = null let temp: ListNode | null for (; c1 || c2;) { if (!c1) { temp = c2 c2 = c2.next } else if (!c2) { temp = c1 c1 = c1.next } else if (c1.val <= c2.val) { temp = c1 c1 = c1.next } else { temp = c2 c2 = c2.next } if (!cr) { cr = temp result = cr } else { cr.next = temp cr = cr.next } } return result } // simplify: combine null and inequality checks function mergeTwoLists3(list1: ListNode | null, list2: ListNode | null): ListNode | null { let result: ListNode | null = null let c1: ListNode | null = list1 let c2: ListNode | null = list2 let cr: ListNode | null = null let temp: ListNode | null for (; c1 || c2;) { if ((c1?.val || Infinity) <= (c2?.val || Infinity)) { temp = c1 c1 = c1.next } else { temp = c2 c2 = c2.next } if (!cr) { cr = temp result = cr } else { cr.next = temp cr = cr.next } } return result } // simplify: constrain variable scope function mergeTwoLists4(list1: ListNode | null, list2: ListNode | null): ListNode | null { let result: ListNode | null = null for (let c1 = list1, c2 = list2, cr = result; c1 || c2;) { let temp: ListNode | null // BUG: val=0 => Infinity if ((c1?.val || Infinity) <= (c2?.val || Infinity)) { temp = c1 c1 = c1.next } else { temp = c2 c2 = c2.next } if (!cr) { cr = temp result = cr } else { cr.next = temp cr = cr.next } } return result } // simplify: extract helper const chooseNext = (a: ListNode | null, b: ListNode | null): ListNode | null => { if (!a) return b if (!b) return a return a.val <= b.val ? a : b } function mergeTwoLists5(list1: ListNode | null, list2: ListNode | null): ListNode | null { let result: ListNode | null = null for (let c1 = list1, c2 = list2, cr = result; c1 || c2;) { const temp = chooseNext(c1, c2) if (temp === c1) { c1 = c1.next } else { c2 = c2.next } if (!cr) { cr = temp result = cr } else { cr.next = temp cr = cr.next } } return result } // simplify: eliminate unnecessary temp variable; initialize result at declaration function mergeTwoLists6(list1: ListNode | null, list2: ListNode | null): ListNode | null { const result = chooseNext(list1, list2) for (let c1 = list1, c2 = list2, cr = result; c1 || c2;) { if (cr === c1) { c1 = c1.next } else { c2 = c2.next } cr.next = chooseNext(c1, c2) cr = cr.next } return result } // annotate with test cases function mergeTwoLists(list1: ListNode | null, list2: ListNode | null): ListNode | null { // 1. list1=[] list2=[] // 2. list1=[1] list2=[] // 3. list1=[] list2=[3] // 4. list1=[4,6] list2=[2,5] const result = chooseNext(list1, list2) // 1. result=[] // 2. result=[1] // 3. result=[3] // 4. result=[2,5] for (let c1 = list1, c2 = list2, cr = result; c1 || c2;) { // 2. c1=[1] c2=[] cr=[1] // 3. c1=[] c2=[3] cr=[3] // 4.1 c1=[4,6] c2=[2,5] cr=[2,5] if (cr === c1) { c1 = c1.next // 2. c1=[] // 4.2 c1=[6] // 4.4 c1=[] } else { c2 = c2.next // 3. c2=[] // 4.1 c2=[5] // 4.3 c2=[] } cr.next = chooseNext(c1, c2) // 2. cr=[1] result=[1] // 3. cr=[3] result=[3] // 4.1 cr=[2,4] result=[2,4] // 4.2 cr=[4,5] result=[2,4,5] // 4.3 cr=[5,6] result=[2,4,5,6] // 4.4 cr=[6] result=[2,4,5,6] cr = cr.next // 2. cr=[] // 3. cr=[] // 4.1 cr=[4,6] result=[2,4,6] // 4.2 cr=[5] result=[2,4,5] // 4.3 cr=[6] result=[2,4,5,6] // 4.4 cr=[] result=[2,4,5,6] } return result // 1. result=[] // 2. result=[1] // 3. result=[3] // 4. result=[2,4,5,6] } // we can eliminate the 3rd cursor but it's harder to follow and requires a new temp variable function mergeTwoListsAlt(list1: ListNode | null, list2: ListNode | null): ListNode | null { const result = chooseNext(list1, list2) for (let c1 = result, c2 = list1 === result ? list2 : list1; c2;) { if (chooseNext(c1.next, c2) === c1.next) { c1 = c1.next } else { const temp = c1.next c1.next = c2 c2 = temp } } return result }