力扣刷题总结之双指针

双指针思想

双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的 。

双指针是很多算法的基础,如归并排序、滑动窗口、字符匹配(左右括号匹配)等。滑动窗口即维持一段区间。

在很多情况下,双指针能帮助我们找到空间或是时间复杂度更低的解。比单个指针来回移动的效率从o(n2)提高到o(n)。

算法框架

for(int i = 0, j = x; i < n; i++)
{
    // step1: j范围有效 & 设计某个性质,使的j具有某种单调性
    while(j < m && check(i, j)) jxx

    // step2:这道题目的具体逻辑

    // step3: 这里注意i,j的更新!!
}

力扣双指针类型集合

可以参考滑动窗口类型集合题目一起学习,两者有重叠

1、合并两个有序链表

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == nullptr)
            return l2;
        if(l2 == nullptr)
            return l1;
        ListNode* pTemp = new ListNode(0);
        ListNode* pHead = pTemp;
        while(l1 != nullptr && l2 != nullptr)
        {
            if(l1->val > l2->val)
            {
                pTemp->next = l2;
                pTemp = l2; 
                l2 = l2->next;
            }
            else
            {
                pTemp->next = l1;
                pTemp = l1;
                l1 = l1->next; 
            }
        }
        if(l1 == nullptr)
            pTemp->next = l2;
        if(l2 == nullptr)
            pTemp->next = l1;
        
        return pHead->next;
    }
};

2、删除排序链表中的重复元素 --简单

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2
输出: 1->2

示例 2:

输入: 1->1->2->3->3
输出: 1->2->3
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(!head)
            return nullptr;
        
        ListNode* pre = head;
        ListNode* cur = head->next;
        while(cur)
        {
            if(cur->val != head->val)
            {
                head->next = cur;
                head = cur;
            }
            
            cur = cur->next;
        }
        
        if(head->next)
            head->next = nullptr;
        
        return pre;
    }
};

3、删除排序数组中的重复项 --简单

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.empty())
            return 0;
        int index = 0;
        int count = 1;
        for(int i = 1; i< nums.size();i++)
        {
            if(nums[index] != nums[i])
            {
                nums[++index] = nums[i];
                count++;
            }
        }
        nums.resize(count);
        return count;
    }
};

4、环形链表 --简单

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

img

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

img

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

img

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(!head)
            return false;
        ListNode* slow = head;
        ListNode* fast = head;

        while(slow && fast)
        {
            slow = slow->next;
            fast = fast->next;

            if(fast)
                fast = fast->next;
            else
                return false;
            
            if(fast == slow)
                return true;
        }

        return false;
    }

5、环形链表 ii --中等

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

**说明:**不允许修改给定的链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

img

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

img

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

img

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(!head)
            return nullptr;
        ListNode* slow = head;
        ListNode* fast = head;
        while(slow && fast)
        {
            slow = slow->next;
            fast = fast->next;

            if(fast)
                fast =fast->next;
            else
                break;
            
            if(fast == slow)
                break;
        }

        if(!fast)
            return nullptr;
        
        slow = head;

        while(slow && fast && slow != fast)
        {
            fast = fast->next;
            slow = slow->next;
        }

        return slow;
    }
};

6、移动0 --简单

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        if(nums.empty())
            return ;
        int index = 0;
        for(int i = 0; i< nums.size();i++)
        {
            if(nums[i] != 0)
            {
                nums[index++] = nums[i];
            }
        }
        for(;index < nums.size();index++)
            nums[index] = 0;
    }
};

7、合并两个有序数组 --简单

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

说明:

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]
class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
       for(int i=m,j=0;i<m+n ;i++)
           nums1[i] = nums2[j++];
        
        vector<int> vec(m + n,0);
        
        int l = 0;
        int r = m;
        int cnt = 0;
        while(l < m && r < m + n)
        {
            vec[cnt++] = nums1[l] < nums1[r] ? nums1[l++] : nums1[r++];
        }
        
        while(l < m)
            vec[cnt++] = nums1[l++];
        while(r < m + n)
            vec[cnt++] = nums1[r++];
        
        copy(vec.begin(),vec.end(),nums1.begin());
    }
};

8、三数之和 --中等

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]
class Solution {
public:
    vector<vector<int> > threeSum(vector<int>& nums) {
        vector<vector<int> > ret;
        vector<int > vtemp;
        int len = nums.size();
        sort(nums.begin(),nums.end());//sort the input
        for(int i=0;i<len-2;i++){
            //find the tripe for each nums[i]
            // j1 and j2 log the index of the other two numbers
            if(i ==0 ||(i>0 && nums[i] != nums[i-1])){
                int p1 = i+1, p2 = len-1; // set two pointers
                while(p1 < p2){
                    if(nums[p1] + nums[p2] < -nums[i]){
                        p1++;
                    }else if(nums[p1] + nums[p2] == -nums[i]){
                        if(p1 == i+1){
                            vector<int > vtemp{nums[i], nums[p1], nums[p2]};
                            ret.push_back(vtemp);
                            vtemp.clear();

                        }else if(nums[p1] != nums[p1-1]){
                            vector<int > vtemp{nums[i], nums[p1], nums[p2]};
                            ret.push_back(vtemp);
                            vtemp.clear();
                        }
                        p1++,p2--;
                    }else{
                         p2--;
                    }
                }
            }
        }
        return ret;
    }
};

9、盛水最多的容器 --困难

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

img

图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例:

输入:[1,8,6,2,5,4,8,3,7]
输出:49
class Solution {
public:
    int maxArea(vector<int>& height) {
        if(height.empty())
            return -1;
        int maxval = INT_MIN;
        int left = 0;
        int right = height.size() -1;
        int temp;
        while(left < right )
        {
            if(height[left] > height [right])
            {
                temp = height[right] * (right - left);
                right--;
            }
            else
            {
                temp = height[left] * (right - left);
                left++;
            }
            if(maxval < temp)
                maxval = temp;
        }
        return maxval;
    }
};

你可能感兴趣的:(力扣刷题总结之双指针)