LeetCode做题笔记之四

这次一口气放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];
}

经验11BitSet好东西,真心好!谁用谁知道!


找到一个字符串里最长的无重复字符的子串(leetcode3题,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字形输出(leetcode6题,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.leetcode191题,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),当n2的幂的时候。

经验13long做直接数运算时,直接数后面要加L表示这个数用LONG

 


将数组向右旋转。(LeetCode189题)

这个算法只使用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++;
    }
}



把一个整数的二进制表示倒过来并输出这个新二进制对应的整数。(LeetCode190easy)。最好成绩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 21easy。太简单了没啥可说。。。。

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=0a,b,c。 LeetCode第15题,Medium

  1. 方法一:先用0pivot,对所有的数进行一次partition,区分开正负数。然后对于每一个数,在符号相反的区域内调用two sum。这个算法可行,时间复杂度理论上是O(N^2),但是用Set之类的数据结构比较多,拖慢了整体的速度。最后无论怎么优化都没成功。

  2. 方法二:先对数组做快排,然后从最小的数开始用Ij做循环,在J后面用二分查找找第三个数

  3. 方法三:对方法二的优化,二分查找应该返回比要查找的数小的最大的数,这样之后的二分查找能够更快完成,我以后抽空优化~

/* 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);
    }
}



你可能感兴趣的:(java,LeetCode,算法)