算法学习——LeetCode力扣双指针篇

算法学习——LeetCode力扣双指针篇1

算法学习——LeetCode力扣双指针篇_第1张图片

27. 移除元素

27. 移除元素 - 力扣(LeetCode)

描述

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

示例

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

代码解析

#include 
#include 
using namespace std;

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {

        int fastIndex = 0, slowIndex = 0;
        for (fastIndex = 0; fastIndex < nums.size(); fastIndex++)
        {
            if (nums[fastIndex] != val)
            {
                nums[slowIndex] = nums[fastIndex];
                slowIndex++;
            }
        }

        return slowIndex;
    }
};



int main()
{
    vector<int> my_nums = { 0,1,2,2,3,0,4,2 };

    int my_val= 2;

    Solution a;

    cout << a.removeElement(my_nums, my_val) << ", nums = [";
    for (int i=0; i < a.removeElement(my_nums,my_val); i++)
    {
        cout << my_nums[i] << ' ';
    }
    cout <<']'<< endl;
        
	return 0;

}


344. 反转字符串

344. 反转字符串 - 力扣(LeetCode)

描述

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例

示例 1:

输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]

示例 2:

输入:s = [“H”,“a”,“n”,“n”,“a”,“h”]
输出:[“h”,“a”,“n”,“n”,“a”,“H”]

提示

  • 1 <= s.length <= 105
  • s[i] 都是 ASCII 码表中的可打印字符

代码解析

class Solution {
public:
    void reverseString(vector<char>& s) {

        char temp;
        
        for(int i=0 ;i<s.size()/2;i++)
        {
            temp = s[i];
            s[i] = s[s.size()-1 - i];
            s[s.size()-1 - i] = temp;
        }
    }
};


151. 反转字符串中的单词

151. 反转字符串中的单词 - 力扣(LeetCode)

描述

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

**注意:**输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例

示例 1:

输入:s = “the sky is blue”
输出:“blue is sky the”

示例 2:

输入:s = " hello world "
输出:“world hello”
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

输入:s = “a good example”
输出:“example good a”
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

提示

  • 1 <= s.length <= 104
  • s 包含英文大小写字母、数字和空格 ’ ’
  • s 中 至少存在一个 单词

进阶

如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。

代码解析

双指针
class Solution {
public:
    string reverseWords(string s) {

       int left=0 ,right=1;
        string result , temp;

        if(s.size()==1) return s;//针对单一字母
        
        while( left < right && right<s.size() )//找到左指针位置,单词第一个字母处
        {
            while(s[left]==' ')
            {
                left++;
                right++;
                if(right > s.size()) %应对单词后面多个空格没有新单词,类似“abc        ”
                {
                     result.erase(result.size()-1,1);
                     return result;
                }
            }
            while(s[right]!=' '&& s[right]!='\0')//确定右指针位置,在单词最后一个字母后
            {
                right++;
            }
            temp = s.substr(left,right-left);//抽取子串
            temp +=' ';//添加空格
            result.insert(0, temp);//头插字串

            left = right;
            right= left+1;
        }
        result.erase(result.size()-1,1);//去除最后一个空格
        return result;
    }
};


206. 反转链表

206. 反转链表 - 力扣(LeetCode)

描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示

链表中节点的数目范围是 [0, 5000]
-5000 <= Node.val <= 5000

进阶

链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

代码解析

双指针法
class Solution {
public:
    ListNode* reverseList(ListNode* head) {

        ListNode *cur ;
        ListNode* temp1, * temp2 ;

        cur = head;
        temp1 = nullptr;
       
        while (cur)
        {
            temp2 = cur->next;
            cur->next = temp1;

            temp1 = cur;
            cur = temp2;
        }
        return temp1;
    }
};


19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

描述

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

进阶:你能尝试使用一趟扫描实现吗?

代码解析

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        int sum_node = 0;
        ListNode *real_head = new ListNode(0,head);
        ListNode *tmp = real_head;
        
        while(tmp != nullptr)
        {
            sum_node++;
            tmp = tmp->next;
        }
       
        sum_node = sum_node - n -1;
        tmp = real_head;
        while(sum_node--) tmp = tmp->next;

        if( tmp->next != nullptr && tmp->next->next != nullptr)
        {
            tmp->next = tmp->next->next;
        }else tmp->next = nullptr;

        return real_head->next;
    }
};


面试题 02.07. 链表相交

面试题 02.07. 链表相交 - 力扣(LeetCode)

描述

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 0 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listA 和 listB 没有交点,intersectVal 为 0
  • 如果 listA 和 listB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]

进阶

你能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?

代码解析

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int length_A = 0 , length_B = 0 ,diff=0 ;
        ListNode *cur_A = headA , *cur_B = headB;
        ListNode *dummy_a = new ListNode(0);
        ListNode *dummy_b = new ListNode(0);

        dummy_a->next = headA;
        dummy_b->next = headB;

        while(dummy_a->next != NULL)
        {
            length_A++;
            dummy_a = dummy_a->next;
        }

        while(dummy_b->next != NULL)
        {
            length_B++;
            dummy_b = dummy_b->next;
        }
       
        if(length_A >= length_B)
        {
            for(int i =0;i< length_A-length_B;i++)
            {
                cur_A = cur_A->next;
            }
        }else
        {
            for(int i =0;i< length_B-length_A;i++)
            {
                cur_B = cur_B->next;
            }
        }

        while(cur_A != cur_B)
        {
            cur_A = cur_A->next;
            cur_B = cur_B->next;
        }
        return cur_A;
    }
};


142. 环形链表 II

142. 环形链表 II - 力扣(LeetCode)

描述

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例

示例 1:

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

示例 2:

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

示例 3:

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

提示

链表中节点的数目范围在范围 [0, 104] 内
-105 <= Node.val <= 105
pos 的值为 -1 或者链表中的一个有效索引

进阶

你是否可以使用 O(1) 空间解决此题?

代码解析

/**
 * 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) {
        ListNode *left = head;
        ListNode *right = head;
        
        while(right != nullptr && right->next != nullptr)
        {
            right = right->next->next;
            left = left->next;
            if(right == left)
            {
                ListNode *indnx1 = head;
                ListNode *indnx2 = right;
                while(1)
                {
                    if(indnx1 == indnx2) return indnx1;
                    indnx1 = indnx1->next;
                    indnx2 = indnx2->next;
                }
            }   
        }
        return nullptr;
    }
};


15. 三数之和

15. 三数之和 - 力扣(LeetCode)

描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

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

示例

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示

3 <= nums.length <= 3000
-105 <= nums[i] <= 105

代码解析

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if (nums[i] > 0) {
                return result;
            }
            // 错误去重a方法,将会漏掉-1,-1,2 这种情况
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }
            */
            // 正确去重a方法
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (right > left) {
                // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
                /*
                while (right > left && nums[right] == nums[right - 1]) right--;
                while (right > left && nums[left] == nums[left + 1]) left++;
                */
                if (nums[i] + nums[left] + nums[right] > 0) right--;
                else if (nums[i] + nums[left] + nums[right] < 0) left++;
                else {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;

                    // 找到答案时,双指针同时收缩
                    right--;
                    left++;
                }
            }

        }
        return result;
    }
};

18. 四数之和

18. 四数之和 - 力扣(LeetCode)

描述

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。

示例

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

提示

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

代码解析

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for (int k = 0; k < nums.size(); k++) {
            // 剪枝处理
            if (nums[k] > target && nums[k] >= 0) {
            	break; // 这里使用break,统一通过最后的return返回
            }
            // 对nums[k]去重
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            for (int i = k + 1; i < nums.size(); i++) {
                // 2级剪枝处理
                if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
                    break;
                }

                // 对nums[i]去重
                if (i > k + 1 && nums[i] == nums[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.size() - 1;
                while (right > left) {
                    // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
                    if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
                        right--;
                    // nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
                    } else if ((long) nums[k] + nums[i] + nums[left] + nums[right]  < target) {
                        left++;
                    } else {
                        result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
                        // 对nums[left]和nums[right]去重
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        // 找到答案时,双指针同时收缩
                        right--;
                        left++;
                    }
                }

            }
        }
        return result;
    }
};

你可能感兴趣的:(LeetCode算法学习,算法,学习,leetcode,c++,c语言)