算法刷题记一 (双指针)
文章目录
- 1、两数之和 II - 输入有序数组(7/21-167)
- 2、平方数之和(7/21-633)
- 3、反转字符串中的元音字母(7/21-345)
- 4、验证回文字符串 Ⅱ(7/21-680)
- 5、合并两个有序数组(7/27-88)
- 6、环形链表(7/27-141)
- 7、通过删除字母匹配到字典里最长单词(7/27-524)
leetcode官网:https://leetcode-cn.com/problems
CS-Notes:https://www.cyc2018.xyz/
1、两数之和 II - 输入有序数组(7/21-167)
解题思路:
使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。
如果两个指针指向元素的和 sum == target,那么得到要求的结果;
如果 sum > target,移动较大的元素,使 sum 变小一些;
如果 sum < target,移动较小的元素,使 sum 变大一些。
数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为 O(1)。
java代码实现:
class Solution {
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;
}
}
2、平方数之和(7/21-633)
解析:
可以看成是在元素为 0~target 的有序数组中查找两个数,使得这两个数的平方和为 target,如果能找到,则返回 true,表示 target 是两个整数的平方和。
本题和 167. Two Sum II - Input array is sorted 类似,只有一个明显区别:一个是和为 target,一个是平方和为 target。本题同样可以使用双指针得到两个数,使其平方和为 target。
本题的关键是右指针的初始化,实现剪枝,从而降低时间复杂度。设右指针为 x,左指针固定为 0,为了使 02 + x2 的值尽可能接近 target,我们可以将 x 取为 sqrt(target)。
因为最多只需要遍历一次 0~sqrt(target),所以时间复杂度为 O(sqrt(target))。又因为只使用了两个额外的变量,因此空间复杂度为 O(1)。
Java实现:
class Solution {
public boolean judgeSquareSum(int c) {
int left = 0, right = (int)Math.sqrt(c);
while(left <= right) {
int sum = left*left + right*right;
if(sum == c) {
return true;
}else if(sum < c) {
++left;
}else {
--right;
}
}
return false;
}
}
3、反转字符串中的元音字母(7/21-345)
解析:
使用双指针,一个指针从头向尾遍历,一个指针从尾到头遍历,当两个指针都遍历到元音字符时,交换这两个元音字符。
为了快速判断一个字符是不是元音字符,我们将全部元音字符添加到集合 HashSet 中,从而以 O(1) 的时间复杂度进行该操作。
时间复杂度为 O(N):只需要遍历所有元素一次
空间复杂度 O(1):只需要使用两个额外变量
Java实现:
class Solution {
//元音字母集合
private final static HashSet<Character> vowels = new HashSet<>( //创建HashSet集合对象
Arrays.asList('a','e','i','o','u','A','E','I','O','U')
);
public String reverseVowels(String s) {
if(s == null) return null;
int left = 0, right = s.length() -1;
char[] result = new char[s.length()];
while(left <= right) {
char cl = s.charAt(left); // charAt返回第i个位置的字符
char cr = s.charAt(right);
if(!vowels.contains(cl)) {
result[left++] = cl; //如果不包含就存如直接数组
} else if(!vowels.contains(cr)) {
result[right--] = cr; //如果不包含就存如直接数组
} else {
result[left++] = cr; //当左右两边都包含就先交换再存入数组
result[right--] = cl;
}
}
return new String(result);
}
}
4、验证回文字符串 Ⅱ(7/21-680)
解析:
所谓的回文字符串,是指具有左右对称特点的字符串,例如 “abcba” 就是一个回文字符串。
使用双指针可以很容易判断一个字符串是否是回文字符串:令一个指针从左到右遍历,一个指针从右到左遍历,这两个指针同时移动一个位置,每次都判断两个指针指向的字符是否相同,如果都相同,字符串才是具有左右对称性质的回文字符串。
本题的关键是处理删除一个字符。在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。
在判断是否为回文字符串时,我们不需要判断整个字符串,因为左指针左边和右指针右边的字符之前已经判断过具有对称性质,所以只需要判断中间的子字符串即可。
在试着删除字符时,我们既可以删除左指针指向的字符,也可以删除右指针指向的字符。
Java实现:
class Solution {
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;
}
}
5、合并两个有序数组(7/27-88)
解析:
需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。
Java实现:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i1 = m-1, i2 = n-1;
int i3 = m+n-1;
while(i2 >= 0){
if(i1 < 0){
nums1[i3--] = nums2[i2--];
}else if(i2 < 0){
nums1[i3--] = nums1[i1--];
}else if(nums1[i1] > nums2[i2]){
nums1[i3--] = nums1[i1--];
}else{
nums1[i3--] = nums2[i2--];
}
}
}
}
6、环形链表(7/27-141)
解析:
使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。
Java实现:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
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;
}
}
7、通过删除字母匹配到字典里最长单词(7/27-524)
解析:
通过删除字符串 s 中的一个字符能得到字符串 t,可以认为 t 是 s 的子序列,我们可以使用双指针来判断一个字符串是否为另一个字符串的子序列。
Java实现:
class Solution {
public String findLongestWord(String s, List<String> dictionary) {
String longsetWord = "";
for(String targer : dictionary){
int l1 = longsetWord.length(), l2 = targer.length();
if(l1 > l2 || (l1 == l2 && longsetWord.compareTo(targer) < 0)){ //longsetWord.compareTo(targer)如果此字符串小于字符串参数,则返回一个小于 0 的值;
continue;
}
if(isSubstr(s,targer)){
longsetWord = targer;
}
}
return longsetWord;
}
//判断两个字符串是否相等
private boolean isSubstr(String s, String targer) {
int i = 0, j = 0;
while(i < s.length() && j < targer.length()) {
if(s.charAt(i) == targer.charAt(j)) { // s.charAt(i) 表示:获取s中下标为i的字符
j++;
}
i++;
}
return j == targer.length();
}
}
还没有评论,来说两句吧...