九章算法笔记 6.链表与数组 Linked List & Array

刷题注意事项 cs3k.com

每道题需要总结的

  1. 思路
  2. 算法
  3. 核心代码
  4. 这个题得到的启示!!!重点是bug free的能力

linked list理解

enter image description here

结果两个都是 1 2 3

  1. node是存在main函数里的局部变量, 还是全局变量?

局部

  1. node1 是一个指针, 在32位即中占有4个字节. 也可以理解为引用, 存的是一个内存的地址. 所以一个ListNode占8个字节, val占4个, next占四个.

可以理解为, 内存是个大数组, ref和pointer都是数组的下标, 是index

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.

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的值

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; } };

猜你喜欢

转载自www.cnblogs.com/jiuzhangsuanfa/p/9895665.html