leetcode刷题 --算法思想

双指针

[167] 两数之和 II - 输入有序数组

Input: numbers = {2, 7, 11, 15}, target = 9
Output: index1 = 1, index2 = 2

我的弱智解法:全部遍历一遍

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int i, j;
        int array[] = new int [2];
        for(i = 0; i < numbers.length - 1; i++ ){
            for(j = i + 1;j < numbers.length;j++){
                if (numbers[i] + numbers[j] == target){
                    array[0] = i + 1;
                    array[1] = j + 1;
                }
            }
        }
        return array;
    }
}

优质解法:使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。

  • 如果两个指针指向元素的和 sum == target,那么得到要求的结果;
  • 如果 sum > target,移动较大的元素,使 sum 变小一些;
  • 如果 sum < target,移动较小的元素,使 sum 变大一些。

数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为 O(1)。

public int[] twoSum(int[] numbers, int target) {
    if (numbers == null) return null;
    int i = 0, j = numbers.length - 1;
    while (i < j) {
        int sum = numbers[i] + numbers[j];
        if (sum == target) {
            return new int[]{i + 1, j + 1};
        } else if (sum < target) {
            i++;
        } else {
            j--;
        }
    }
    return null;
}

[633]平方数之和

Input: 5
Output: True
Comment: 1 * 1 + 2 * 2 = 5

解题思路和上道题类似,为了降低时间复杂度,使用到了sqrt

public boolean judgeSquareSum(int target) {
	if (target < 0) return false;
	int i = 0, j = (int) Math.sqrt(target);
	while (i <= j) {
		int powSum = i * i + j * j;
		if (powSum == target) {
			return true;
		} else if (powSum > target) {
			j--;
		} else {
			i++;
		}
	}
	return false;
}

[345]反转字符串中的元音字母

Input: "leetcode"
Output: "leotcede"

解题思路:使用双指针,一个指针从头向尾遍历,一个指针从尾到头遍历,当两个指针都遍历到元音字符时,交换这两个元音字符。

class Solution {
	private final static HashSet<Character> vowels = new HashSet<>
	(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
	public String reverseVowels(String s) {
		if (s == null) return null;
		int i = 0, j = s.length() - 1;
		char[] result = new char[s.length()];
		while (i <= j) {
			char ci = s.charAt(i);
			char cj = s.charAt(j);
			if (!vowels.contains(ci)) {
				result[i++] = ci;
			} else if (!vowels.contains(cj)) {
				result[j--] = cj;
			} else {
				result[i++] = cj;
				result[j--] = ci;
			}
		}
		return new String(result);
	}
}

[680] 验证回文字符串 Ⅱ

Input: "abca"
Output: True
Comment: You could delete the character 'c'.

解题思路:在判断是否为回文字符串时,我们不需要判断整个字符串,因为左指针左边和右指针右边的字符之前已经判断过具有对称性质,所以只需要判断中间的子字符串即可。

在试着删除字符时,我们既可以删除左指针指向的字符,也可以删除右指针指向的字符。

public boolean validPalindrome(String s) {
	for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
		if (s.charAt(i) != s.charAt(j)) {
			return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j);
        }
    }
    return true;
}

private boolean isPalindrome(String s, int i, int j) {
    while (i < j) {
        if (s.charAt(i++) != s.charAt(j--)) {
            return false;
        }
    }
    return true;
}

[88] 合并两个有序数组

Input:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3
Output: [1,2,2,3,5,6]

解题思路:把归并结果存到第一个数组上。需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。

public void merge(int[] nums1, int m, int[] nums2, int n) {
    int index1 = m - 1, index2 = n - 1;
    int indexMerge = m + n - 1;
    while (index1 >= 0 || index2 >= 0) {
        if (index1 < 0) {
            nums1[indexMerge--] = nums2[index2--];
        } else if (index2 < 0) {
            nums1[indexMerge--] = nums1[index1--];
        } else if (nums1[index1] > nums2[index2]) {
            nums1[indexMerge--] = nums1[index1--];
        } else {
            nums1[indexMerge--] = nums2[index2--];
        }
    }
}

[141]环形链表
使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public boolean hasCycle(ListNode head) {
    if (head == null) {
        return false;
    }
    ListNode l1 = head, l2 = head.next;
    while (l1 != null && l2 != null && l2.next != null) {
        if (l1 == l2) {
            return true;
        }
        l1 = l1.next;
        l2 = l2.next.next;
    }
    return false;
}

[524]通过删除字母匹配到字典里最长单词

Input:	
s = "abpcplea", d = ["ale","apple","monkey","plea"]

Output: "apple"

解题思路:可以认为 target 是 s 的子序列,我们可以使用双指针来判断一个字符串是否为另一个字符串的子序列。

public String findLongestWord(String s, List<String> d) {
    String longestWord = "";
    for (String target : d) {
        int l1 = longestWord.length(), l2 = target.length();
        if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) {
            continue;
        }
        if (isSubstr(s, target)) {
            longestWord = target;
        }
    }
    return longestWord;
}

private boolean isSubstr(String s, String target) {
    int i = 0, j = 0;
    while (i < s.length() && j < target.length()) {
        if (s.charAt(i) == target.charAt(j)) {
            j++;
        }
        i++;
    }
    return j == target.length();
}

排序

你可能感兴趣的:(数据结构和算法和数据库和刷题)