算法基础/双指针

1.82. 删除排序链表中的重复元素 II

题目描述:

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

示例:

解答描述:

删除有序链表中的重复数字的解题思路不难,就是从头开始遍历,跳过所有的重复数字即可。

该题难点在于链表的实现上,因为要返回头结点,而头结点head可能会被删除,所以需要开辟一个新的哑结点new_head,new_head->next=head,最终只需要返回new_head->next即可

用两个指针r=new_head,p=head,前后遍历。


代码:

/**
 * 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* deleteDuplicates(ListNode* head) {
        if(head==NULL || head->next==NULL)//只有0个或1个元素,直接返回即可
        {
            return head;
        }
        ListNode *p=head;

        ListNode *new_head=new ListNode();//新的负责节点,哑结点,它的后面是head节点
        new_head->next=head;
        ListNode *r=new_head;

        while(p!=NULL)
        {
            while(p->next!=NULL && p->next->val==p->val )//有重复时,把所有重复的删除
            {
                p=p->next;
            } 
            if(r->next==p)//r和p直接没有其他元素时,直接令r=r->next
            {
                r=r->next;
            }
            else//不存在重复的该元素,直接加入结果链表
            {
                r->next=p->next;
            }
            p=p->next;   
        }
        return new_head->next;
    }
};

2.15. 三数之和

题目描述:

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

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

示例:

示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:
输入:nums = []
输出:[]

示例 3:
输入:nums = [0]
输出:[]

解答描述:

这是第二次做这个题了,思路也很简单,固定a,双指针扫描b,c。

关键点在于为了得到不重复的三元组,需要对a,b,c都跳过重复的字符。


代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n=nums.size();
        vector<vector<int>> ans;
        vector<int> temp(3);
        //先排序
        sort(nums.begin(),nums.end());
        //然后枚举每个a,双指针法找到相应的b,c
        for(int i=0;i<n-2;i++)
        {
            if(nums[i]>0)
            {
                return ans;
            }
            if(i>0 && nums[i]==nums[i-1])//对a去重
            {
                continue;
            }
            int l=i+1;
            int r=n-1;
            while(l<r)
            {
                if((nums[i]+nums[l]+nums[r])==0)
                {
                    temp[0]=nums[i];
                    temp[1]=nums[l];
                    temp[2]=nums[r];
                    ans.emplace_back(temp);
                    //关键一步,去除重复的b和c
                    /*//为什么这样就超出时间限制
                    while(l<r && nums[l]==nums[l+1]){l=l+1;}
                    while(l<r && nums[r]==nums[r-1]){r=r-1;}*/
                    //为什么这样又可以呢
                    while(l<r && nums[l]==nums[++l]);
                    while(l<r && nums[r]==nums[--r]);
                   
                    
                }
                else if((nums[i]+nums[l]+nums[r])>0)
                {
                    r--;
                }
                else
                {
                    l++;
                }
            }
        }
        return ans;
       
    }
};

3.844. 比较含退格的字符串

题目描述:

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。


示例:

示例 1:
输入:s = "ab#c", t = "ad#c"
输出:true
解释:s 和 t 都会变成 "ac"。

示例 2:
输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 ""。

示例 3:
输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 "c",但 t 仍然是 "b"。

解答描述:

该题需要注意的是,每个#只能影响它前面的字符,退掉前面的字符,不能影响后面的字符。

所以从尾到头进行扫描,记录下当前还未使用的#个数cnt,每遇到一个#,cnt+1,每遇到一个字符cnt-1,如果cnt减为0了,而此时两个字符串对应字符不相等,说明二者不相等,返回false。或者当一个字符串遍历完了,而另一个没有时,也返回false。


代码:

class Solution {
public:
    bool backspaceCompare(string s, string t) {
        int n1=s.size();
        int n2=t.size();
        //从后往前遍历,并记录当前还剩多少个#没有应用
        int i=n1-1;
        int j=n2-1;
        int cnt1=0;
        int cnt2=0;
        while(i>=0 || j>=0)
        {
            while(i>=0)
            {
                if(s[i]=='#')//当遇到#时,cnt1++
                {
                    cnt1++;
                    i--;
                }
                else
                {
                    if(cnt1>0)//当cnt1>0时,表明该字符需要用掉一个退格符退去
                    {
                        cnt1--;
                        i--;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            while(j>=0)
            {
                if(t[j]=='#')//当遇到#时,cnt1++
                {
                    cnt2++;
                    j--;
                }
                else
                {
                    if(cnt2>0)//当cnt1>0时,表明该字符需要用掉一个退格符退去
                    {
                        cnt2--;
                        j--;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            if(i>=0 && j>=0)//当没有遍历完且两个字符串不相等时,返回false
            {
                if(s[i]!=t[j])
                {
                    return false;
                } 
            }
            else 
            {
                if(i>=0 || j>=0)//当只有一个字符串遍历完
                {
                    return false;
                }
            }
            i--;
            j--;
        }
        return true;
    }
};

4.986. 区间列表的交集

题目描述:
示例:

解答描述:
代码:

5.11. 盛最多水的容器

题目描述:

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例:

 

解答描述:

这个也是第二次做了,思路就是:每次让高度较小的那边移动,才有可能找到容量更大的。


代码:

class Solution {
public:
    int maxArea(vector<int>& height) {
        int n=height.size();
        int l=0;
        int r=n-1;
        int max_s=0;
        while(l<r)
        {
            int h=min(height[l],height[r]);
            int w=r-l;
            max_s=max(max_s,h*w);
            if(height[l]<height[r])
            {
                l++;
            }
            else
            {
                r--;
            }   
        }
        return max_s;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值