相向双指针-习题

1. 概括

在一串有序数列中,两根指针一头一尾,向中间靠拢直到相遇,时间复杂度为O(n),

类型题有Two Sum, Partition等

2. Two Sum类型

LintCode 607 · 两数之和 III-数据结构设计

要点:需要考虑重复数字,数据本身是无序的

方案一:使用list存放数字。

add()方法:用到插入排序,将新数字插入到list的末尾,不断与前面有序的数组元素依次进行比较,如果没有在正确的位置交换两元素,继续下一轮比较,如果在正确的位置则结束。时间复杂度O(n), 空间复杂度O(n)

find()方法:对有序list双指针。时间复杂度O(n)

方法二:使用HashMap存放数字,及其出现的频次

add()方法:直接加入哈希表中,注意更新数字出现的频次。时间复杂度O(1)

find()方法:遍历HashMap里的每个num,检查target - num是否也在HashMap中。            时间复杂度O(n)

LeetCode 15. 三数之和

要点:首先需要对输入数组nums排序,经典two sum需要在有序列中进行。                           要求中不可包含重复的三元组,一定要去重。

解法:选定一个点a,另外两个点的目标和为-a,转化为two sum问题。时间复杂度O(n^2)

去重:

// 外层数字循环,去重
// 前提条件:相同的数字必须相邻
if(index > 0 && nums[index-1] == nums[index]){
    continue;
}
// 内层双指针去重
if(nums[left] + nums[right] == target){
    ......
    ......
    // 如果左指针当前数字与左侧的数字相同,左指针右移,跳过重复
    while(left < right && nums[left] == nums[left-1]){
        left++;
    }
    // 如果右指针当前数字与右侧的数字相同,右指针左移,跳过重复
    while(left < right && nums[right] == nums[right+1]){
        right--;
    }
}

LeetCode 611. 有效三角形的个数

要点:两小边之和大于最大边;要进行two sum需要先对数组排序;无需去掉重复答案

解法:固定最大边,对之前的子数组进行two sum操作,因为只需求解的个数,因此无需一一枚举,可以批量求解的个数。

时间复杂度O(n^2),空间复杂度O(1)

LeetCode 18. 四数之和

解法:类似于三数之和的做法,双重循环固定两个数,然后做two sum。要记得去重。

时间复杂度O(n^3),空间复杂度O(k) k为解的个数

LeetCode 454. 四数相加 II

解法:用到了hashmap,将 a,b 组成的和及其组成方案个数统计在hash里,然后再去枚举 c,d 的组合,然后找 -(c+d) 在 hash 里的组合数。

时间复杂度O(n^2)

3. Partition类型

1)经典分区算法

给定一个整数数组nums和一个整数k,将所有小于k的元素移到左边,所有大于等于k的元素移到右边。时间复杂度 O(n),代码示例:

while(left <= right){
    // 左指针寻找一个不属于左边的数
    while(left <= right && nums[left] < k){
        left++;
    }
    // 右指针寻找一个不属于右边的数
    while(left <= right && nums[right] >= k){
        right--;
    }
    if(left <= right){
        // 交换左右指针数字,使得双方都到正确的一端
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

LintCode 144 · 交错正负数

要点:数据确保正负数个数相差不超过1;不使用额外空间;不需要保持正整数或负整数原来的顺序

思路:先用O(n)的时间将正整数和负整数分成左右两区,然后间隔交换正负数。

时间复杂度O(n)

注:1)要根据正负数个数的情况,设定两边交换的起点

LeetCode 75. 颜色分类 (3种颜色)

要点:array + 固定有限种元素排序 ——> 有限次快排分区

思路:可以用同向双指针做两次分区,注意墙的初始位置是-1,即 int lastSmallPointer = -1;

 每当遇到nums[i] < pivot的情况,先右移“墙”指针,再交换元素,即 nums[i] <=> nums[lastSmallPointer]

时间复杂度 O(n)

LintCode 143 · 颜色分类 II (k种颜色)

 思路:类似于快速排序的思想,按颜色个数k对数组分区,递归的程序设计方式

时间复杂度 O(nlogk)

LeetCode 283. 移动零

要点:在原数组上操作;最小化操作数

思路:使用同向双指针,填充指针fillPointer指向将被非零数填充的位置,移动指针movePointer不断前移,直到指向一个非零数,把该数赋值给填充指针指向的元素,并左移一位fillPointer。当movePointer移动结束后,将fillPointer右侧的非零数赋值为0

时间复杂度O(n)

4. 拓展

1) List 与 Arraylist

2)heap

3)HashMap底层实现 getOrDefault()方法

4)链表

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值