刷题注意事项 cs3k.com
每道题需要总结的
- 思路
- 算法
- 核心代码
- 这个题得到的启示!!!重点是bug free的能力
linked list理解
结果两个都是 1 2 3
- node是存在main函数里的局部变量, 还是全局变量?
局部
- node1 是一个指针, 在32位即中占有4个字节. 也可以理解为引用, 存的是一个内存的地址. 所以一个ListNode占8个字节, val占4个, next占四个.
可以理解为, 内存是个大数组, ref和pointer都是数组的下标, 是index
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.
Only constant memory is allowed.
复杂问题的解决方案
cs3k.com
复杂的, 不能一眼看到结果的, 要拆分.
拆分就是要步骤化, 先框架, 再细节.
复杂的问题通过一个for循环, 一个while循环, 或者一个123步怎么做, 变成一个更小的问题.
每个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的值
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的步骤是:
- 找所有的点
- 把所有的点复制一遍
- 把所有的边复制一遍
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
- copy list 翻倍
1->2->3->4
=>
1->1’->2->2’->3->3’->4->4’->NULL
- copy random 链接
A.next.random = A.random.next
- 分开
/*第一遍扫的时候巧妙运用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
两种问法
- 有环
- 环入口
快慢指针
小细节
- null pointer checking验证xx.xxy一定要检测不空
- 数组检测是否越界,再取它的值
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](https://i-blog.csdnimg.cn/blog_migrate/749f0032c10e00fc05985050307534e6.png)
![2](https://i-blog.csdnimg.cn/blog_migrate/67f2d25ea869ac7b2c8f272634ed5d65.png)
![3](https://i-blog.csdnimg.cn/blog_migrate/7e233ab113f4fab839019aa47beb0345.png)
![snipaste_20171017_114354](https://i-blog.csdnimg.cn/blog_migrate/ef156f1df838a191b28d70b53c8c9fc0.png)
网上别人的偏数学的解释,参考LeetCode Discuss(https://leetcode.com/discuss/16567/concise-solution-usi:
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所在的位置
实在不明白,就背下来吧。。。
Intersection of 2 linked list
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.
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