九章算法筆記 6.鏈表與數組 Linked List & Array

刷題注意事項 cs3k.com

每道題須要總結的

  1. 思路
  2. 算法
  3. 核心代碼
  4. 這個題獲得的啓示!!!重點是bug free的能力

 

linked list理解

enter image description here

結果兩個都是 1 2 3java

  1. node是存在main函數裏的局部變量, 仍是全局變量?

局部node

  1. node1 是一個指針, 在32位即中佔有4個字節. 也能夠理解爲引用, 存的是一個內存的地址. 因此一個ListNode佔8個字節, val佔4個, next佔四個.

能夠理解爲, 內存是個大數組, ref和pointer都是數組的下標, 是indexc++

enter image description here

Reverse Nodes in k-Groups

Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.面試

If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.算法

You may not alter the values in the nodes, only nodes itself may be changed.shell

Only constant memory is allowed.數組

複雜問題的解決方案

cs3k.com

複雜的, 不能一眼看到結果的, 要拆分.框架

拆分就是要步驟化, 先框架, 再細節.dom

複雜的問題經過一個for循環, 一個while循環, 或者一個123步怎麼作, 變成一個更小的問題.wordpress

每一個function須要明確

dummy node

java要new, c++不要new,c++若是new了要刪除

HEAD = DUMMY 這句話老是須要麼?

幾乎

何時使用 DUMMY NODE?

鏈表結構發生變化的時候

DUMMY NODE 是否須要刪除?

不須要

使用 DUMMY NODE 算面試官會說我耗費了額外空間麼?

他沒那麼神精病, O(1)的額外空間不算額外空間,O(n)纔算

DUMMY NODE 非用不可麼?

不是, 可是用了代碼比較簡潔

DUMMY NODE 初始化的值重要麼?

不重要, 只須要它的next的值

enter image description here

enter image description here

Copy List with Random Pointer

cs3k.com

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.

相似clone graph, 其中clone graph的步驟是:

  1. 找全部的點
  2. 把全部的點複製一遍
  3. 把全部的邊複製一遍

hash map solution

連同老節點和新節點.

public class Solution { public RandomListNode copyRandomList(RandomListNode head) { if (head == null) { return null; } HashMap<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>(); RandomListNode dummy = new RandomListNode(0); RandomListNode pre = dummy, newNode; while (head != null) { if (map.containsKey(head)) { newNode = map.get(head); } else { newNode = new RandomListNode(head.label); map.put(head, newNode); } pre.next = newNode; if (head.random != null) { if (map.containsKey(head.random)) { newNode.random = map.get(head.random); } else { newNode.random = new RandomListNode(head.random.label); map.put(head.random, newNode.random); } } pre = newNode; head = head.next; } return dummy.next; } }

no extra space solution

神馬是extra space?

除了input 和output之外的額外空間叫extra space

  1. copy list 翻倍

1->2->3->4

=>

1->1’->2->2’->3->3’->4->4’->NULL

  1. copy random 連接

A.next.random = A.random.next

  1. 分開
/*第一遍掃的時候巧妙運用next指針, 開始數組是1->2->3->4 。 而後掃描過程當中 先創建copy節點 1->1`->2->2`->3->3`->4->4`, 而後第二遍copy的時候去創建邊的copy, 拆分節點, 一邊掃描一邊拆成兩個鏈表,這裏用到兩個dummy node。第一個鏈表變回 1->2->3 , 而後第二變成 1`->2`->3` */ //No HashMap version public class Solution { private void copyNext(RandomListNode head) { while (head != null) { RandomListNode newNode = new RandomListNode(head.label); newNode.random = head.random; newNode.next = head.next; head.next = newNode; head = head.next.next; } } private void copyRandom(RandomListNode head) { while (head != null) { if (head.next.random != null) { head.next.random = head.random.next; } head = head.next.next; } } private RandomListNode splitList(RandomListNode head) { RandomListNode newHead = head.next; while (head != null) { RandomListNode temp = head.next; head.next = temp.next; head = head.next; if (temp.next != null) { temp.next = temp.next.next; } } return newHead; } public RandomListNode copyRandomList(RandomListNode head) { if (head == null) { return null; } copyNext(head); copyRandom(head); return splitList(head); } } 

Linked List Cycle

cs3k.com

兩種問法

  1. 有環
  2. 環入口

快慢指針

enter image description here

小細節

  1. null pointer checking驗證xx.xxy必定要檢測不空
  2. 數組檢測是否越界,再取它的值
public class Solution { public Boolean hasCycle(ListNode head) { if (head == null || head.next == null) { return false; } ListNode fast, slow; fast = head.next; slow = head; while (fast != slow) { if(fast==null || fast.next==null) return false; fast = fast.next.next; slow = slow.next; } return true; } }

Linked List Cycle II

cs3k.com

 

個人理解:

1.環如圖,環外長度n用黃綠色表示,環長度m,用粉色表示如pic1。
1
2.由於s一次一步,f一次兩步,因此s每走一步,f都比s多走一步。因此當慢指針s走到n的時候,f在他前面n步,如pic2。
2
 
3.現階段f已經在s前面n步了。兩我的離口圈還差m-n步,m-n長度用藍色表示。若是f想追上s,須要比s多走m-n步。又由於f比s多走的步數和s走的同樣多(s走1步,f比他多走1步;s走2步,f比它多走2步;以此類推)。因此當s再走m-n步的時候,s和f相遇如pic3.
3
4.pic3的時候,是s從交點O走出來m-n步的時候,因此s想走到O,還須要m-(m-n)即n步。此時再放一個慢指針s2在起點,當s和s2都一次一步的走的時候,他們同事走完黃綠色的n的長度,在交點O相遇。Bingo
snipaste_20171017_114354

網上別人的偏數學的解釋,參考LeetCode Discuss(https://leetcode.com/discuss/16567/concise-solution-usi:

Linked List Cycle II

1). 使用快慢指針法,若鏈表中有環,能夠獲得兩指針的交點M

2). 記鏈表的頭節點爲H,環的起點爲E

2.1) L1爲H到E的距離
2.2) L2爲從E出發,首次到達M時的路程
2.3) C爲環的周長
2.4) n爲快慢指針首次相遇時,快指針在環中繞行的次數

根據L1,L2和C的定義,咱們能夠獲得:

慢指針行進的距離爲L1 + L2

快指針行進的距離爲L1 + L2 + n * C

因爲快慢指針行進的距離有2倍關係,所以:

2 * (L1+L2) = L1 + L2 + n * C => L1 + L2 = n * C => L1 = (n – 1)* C + (C – L2)

能夠推出H到E的距離 = 從M出發繞環到達E時的路程

所以,當快慢指針在環中相遇時,咱們再令一個慢指針從頭節點出發

接下來當兩個慢指針相遇時,即爲E所在的位置

實在不明白,就背下來吧。。。

snipaste_20171012_153807

Intersection of 2 linked list

enter image description here

L1走到頭,而後把尾巴接到L2上, 而後liked list cycle II

 

public class Solution { /** * @param headA: the first list * @param headB: the second list * @return: a ListNode */ public ListNode getIntersectionNode(ListNode headA, ListNode headB) { if (headA == null || headB == null) { return null; } // get the tail of list A. ListNode node = headA; while (node.next != null) { node = node.next; } node.next = headB; ListNode result = listCycleII(headA); node.next = null; return result; } private ListNode listCycleII(ListNode head) { ListNode slow = head, fast = head.next; while (slow != fast) { if (fast == null || fast.next == null) { return null; } slow = slow.next; fast = fast.next.next; } slow = head; fast = fast.next; while (slow != fast) { slow = slow.next; fast = fast.next; } return slow; }

Sort List

cs3k.com

Sort a linked list in O(n log n) time using constant space complexity.

空間複雜度相關

constant space

O(1) memory

no extra space

三者同樣

基於比較的排序,最優的時間複雜度也是nlogn

O(n) bucket sort

O(n * n^(1/2)) shell sort

O(nlogn) quick merge heap

radix sort 和counting sort(數數有幾個1 有幾個2 再用hash??)

這兩個是基於value的排序, 要求key可數, float double啥的就不行了

space complexity

quick: O(1)

merge: O(n) 必定要倒騰出來,再挪回去,因此須要其餘的n個位置

heap:O(1)/O(n) 取決於用不用priority queue

list vs array

list:靈活,可打亂,可重組

array:下標連續,整塊內存

ps:quick sort和merge sort都本身實現下

// version 1: Merge Sort public class Solution { private ListNode findMiddle(ListNode head) { ListNode slow = head, fast = head.next; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; } return slow; } private ListNode merge(ListNode head1, ListNode head2) { ListNode dummy = new ListNode(0); ListNode tail = dummy; while (head1 != null && head2 != null) { if (head1.val < head2.val) { tail.next = head1; head1 = head1.next; } else { tail.next = head2; head2 = head2.next; } tail = tail.next; } if (head1 != null) { tail.next = head1; } else { tail.next = head2; } return dummy.next; } public ListNode sortList(ListNode head) { if (head == null || head.next == null) { return head; } ListNode mid = findMiddle(head); ListNode right = sortList(mid.next); mid.next = null; ListNode left = sortList(head); return merge(left, right); } } // version 2: Quick Sort 1 public class Solution { public ListNode sortList(ListNode head) { if (head == null || head.next == null) { return head; } ListNode mid = findMedian(head); // O(n) ListNode leftDummy = new ListNode(0), leftTail = leftDummy; ListNode rightDummy = new ListNode(0), rightTail = rightDummy; ListNode middleDummy = new ListNode(0), middleTail = middleDummy; while (head != null) { if (head.val < mid.val) { leftTail.next = head; leftTail = head; } else if (head.val > mid.val) { rightTail.next = head; rightTail = head; } else { middleTail.next = head; middleTail = head; } head = head.next; } leftTail.next = null; middleTail.next = null; rightTail.next = null; ListNode left = sortList(leftDummy.next); ListNode right = sortList(rightDummy.next); return concat(left, middleDummy.next, right); } private ListNode findMedian(ListNode head) { ListNode slow = head, fast = head.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; } private ListNode concat(ListNode left, ListNode middle, ListNode right) { ListNode dummy = new ListNode(0), tail = dummy; tail.next = left; tail = getTail(tail); tail.next = middle; tail = getTail(tail); tail.next = right; tail = getTail(tail); return dummy.next; } private ListNode getTail(ListNode head) { if (head == null) { return null; } while (head.next != null) { head = head.next; } return head; } } // version 3: Quick Sort 2 /** * Definition for ListNode. * public class ListNode { * int val; * ListNode next; * ListNode(int val) { * this.val = val; * this.next = null; * } * } */ class Pair { public ListNode first, second; public Pair(ListNode first, ListNode second) { this.first = first; this.second = second; } } public class Solution { /** * @param head: The head of linked list. * @return: You should return the head of the sorted linked list, using constant space complexity. */ public ListNode sortList(ListNode head) { if (head == null || head.next == null) { return head; } ListNode mid = findMedian(head); // O(n) Pair pair = partition(head, mid.val); // O(n) ListNode left = sortList(pair.first); ListNode right = sortList(pair.second); getTail(left).next = right; // O(n) return left; } // 1->2->3 return 2 // 1->2 return 1 private ListNode findMedian(ListNode head) { ListNode slow = head, fast = head.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; } // < value in the left, > value in the right private Pair partition(ListNode head, int value) { ListNode leftDummy = new ListNode(0), leftTail = leftDummy; ListNode rightDummy = new ListNode(0), rightTail = rightDummy; ListNode equalDummy = new ListNode(0), equalTail = equalDummy; while (head != null) { if (head.val < value) { leftTail.next = head; leftTail = head; } else if (head.val > value) { rightTail.next = head; rightTail = head; } else { equalTail.next = head; equalTail = head; } head = head.next; } leftTail.next = null; rightTail.next = null; equalTail.next = null; if (leftDummy.next == null && rightDummy.next == null) { ListNode mid = findMedian(equalDummy.next); leftDummy.next = equalDummy.next; rightDummy.next = mid.next; mid.next = null; } else if (leftDummy.next == null) { leftTail.next = equalDummy.next; } else { rightTail.next = equalDummy.next; } return new Pair(leftDummy.next, rightDummy.next); } private ListNode getTail(ListNode head) { if (head == null) { return null; } while (head.next != null) { head = head.next; } return head; } }

Merge Sorted Array

cs3k.com

Given two sorted integer arrays A and B, merge B into A as one sorted array.

從後往前排

class Solution {
    /** * @param A: sorted integer array A which has m elements, * but size of A is m+n * @param B: sorted integer array B which has n elements * @return: void */ public void mergeSortedArray(int[] A, int m, int[] B, int n) { int i = m-1, j = n-1, index = m + n - 1; while (i >= 0 && j >= 0) { if (A[i] > B[j]) { A[index--] = A[i--]; } else { A[index--] = B[j--]; } } while (i >= 0) { A[index--] = A[i--]; } while (j >= 0) { A[index--] = B[j--]; } } }

merge two sorted arrays

每次比較, 誰小誰出列的反向, 誰大誰出列

class Solution {
    /** * @param A and B: sorted integer array A and B. * @return: A new sorted integer array */ public int[] mergeSortedArray(int[] A, int[] B) { if (A == null || B == null) { return null; } int[] result = new int[A.length + B.length]; int i = 0, j = 0, index = 0; while (i < A.length && j < B.length) { if (A[i] < B[j]) { result[index++] = A[i++]; } else { result[index++] = B[j++]; } } while (i < A.length) { result[index++] = A[i++]; } while (j < B.length) { result[index++] = B[j++]; } return result; } }

Intersection of Two Arrays

cs3k.com

Given two arrays, write a function to compute their intersection.

enter image description here

1.哈希表 小的塞哈希表

2.小的出去

相同的都出去, 加到新的隊列裏

3.排小的

for打的的每一個數, 二分查n

// version 1: sort & merge public class Solution { /** * @param nums1 an integer array * @param nums2 an integer array * @return an integer array */ public int[] intersection(int[] nums1, int[] nums2) { Arrays.sort(nums1); Arrays.sort(nums2); int i = 0, j = 0; int[] temp = new int[nums1.length]; int index = 0; while (i < nums1.length && j < nums2.length) { if (nums1[i] == nums2[j]) { if (index == 0 || temp[index - 1] != nums1[i]) { temp[index++] = nums1[i]; } i++; j++; } else if (nums1[i] < nums2[j]) { i++; } else { j++; } } int[] result = new int[index]; for (int k = 0; k < index; k++) { result[k] = temp[k]; } return result; } } // version 2: hash map public class Solution { /** * @param nums1 an integer array * @param nums2 an integer array * @return an integer array */ public int[] intersection(int[] nums1, int[] nums2) { if (nums1 == null || nums2 == null) { return null; } HashSet hash = new HashSet<>(); for (int i = 0; i < nums1.length; i++) { hash.add(nums1[i]); } HashSet resultHash = new HashSet<>(); for (int i = 0; i < nums2.length; i++) { if (hash.contains(nums2[i]) && !resultHash.contains(nums2[i])) { resultHash.add(nums2[i]); } } int size = resultHash.size(); int[] result = new int[size]; int index = 0; for (Integer num : resultHash) { result[index++] = num; } return result; } } // version 3: sort & binary search public class Solution { /** * @param nums1 an integer array * @param nums2 an integer array * @return an integer array */ public int[] intersection(int[] nums1, int[] nums2) { if (nums1 == null || nums2 == null) { return null; } HashSet set = new HashSet<>(); Arrays.sort(nums1); for (int i = 0; i < nums2.length; i++) { if (set.contains(nums2[i])) { continue; } if (binarySearch(nums1, nums2[i])) { set.add(nums2[i]); } } int[] result = new int[set.size()]; int index = 0; for (Integer num : set) { result[index++] = num; } return result; } private boolean binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return false; } int start = 0, end = nums.length - 1; while (start + 1 < end) { int mid = (end - start) / 2 + start; if (nums[mid] == target) { return true; } if (nums[mid] < target) { start = mid; } else { end = mid; } } if (nums[start] == target) { return true; } if (nums[end] == target) { return true; } return false; } }

Median of two Sorted Arrays

There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays.

聯想到quick select那道題, 其中k = median

就merge k= (n+m/2)次, 可是不夠快

若是O(k)不夠快,只能往logn方向走

可是兩個數組的二分, 很差分, 因此想到縮小一倍問題的size

因此在一個數組裏刪掉2/k個數

終止的三個條件

cs3k.com

A沒了

B沒了

k= 1

public class Solution { public double findMedianSortedArrays(int A[], int B[]) { int len = A.length + B.length; if (len % 2 == 1) { return findKth(A, 0, B, 0, len / 2 + 1); } return ( findKth(A, 0, B, 0, len / 2) + findKth(A, 0, B, 0, len / 2 + 1) ) / 2.0; } // find kth number of two sorted array public static int findKth(int[] A, int A_start, int[] B, int B_start, int k){ if (A_start >= A.length) { return B[B_start + k - 1]; } if (B_start >= B.length) { return A[A_start + k - 1]; } if (k == 1) { return Math.min(A[A_start], B[B_start]); } int A_key = A_start + k / 2 - 1 < A.length ? A[A_start + k / 2 - 1] : Integer.MAX_VALUE; int B_key = B_start + k / 2 - 1 < B.length ? B[B_start + k / 2 - 1] : Integer.MAX_VALUE; if (A_key < B_key) { return findKth(A, A_start + k / 2, B, B_start, k - k / 2); } else { return findKth(A, A_start, B, B_start + k / 2, k - k / 2); } } }

Maximum Subarray

cs3k.com

Given an array of integers, find a contiguous subarray which has the largest sum.

求數組中間的一段, 就長減短

PrefixSum[i] = A[0] + A[1] + … A[i – 1], PrefixSum[0] = 0

易知構造 PrefixSum 耗費 O(n) 時間和 O(n) 空間

如需計算子數組從下標i到下標j之間的全部數之和

則有 Sum(i~j) = PrefixSum[j + 1] – PrefixSum[i]

相似的closest就拍個序

// Version 1: Greedy public class Solution { public int maxSubArray(int[] A) { if (A == null || A.length == 0){ return 0; } int max = Integer.MIN_VALUE, sum = 0; for (int i = 0; i < A.length; i++) { sum += A[i]; max = Math.max(max, sum); sum = Math.max(sum, 0); } return max; } } // Version 2: Prefix Sum public class Solution { public int maxSubArray(int[] A) { if (A == null || A.length == 0){ return 0; } int max = Integer.MIN_VALUE, sum = 0, minSum = 0; for (int i = 0; i < A.length; i++) { sum += A[i]; max = Math.max(max, sum - minSum); minSum = Math.min(minSum, sum); } return max; } } public class Solution { /** * @param nums: a list of integers * @return: A integer indicate the sum of minimum subarray */ public int maxSubArray(ArrayList nums) { // write your code if(nums.size()==0) return 0; int n = nums.size(); int []global = new int[n]; int []local = new int[n]; global[0] = nums.get(0); local[0] = nums.get(0); for(int i=1;i<n;i++) { local[i] = Math.max(nums.get(i),local[i-1]+nums.get(i)); global[i] = Math.max(local[i],global[i-1]); } return global[n-1]; } }

Subarray Sum Closest

cs3k.com

Given an integer array, find a subarray with sum closest to zero. Return the indexes of the first number and last number.

class Solution { public: /** * @param nums: A list of integers * @return: A list of integers includes the index of the first number * and the index of the last number */ struct node { //成員,生成,比較 node(int _value, int _pos):value(_value), pos(_pos) {} int value, pos; //重點有二,一個是&,一個是const bool operator<(const node &o) const{ return (value < o.value || value == o.value && pos < o.pos); } }; vector<int> subarraySumClosest(vector<int> nums){ // write your code here vector<node> s; vector<int> results(2); s.push_back(node(0,-1)); int sum = 0, len = nums.size(); for (int i = 0; i < len ; ++i) { sum += nums[i]; s.push_back(node(sum, i)); } sort(s.begin(), s.end()); len = s.size(); int ans = 0x7fffffff; for (int i = 0; i < len-1; ++i) if (abs(s[i+1].value - s[i].value) < ans) { ans = abs(s[i+1].value - s[i].value); results[0] = min(s[i].pos, s[i+1].pos)+1; results[1] = max(s[i].pos, s[i+1].pos); } return results; } };
相關文章
相關標籤/搜索