算法技巧套路

双指针法

这是一种使用两个指针互相配合来存储节点以便于运算的技巧

适用于数组、链表等线性结构,常用思路有碰撞指针、滑动窗口、快慢指针。

对撞指针

leetcode第一题两数之和

class Solution {
    public int[] twoSum(int[] nums, int target) {
        //双指针法
        //采用对撞指针法,时间复杂度降低到 o(n)
        int len = nums.length;
        int left=0,right=len-1;

        int[] res=new int[2];
        int[] tmp1=new int[nums.length];
        System.arraycopy(nums,0,tmp1,0,nums.length);

        //将数组排序
        Arrays.sort(nums);
        //如何将排序后的数组下标和原有数组下标对应起来????

    
        int m=0,n=0;
        for(int i = left,j=right;i< j;){
            if(nums[i]+nums[j]==target){
                m = i;
                n = j;
                break;
            }else if(nums[i]+nums[j]>target){
                //右指针左移动
                j--;
            }else if(nums[i]+nums[j]<target){
                //左指针右移动
                i++;
            }
        }
        //两个数不能一样,只能用两个变量了
        int i,j;
        for ( i=0;i<nums.length;i++) {
            if(tmp1[i]==nums[m]){
                res[0] = i;
                break;
            }
        }
        for ( j=0;i<nums.length;j++) {
            // i!=j
            if(tmp1[j]==nums[n]&&i!=j){
                res[1] = j;
                break;
            }
        }
        return res;
    }

}
**这道题时间复杂度巨高!!!为什么呢???用到了系统的排序算法,这也暗示我们在使用双指针法的时候,数组一般要有序!**

主要学会这个思想!!!

leetcode881题救生艇

class Solution {
    public int numRescueBoats(int[] people, int limit) {
            //采用双指针法 对撞指针法
            
            //对数组进行排序
            Arrays.sort(people);
            //数组长度
            int len = people.length;
            //计数器及总和
            int count=0,sum=0;
            //长度为1时
            if(len==1){
                return 1;
            }
            //定义指针
            int i=0,j=len-1;
            while(i<=j){
         //题目已经说明了一首船最多在两个人!!!
             if(people[i]+people[j]<=limit){
                     i++;
                }
                j--;
                count++;
            }  
            return count;
    }
}

快慢指针

leetcode第141题 环形链表

/**
 * 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) {
        ListNode fast ;
        ListNode slow = fast = head;
        if(head==null){
            return false;
        }
        //while循环里面,快指针如果没有循环,那么链表就终止了
        while(fast!=null&&fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow==fast){
                return true;
            }
           
        }
        return false;
    }
}

二分查找法

此算法要求,数据结构是顺序存储且关键字有序

leetcode704题二分查找

class Solution {
    public int search(int[] nums, int target) {
        //暴力解
        // for(int i =0; i< nums.length; i++) {
        //     if(nums[i]==target){
        //         return i;
        //     }
            

        // }
        // return -1;

        // 二分查找法
        int left = 0, right = nums.length - 1;
        int mid = (left + right) / 2;
        while(left<=right){
            if(nums[mid]==target){
                return mid;
            }else if(nums[mid]>target){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
            mid = (left + right) / 2;
        } 
        return -1;
    }
}

leetcode 35 搜索插入位置

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while(left <= right) {
            int mid = (left + right) / 2;
            if(nums[mid] == target) {
                return mid;
            } else if(nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return right+1;
    }
}

leetcode162寻找峰值

这一题,我首先想到的是暴力解,讨论各种情况,成功提交!

class Solution {
    public int findPeakElement(int[] nums) {
        // 暴力解法
        // 先讨论数组长度《3的情况
        int len = nums.length;
        if(len == 1){
            return 0;
        }else if(len == 2){
            if(nums[0]>nums[1]){
                return 0;
            }
            else if(nums[0]<nums[1]){
                return 1;
            }
        }
        //讨论数组长度》=3
        //定义三个指针
        int left = 0;
        int mid =  1;
        int right = 2;
        // 讨论峰值在边界情况
        if(nums[0]>nums[1]){
            return 0;
        }
        if(nums[len-1]>nums[len-2]){
            return len -1;
        }
        // 讨论峰值在区间内
        while(right < len){
            if(nums[mid]>nums[left]&&nums[mid]>nums[right]){
                return mid;
            }
            left++;
            mid++;
            right++;
        }
    return 0;
    }
}
class Solution {
    public int findPeakElement(int[] nums) {

    // 二分法
    // 使用二分法关键是理解好核心逻辑,一般二分法要排序,但是这样排序后,索引就乱了
    // 重新审查题意,发现, nums[-1] = nums[n] = -∞
    int len = nums.length;
    int left = 0,right = len -1;
    int mid = (left + right) / 2;
    while(left<right){
        // 只要找到一个峰值就可以了!!!
        // 说明峰值在右侧
        if(nums[mid+1]>nums[mid]){
            //右边高,说明在mid右边有峰值,所以mid一定不是
            //mid已经不是了,排除掉
            left = mid + 1 ;
        }
        // 说明峰值在左侧
        else {
            // 左边高,说明左边有峰值,可能mid就是
         right = mid ;// mid在下一次查找中还要考虑在内
        }
        // 这里的代码很贼!,在找峰值的时候,右侧是left=mid+1,左侧是right=mid
        // 其实核心就是往右侧靠,因为最左侧,循环到底,相对于num[-1]也是右侧!!!
        mid = (left + right) / 2;
    }
    return left;   
	}
}

leetcode74 搜索二维矩阵

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        // 这个二维数组是有序的
        // 使用二分法
        // 获取获取m值,和n值
        int m = matrix.length;
        int n = matrix[0].length;
        // 防止溢出
        int left = 0,right = m*n-1; 
        int mid = left + (right - left) / 2;
        // 如何将二维数组的下标索引转换成一维数组索引
        while(left <= right){
            // 这是问题的核心所在,一维数组索引mid转换二维为 mid/n mid%n
            // 二维数组索引x y转换一维为 x*m+y
            int value = matrix[mid/n][mid%n];
            if(value==target){
                return true;
            }else if(value>target){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
            mid = left + (right - left) / 2;
        }
        return false;
    }
}

滑动窗口

数组中的定长问题,想到滑动窗口

leetcode209

class Solution {
            public int minSubArrayLen(int s, int[] nums) {
        //lo为队列头 li为队列尾,初始min值定义为数组长度+1
        int lo = 0, hi = 0, sum = 0, min = nums.length+1;
        while (hi < nums.length) {
            // 进队+,队尾指针+1,
            sum += nums[hi++];
            while (sum >= s) {
                // 达到条件,每次都寄存最小的min值
                min = Math.min(min, hi - lo);
                // 这是核心代码,出队-,并且队头指针+1
                sum -= nums[lo++];
            }
        }
        return min == nums.length+1 ? 0 : min;
    }
//用双指针来实现滑动窗口和队列进出得数据结构

}

leetcode 1456. 定长子串中元音的最大数目

class Solution {
    public int maxVowels(String s, int k) {
        // 数组中的定长问题 使用滑动窗口
        if(s == null || s.length()==0 || s.length() < k){
            return 0;
        }
        // 通过hashset查找更高效(相比较for遍历)
        HashSet<Character> set = new HashSet<>();
        set.add('a');
        set.add('e');
        set.add('i');
        set.add('o');
        set.add('u');

        // 定义零时存储结果集
        int res = 0, count = 0;
        for(int i = 0; i < k; i++){
            char temp = s.charAt(i);
            if(set.contains(temp)){
                count++;
            }
        }
        // 判断里面的最大数
        res = Math.max(res,count);
        for(int i = k; i < s.length(); i++){
            // 采取一进一出式的判断
            char out = s.charAt(i-k);
            char in = s.charAt(i);
            if(set.contains(out)){
                // 出去的值 --
                count--;
            }
            if(set.contains(in)){
                // 进来的值
                count++;
            }
            // 每次都要比较最大值
            res = Math.max(res,count);
        }
        return res;
    }
}

用一个新的函数,时间复杂度更低

class Solution {
    public int maxVowels(String s, int k) {
        int cnt = 0;
        for(int i = 0; i < k; i++){
            if(isVowel(s.charAt(i))){
                cnt++;
            }
        }
        int res = cnt;

        for(int i = k; i < s.length(); i++){
            if(isVowel(s.charAt(i - k))){
                cnt--;
            }
            if(isVowel(s.charAt(i))){
                cnt++;
                res = Math.max(res, cnt);
            }
        }
        return res;
    }

    private boolean isVowel(char ch) {
        return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u';
    }
}

加法模板

只要记住这个公式,不管两个数是列表形式,还是数组形式,都不会写错!

<公式>

当前位 = (A 的当前位 + B 的当前位 + 进位carry) % 10
注意,AB两数都加完后,最后判断一下进位 carry, 进位不为 0 的话加在前面。

<加法模板>

while ( A 没完 || B 没完)
    A 的当前位
    B 的当前位
和 = A 的当前位 + B 的当前位 + 进位carry

当前位 =% 10;
进位 =/ 10;

判断还有进位吗

989:数组形式的整数加法

 class Solution {
    public List<Integer> addToArrayForm(int[] A, int K) {
        int n = A.length;
        List<Integer> res = new ArrayList<>();  // 可以用 LinkeList,或者 ArrayList 往后加,最后反转
        int i = n - 1, sum = 0, carry = 0;
        while (i >= 0 || K != 0) {  // 循环条件:两个数有一个没完
            int x = i >= 0 ? A[i]: 0;
            int y = K != 0 ? K % 10 : 0;
        sum = x + y + carry;
        carry = sum / 10;
        K = K / 10;

        i--;
        //这里的0 代表的往前添加
        res.add(0, sum % 10);
    }
    if (carry != 0) res.add(0, carry);
    return res;
}

}
顺便附上字符串比较的模板。比如这道谷歌高频题:809. 情感丰富的文字
<比较模板>

while( A 没完 && B 没完)
A 的当前字符
B 的当前字符

A 的当前字符长度
B 的当前字符长度

判读符合比较条件吗

判断 A B 都走完了吗

参考例题

leetcode 989 号算法题:数组形式的整数加法

leetcode 66 号算法题:加 1

class Solution {
    public int[] plusOne(int[] digits) {
        // 又是一题使用加法魔板,但是这个加法魔板有点特殊,主要考察算法思想
        // 请不要把数组转换成数字+1,再转换成数组,也不要使用链表装换成数组
        // 直接再数组里面操作,然后返回!!!
    //     int len = digits.length,len2 = 1;
    //     LinkedList<Integer> res = new LinkedList<Integer>();
    //     int sum = 0, carry = 0;
    //     while( len>=0 ){
    //         int x  = len>=0 ? digits[len] : 0;
    //         int y  = len2>0 ? 1 : 0;
    //         sum = x + y + carry;
    //         carry = sum / 10;
    //         res.addFirst(sum % 10);
    //         len--;
    //         len2--;
    //     }
    //     if(carry!=0){
    //         res.addFirst(carry);
    //     }
    //     return (int[])res.toArray(new int[res.size()]);
    // }

    // 以上的代码不适用本题,但是思路很相似!
    //为何要在数组中直接操作???因为这是一个加1操作,没有任何的变数,直接操作简单的很!!!
    //还有就是对问题的分类意识,流程图意思。是解题的关键
    int len = digits.length;
    for(int i = len -1; i >= 0; i--){
        //末尾直接+1
        digits[i]++;
        //判断是否满10
        digits[i] = digits[i] % 10;
        // 如果不满10,直接返回
        if(digits[i] !=0)   return digits;
    }
    // 如果整个反向遍历都结束了,还没有返回,就要返回 1000000000类型的
    // 重新申请一个数组.默认是[0,0,0,0,0,0]类型
    int[]temp = new int[digits.length+1];
    temp[0] = 1;
    return temp;
	}
}

leetcode 415 号算法题:字符串相加
leetcode 67 号算法题:二进制求和
leetcode 2 号算法题:两数相加

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值