这次一口气放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);
}
}原文地址:http://08310302.blog.51cto.com/7006444/1627581