面试算法大全-双指针技巧总结

16、 双指针技巧

	双指针具体就是用两个变量动态存储两个或多个结点,来方便我们进行一些操作。通常用在线性的数据结构中,比如链表和数组。常用的双指针思想有:快慢指针、碰撞指针、滑动窗口等。
15.2.1 左右指针
 //nums 数组中查找target的位置
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 left;
    }

//反转数组
public int[] reverse(int[] nums){
    int left=0;
    int right=nums.length-1;
    while(left<right){
        int temp=nums[left];
        nums[left]=nums[right];
        nums[right]=temp;
        left++;
        right++;
    }
    return nums;
}
15.2.2 快慢指针
//移除元素,返回新数组长度
//给定 nums = [3,2,2,3], val = 3,
//函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
public int removeElement(int[] nums,int val){
    int fast=0;
    int slow=0;
    while(fast<nums.length){
        if(nums[fast]!=val){
            nums[slow]=nums[fast];
            slow++;
        }
       fast++;
    }
    return slow;
}

//双指针去重  
//给定 nums = [0,0,1,1,1,2,2,3,3,4],
//函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
int removeDuplicates(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    int slow = 0, fast = 0;
    while (fast < nums.length) {
        if (nums[fast] != nums[slow]) {
            slow++;
            // 维护 nums[0..slow] 无重复
            nums[slow] = nums[fast];
        }
        fast++;
    }
    // 数组长度为索引 + 1
    return slow + 1;
}
盛最多水的容器

暴力求解

public int maxArea(int[] height) {
    int maxarea = 0;
    int area = 0;
    int length = height.length;
    for (int i = 0; i < length - 1; i++) {
        for (int j = i + 1; j < length; j++) {
            area = Math.min(height[i], height[j]) * (j - i);
            maxarea = Math.max(maxarea, area);
        }
    }
    return maxarea;
}

最简形式

class Solution {
    public int maxArea(int[] height) {
        int i = 0, j = height.length - 1, res = 0;
        while(i < j){
            res = height[i] < height[j] ? Math.max(res, (j - i) * height[i++]): Math.max(res, (j - i) * height[j--]); 
        }
        return res;
    }
}

双指针求解

public int maxArea(int[] height) {
    int maxarea = 0, left = 0, length = height.length;
    int area;
    int right;
    //从前面开始找
    while (left < length) {
        right = length - 1;
        while (right > left) {
            if (height[right] < height[left]) {
                right--;
            } else {
                break;
            }
        }
        //计算矩形的面积
        area = height[left] * (right - left);
        //保存计算过的最大的面积
        maxarea = Math.max(maxarea, area);
        left++;
    }
    //从后面开始找,和上面类似
    right = length - 1;
    while (right > 0) {
        left = 0;
        while (right > left) {
            if (height[right] > height[left]) {
                left++;
            } else {
                break;
            }
        }
        area = height[right] * (right - left);
        maxarea = Math.max(maxarea, area);
        right--;
    }
    return maxarea;
}

双指针优化

public int maxArea(int[] height) {
    int maxarea = 0, left = 0, right = height.length - 1;
    int area = 0;
    while (left < right) {
        //计算面积,面积等于宽*高,宽就是left和right之间的距离,高就是
        //left和right所对应的最低高度
        area = Math.min(height[left], height[right]) * (right - left);
        //保存计算过的最大的面积
        maxarea = Math.max(maxarea, area);
        //柱子矮的往中间靠
        if (height[left] < height[right])
            left++;
        else
            right--;
    }
    return maxarea;
}
接雨水

三指针求解

public int trap(int[] height) {
    if (height.length <= 2)
        return 0;
    //找到最高的柱子的下标
    int max = Integer.MIN_VALUE;
    int maxIndex = -1;
    for (int i = 0; i < height.length; i++) {
        if (height[i] > max) {
            max = height[i];
            maxIndex = i;
        }
    }

    //统计最高柱子左边能接的雨水数量
    int left = height[0];
    int right = 0;
    int water = 0;
    for (int i = 1; i < maxIndex; i++) {
        right = height[i];
        if (right > left) {
            left = right;
        } else {
            water += left - right;
        }
    }

    //统计最高柱子右边能接的雨水数量
    right = height[height.length - 1];
    for (int i = height.length - 2; i > maxIndex; i--) {
        left = height[i];
        if (height[i] > right) {
            right = left;
        } else {
            water += right - left;
        }
    }

    //返回盛水量
    return water;
}

双指针求解

public int trap(int[] height) {
    if (height.length <= 2)
        return 0;
    int water = 0;
    int left = 0, right = height.length - 1;
    //最开始的时候确定left和right的边界,这里的left和right是
    //柱子的下标,不是柱子的高度
    while (left < right && height[left] <= height[left + 1])
        left++;
    while (left < right && height[right] <= height[right - 1])
        right--;

    while (left < right) {
        int leftValue = height[left];
        int rightValue = height[right];
        //在left和right两根柱子之间计算盛水量
        if (leftValue <= rightValue) {
            //如果左边柱子高度小于等于右边柱子的高度,根据木桶原理,
            // 桶的高度就是左边柱子的高度
            while (left < right && leftValue >= height[++left]) {
                water += leftValue - height[left];
            }
        } else {
            //如果左边柱子高度大于右边柱子的高度,根据木桶原理,
            // 桶的高度就是右边柱子的高度
            while (left < right && height[--right] <= rightValue) {
                water += rightValue - height[right];
            }
        }
    }
    return water;
}


//三个while循环合并
public int trap(int[] height) {
    int left = 0;
    int right = height.length - 1;
    int water = 0;
    int leftmax = 0;
    int rightmax = 0;
    while (left < right) {
        //确定左边的最高柱子
        leftmax = Math.max(leftmax, height[left]);
        //确定左边的最高柱子
        rightmax = Math.max(rightmax, height[right]);
        //那么桶的高度就是leftmax和rightmax中最小的那个
        if (leftmax < rightmax) {
            //桶的高度是leftmax
            water += (leftmax - height[left++]);
        } else {
            //桶的高度是rightmax
            water += (rightmax - height[right--]);
        }
    }
    return water;
}

双指针代码简化

public int trap(int[] height) {
    int left = 0, right = height.length - 1, water = 0, bucketHeight = 0;
    while (left < right) {
        //取height[left]和height[right]的最小值
        int minHeight = Math.min(height[left], height[right]);
        //如果最小值minHeight大于桶的高度bucketHeight,要更新桶的高度到minHeight
        bucketHeight = bucketHeight < minHeight ? minHeight : bucketHeight;
        water += height[left] >= height[right] ? (bucketHeight - height[right--]) : (bucketHeight - height[left++]);
    }
    return water;
}
无重复字符的最长子串

双指针求解

public int lengthOfLongestSubstring(String s) {
    if (s.length() == 0){
        return 0;
    }
    HashMap<Character, Integer> map = new HashMap<>();
    int max = 0;
    for (int i = 0, j = 0; i < s.length(); ++i) {
        //如果有重复的,就修改j的值
        if (map.containsKey(s.charAt(i))) {
            j = Math.max(j, map.get(s.charAt(i)) + 1);
        }
        map.put(s.charAt(i), i);
        //记录查找的最大值
        max = Math.max(max, i - j + 1);
    }
    //返回最大值
    return max;
}

使用队列求解

public int lengthOfLongestSubstring(String s) {
    //用链表实现队列,队列是先进先出的
    Queue<Character> queue = new LinkedList<>();
    int max = 0;
    for (char c : s.toCharArray()) {
        while (queue.contains(c)) {
            //如果有重复的,队头出队,这里通过while循环,
            //如果还有重复的就继续出队,直到队列中没有
            // 重复的元素为止
            queue.poll();
        }
        //添加到队尾
        queue.add(c);
        //记录下最大长度
        max = Math.max(max, queue.size());
    }
    return max;
}
旋转链表
public ListNode rotateRight(ListNode head, int k) {
    if (head == null)
        return head;
    ListNode fast = head, slow = head;
    //链表的长度
    int len = 1;
    //统计链表的长度,顺便找到链表的尾结点
    while (fast.next != null) {
        len++;
        fast = fast.next;
    }
    //首尾相连,先构成环
    fast.next = head;
    //慢指针移动的步数
    int step = len - k % len;
    //移动步数,这里大于1实际上是少移了一步
    while (step-- > 1) {
        slow = slow.next;
    }
    //temp就是需要返回的结点
    ListNode temp = slow.next;
    //因为链表是环形的,slow就相当于尾结点了,
    //直接让他的next等于空
    slow.next = null;
    return temp;
}
环形链表
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值