这次一口气放9道题~
LeetCode 23题,Hard!
归并K个有序链表。使用堆来做,一开始把K个链表的第一个元素放进数组,然后建堆。之后取出第一个元素(如果这个元素是nil元素,直接退出)放进归并后的链表,再从这个元素所在的链表取第一个元素放到原来元素的位子上,然后重新维护堆性质。如果那个链表已经没有元素,那就插入一个nil元素。
趁机用了用泛型方法,真好使!哨兵nil也不错,感谢算法导论!
public class WrappedListNode { public int value; public int listNo; public WrappedListNode(){} public WrappedListNode(int value, int listNo) { super(); this.value = value; this.listNo = listNo; } } public static int getLeft(int n){ return 2 * n + 1; } public static int getRight(int n){ return 2 * n + 2; } public static <T> void swap (int i, int j, T[] array) { T temp = array[i]; array[i] = array[j]; array[j] = temp; } public static void max_heapfy(int n, WrappedListNode[] array, int length){ int left = getLeft(n); int right = getRight(n); int largest = n; if (left <= length - 1 && array[left].value < array[n].value){ largest = left; } if (right <= length - 1 && array[right].value < array[largest].value){ largest = right; } if (largest != n){ swap(n, largest, array); max_heapfy(largest, array, length); } } public static void build_heap(WrappedListNode[] array, int length){ for (int i = length / 2 - 1; i >= 0; i--){ max_heapfy(i, array, length); } } public ListNode mergeKLists(List<ListNode> lists) { if (lists.size() == 0) return null; if (lists.size() == 1) return lists.get(0); int length = 0; WrappedListNode[] array = new WrappedListNode[lists.size()]; ListNode[] listNodes = new ListNode[lists.size()]; ListNode head = null, tail, tmp; WrappedListNode tmp1, nil = new WrappedListNode(Integer.MAX_VALUE, -1); for (int i = 0; i < lists.size(); i++){ if (lists.get(i) != null){ tmp = lists.get(i); WrappedListNode node = new WrappedListNode(tmp.val, i); array[length] = node; length++; lists.set(i, tmp.next); listNodes[i] = tmp; } } if (length == 0) return null; build_heap(array, length); head = tail = new ListNode(listNodes[array[0].listNo].val); for (; ;){ tmp1 = array[0]; if (tmp1.listNo != -1){ tail.next = listNodes[tmp1.listNo]; tail = tail.next; tmp = lists.get(tmp1.listNo); if (tmp != null){ WrappedListNode node = new WrappedListNode(tmp.val, tmp1.listNo); listNodes[tmp1.listNo] = tmp; array[0] = node; lists.set(tmp1.listNo, tmp.next); } else{ array[0] = nil; } max_heapfy(0, array, length); } else { return head.next; } } }
经验10:维护堆性质后不能减少length,因为不能保证length-1位的元素一定是nil
136题,Medium
找出一组数里那个单蹦的(其他都是一对)先放一个投机取巧法,十八岁以下严禁模仿!
public int singleNumber(int[] A) { BitSet bitSet = new BitSet(); for (int i = 0; i < A.length; i++) bitSet.flip(A[i] + 2000); for (int i = 0; i < A.length; i++) if (bitSet.get(A[i] + 2000)) return A[i]; return A[0]; }
正确做法:使用BitSet,正负分开统计
public int singleNumber(int[] A) { BitSet bitSetPositive = new BitSet(); BitSet bitSetNegative = new BitSet(); for (int i = 0; i < A.length; i++) if (A[i] >= 0) bitSetPositive.flip(A[i]); else bitSetNegative.flip(-A[i]); for (int i = 0; i < A.length; i++) if (A[i] >= 0){ if (bitSetPositive.get(A[i])) return A[i]; } else{ if (bitSetNegative.get(A[i])) return A[i]; } return A[0]; }
经验11:BitSet好东西,真心好!谁用谁知道!
找到一个字符串里最长的无重复字符的子串(leetcode第3题,medium)
O(N)的算法,时间最好280ms,大概前2%左右,这个算法已经不能更优化了。
大概的思路是:找到一对相同的之后,计算这一对的后面那个与j(上一对的前面那个+1)之间的距离。为了不单独处理所以给了j-1的初始值。(其实这个算法一开始有两个指针,后来我发现好像一个指针也能跑,但是两个到一个指针稍稍有点难说这个区别)
public int lengthOfLongestSubstring(String s) { Map<Character, Integer> map = new HashMap<>(); int length = s.length(), lenLS = 0, j = -1; for (int i = 0; i < length; i++){ Integer pos = map.get(s.charAt(i)); if (pos != null && pos >= j){ lenLS = lenLS >= i - 1 - j ? lenLS : i - 1 - j; j = pos; } map.put(s.charAt(i), i); } return lenLS < length - 1 - j ? length - 1 - j : lenLS; }
把输入的字符串排成Z字形输出(leetcode第6题,easy),没啥可优化的。
O(N)的算法,时间最好429ms,大概前5%左右。
public String convert(String s, int nRows) { if (nRows == 1 || s.length() < nRows) return s; StringBuilder[] sBuilders = new StringBuilder[nRows]; StringBuilder sResult = new StringBuilder(); int oneIteration = 2 * (nRows - 1); for (int i = 0; i < nRows; i++){ sBuilders[i] = new StringBuilder(); } for (int i = 0; i < s.length() / oneIteration; i++){ sBuilders[0].append(s.charAt(i * oneIteration)); for (int j = 1; j < nRows - 1; j++){ sBuilders[j].append(s.charAt(i * oneIteration + j)) .append(s.charAt((i + 1) * oneIteration - j)); } sBuilders[nRows - 1].append(s.charAt(i * oneIteration + nRows - 1)); } int remain = s.length() % oneIteration; if (remain <= nRows){ for (int i = 0; i < remain; i++){ sBuilders[i].append(s.charAt(s.length() / oneIteration * oneIteration + i)); } } if (remain > nRows){ for (int i = 0; i < nRows; i++){ sBuilders[i].append(s.charAt(s.length() / oneIteration * oneIteration + i)); } for (int i = 0; i < remain - nRows; i++){ sBuilders[nRows - 2 - i]. append(s.charAt(s.length() / oneIteration * oneIteration + nRows + i)); } } for (StringBuilder sTemp : sBuilders){ sResult.append(sTemp); } return sResult.toString(); }
求一个无符号数的二进制表示有多少个1.(leetcode第191题,easy)。最好时间
主要难点是位运算吧。而且Java直接给负数略坑啊
public int hammingWeight(int n) {
int sum = 0;
long nn = n & 0xffffffffL;
while (nn > 0)
{
sum += nn & 1;
nn = nn >> 1;
}
return sum;
}
经验12:取余n等于& (n-1),当n是2的幂的时候。
经验13:long做直接数运算时,直接数后面要加L表示这个数用LONG。
将数组向右旋转。(LeetCode第189题)
这个算法只使用O(1)的空间,原载于编程珠玑。我只能说,精彩而美妙!
public void rotate(int[] nums, int k) { int realK = k % nums.length; reverse(nums, 0, nums.length - realK - 1); reverse(nums, nums.length - realK, nums.length - 1); reverse(nums, 0, nums.length - 1); } public static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } public void reverse(int[] array, int begin, int end) { int i = begin, j = end; while (j > i){ swap(array, i, j); j--;i++; } }
把一个整数的二进制表示倒过来并输出这个新二进制对应的整数。(LeetCode190,easy)。最好成绩300ms,前30%。还是2的幂使用位操作可以大大简化
public int reverseBits(int n) { long sum = 0; long nn = n & 0xffffffffL; int digit = 0; while (nn > 0) { if ((nn & 1) == 1) sum += (1 << (31 - digit)); nn = nn >> 1; digit++; } return (int)sum; }
归并两个链表,leetcode 21,easy。太简单了没啥可说。。。。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode list = new ListNode(0); ListNode root = list; while (l1 != null && l2 != null){ if (l1.val > l2.val){ list.next = l2; l2 = l2.next; } else{ list.next = l1; l1 = l1.next; } list = list.next; } if (l1 == null){ list.next = l2; } else{ list.next = l1; } return root.next; }
在一个数组里寻找所有满足a+b+c=0的a,b,c。 LeetCode第15题,Medium
方法一:先用0做pivot,对所有的数进行一次partition,区分开正负数。然后对于每一个数,在符号相反的区域内调用two sum。这个算法可行,时间复杂度理论上是O(N^2),但是用Set之类的数据结构比较多,拖慢了整体的速度。最后无论怎么优化都没成功。
方法二:先对数组做快排,然后从最小的数开始用I,j做循环,在J后面用二分查找找第三个数
方法三:对方法二的优化,二分查找应该返回比要查找的数小的最大的数,这样之后的二分查找能够更快完成,我以后抽空优化~
/* Too Slow.... * Due to operations on Java Collection * * public List<List<Integer>> threeSum(int[] num) { List<List<Integer>> lists = new ArrayList<List<Integer>>(); if (num.length == 0){ return lists; } int border = partition(num); HashSet<Integer> set = new HashSet<Integer>(); HashSet<Integer> set2 = new HashSet<Integer>(); int zeros = 0; for (int i = 0; i < border; i++){ if (0 == num[i]) ++zeros; int positiveSum = 0 - num[i]; if (!set2.contains(positiveSum)){ twoSum(num, positiveSum, border, num.length, set, lists); set2.add(positiveSum); } } for (int i = border; i < num.length; i++){ if (0 == num[i]) ++zeros; int negativeSum = 0 - num[i]; if (!set2.contains(negativeSum)){ twoSum(num, negativeSum, 0, border, set, lists); set2.add(negativeSum); } } if (zeros >= 3){ List<Integer> list = new ArrayList<>(); list.add(0); list.add(0); list.add(0); lists.add(list); } return lists; } public void twoSum(int[] numbers, int target, int begin, int end, HashSet<Integer> set, List<List<Integer>> lists) { set.clear(); for (int i = begin; i < end; i++) { if (numbers[i] <= target - numbers[i] && set.contains(target - numbers[i])) { List<Integer> list = new ArrayList<>(); list.add(0 - target); list.add(numbers[i]); list.add(target - numbers[i]); lists.add(list); } set.add(numbers[i]); } } private int partition(int[] num){ int i = 0, j = num.length - 1; while (true){ while (i <= num.length - 1 && num[i] < 0) i++; while (j >= 0 && num[j] >= 0) j--; if (i < j){ int temp = num[i]; num[i] = num[j]; num[j] = temp; } else break; } return i == num.length? i - 1 : i; }*/ public List<List<Integer>> threeSum(int[] num) { List<List<Integer>> lists = new ArrayList<List<Integer>>(); if (num.length < 3){ return lists; } quickSort(num, 0, num.length - 1); int positiveStartAt = 0; for (; positiveStartAt < num.length; positiveStartAt++){ if (num[positiveStartAt] >= 0) break; } if (positiveStartAt == num.length){ return lists; } for (int i = 0; i < num.length && num[i] <= 0; i++){ for (int j = i + 1; j < num.length - 1; j++){ if (num[i] + num[j] > 0) break; if (binarySearch(num, Math.max(positiveStartAt - 1, j) + 1, num.length - 1, 0 - num[i] - num[j]) != -1){ List<Integer> list = new ArrayList<Integer>(); list.add(num[i]); list.add(num[j]); list.add(0 - num[i] - num[j]); lists.add(list); } while (j < num.length && num[j] == num[j + 1]){ j++; } } while (i < num.length && num[i] == num[i + 1]){ i++; } } return lists; } private int binarySearch(int[] num, int left, int right, int key){ if (left > right) return -1; int mid = (left + right) / 2; if (num[mid] > key){ return binarySearch(num, left, mid - 1, key); } else if (num[mid] < key){ return binarySearch(num, mid + 1, right, key); } else return mid; } private void swap(int[] num, int i, int j){ int temp = num[i]; num[i] = num[j]; num[j] = temp; } public static final Random RANDOM = new Random(); public static int getRandom(int min, int max){ return RANDOM.nextInt(max - min) + min; } private int partition(int[] num, int left, int right){ swap(num, right, getRandom(left, right)); int x = num[right]; int i = left - 1; for (int j = left; j < right; j++){ if (num[j] <= x){ i++; swap(num, i, j); } } swap(num, i + 1, right); return i + 1; } private void quickSort(int[] num, int left, int right){ if (left < right){ int q = partition(num, left, right); quickSort(num, left, q - 1); quickSort(num, q + 1, right); } }