LeetCode--Two Pointers

141. Linked List Cycle(判断链表是否有环)

Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?

解析:

判断一个链表是否有循环,使用快慢指针,慢指针slow一次走一步,快指针fast一次走两步。两个指针同时从head出发,如果fast追上了slow,则说明链表有环;如果fast到达链表末尾还没追上,则表示无环。

同样可以运用 双指针 的问题有:

  1. 求链表的中间节点。如果链表节点数为奇数,则返回中间节点;如果节点数为偶数,则返回中间两个节点的任意一个节点。定义两个指针slow和fast,两者同时从head出发,slow一次走一步,fast一次走两步,当fast走到链表末尾时,slow刚好处于链表的中间。
  2. 查找链表倒数第K个节点。定义两个指针slow和fast,两者从head出发。fast先走K-1步,然后slow和fast同时走。当fast到达链表末尾时,slow指向倒数第K个节点。

    C++代码实现:

    /**
    * 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==NULL || head->next==NULL)
            return false;
        ListNode *slow = head;
        ListNode *fast = head;
        while(fast->next!=NULL && fast->next->next!=NULL){
            slow = slow->next;
            fast = fast->next->next;
            if(fast==slow)
                return true;
        }
        return false;
     }
    };

61. Rotate List(旋转链表)

Given a list, rotate the list to the right by k places, where k is non-negative.

For example:

Given 1->2->3->4->5->NULL and k = 2,
return 4->5->1->2->3->NULL.

解析:

从链表的倒数第K节点开始右旋链表,则需要找到“断点节点”。注意K的大小。如果k大于节点总数,取k=k%count。先遍历链表,记录节点总数count。定义三个指针p q s。q从head走count-k步到达断点,p指向断点的下一个节点,然后s从p开始走到链表末尾,将s->next指向head,head重新指向p。

C++实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(k<=0 || head==NULL)
            return head;
        int count = 0;              //记录链表节点总数
        ListNode *p=head,*q=head,*s=NULL;
        while(p!=NULL){     
            count++;
            p = p->next;
        }
        if(k%count==0)              //如果k为节点数的倍数,则直接返回head
            return head;
        else if(k>count)            //如果k大于节点总数,取k=k%count
            k %= count;

        int step = count-k-1;
        while(step>0){             //q走step步,指向断点
            q = q->next;
            step--;
        }
        p = q->next;              //p指向断点的下一个节点
        q->next = NULL;
        s = p;
        while(s->next!=NULL){    //s指向断链后第二条链的最后一个节点
            s = s->next;
        }
        s->next = head;
        head = p;
        return head;
    }
};

524. Longest Word in Dictionary through Deleting

Given a string and a string dictionary, find the longest string in the dictionary that can be formed by deleting some characters of the given string. If there are more than one possible results, return the longest word with the smallest lexicographical order. If there is no possible result, return the empty string.

Example 1:

Input:
s = “abpcplea”, d = [“ale”,”apple”,”monkey”,”plea”]
Output:
“apple”

Example 2:

Input:
s = “abpcplea”, d = [“a”,”b”,”c”]
Output:
“a”

Note:

All the strings in the input will only contain lower-case letters.
The size of the dictionary won’t exceed 1,000.
The length of all the strings in the input won’t exceed 1,000.

解析:

判断d中的单词Word是否是s的一个删减部分字符后的子串,可以设定两个指针p 、q分别指向s和word的起始字符,如果p==q,则p++,q++;否则,p++(直到在s中找到q所指向的字符)。本题中,将所有候选结果放入vector中,对vector进行排序。排序依据:先按字符串长度,再按字典序排列。

C++代码实现:

class Solution {
public:
    string findLongestWord(string s, vector<string>& d) {
        if(d.size()==0 || s.length()==0)
            return "";

        vector<string> selections;
        for(string word : d){
            if(word.length()>0 && check(s,word))
                selections.push_back(word);
        }
        if(selections.size()==0)
            return "";
        sort(selections.begin(),selections.end(),Solution::cmp);
        return selections[0];
    }
private:
    bool check(const string& s,const string& word){
        int sLen = s.length();
        int wordLen = word.length();
        if(sLen < wordLen)
            return false;
        int p = 0;
        int q = 0;
        while(p<sLen && q<wordLen){
            if(s[p]==word[q]){
                p++;
                q++;
            }
            else{
                p++;
            }
        }
        if(q==wordLen)
            return true;
        return false;
    }
    bool static cmp(const string& a,const string& b){   //先根据字符串长度,再按字典序 排序
        int len1 = a.length();
        int len2 = b.length();
        if(len1>len2)
            return true;
        else if(len1==len2)
            return a.compare(b)<0;
        else
            return false;
    }
};

142. Linked List Cycle II(查找链表中环的起始位置)

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Note: Do not modify the linked list.

解析:

判断一个链表是否有循环,使用快慢指针,慢指针slow一次走一步,快指针fast一次走两步。两个指针同时从head出发,如果fast追上(==)了slow,则说明链表有环;如果fast到达链表末尾还没追上,则表示无环。
查找环的起始位置,当fast==slow时,表示有环,此时,设定一个指针begin,从head开始,循环判断begin==slow,如果相等,则说明begin所指位置为环的起始点,否则begin和slow指针分别指向下一个节点。

C++代码实现:

/**
 * 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==NULL || head->next==NULL)
            return NULL;

        ListNode* slow = head;
        ListNode* fast = head;
        ListNode* result = head;

        while(fast->next!=NULL && fast->next->next!=NULL){
            slow = slow->next;
            fast = fast->next->next;
            if(fast==slow){         //此时指针不一定指向环的起始位置
                while(slow!=result){
                    slow = slow->next;
                    result = result->next;
                }
                return result;
            }
        }
        return NULL;
    }
};

19. Remove Nth Node From End of List(删除链表倒数第n个节点)

Given a linked list, remove the nth node from the end of list and return its head.

For example,

Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.

解析:

使用双指针,定义slow和fast,fast从head先走n-1步然后slow和fast同时走,直到fast到达链表末尾,此时,slow指向待删除的节点,fast指向末尾。定义一个指针pre,从head开始走,直到pre指向slow的前一个节点,将pre->next=slow->next同时删除节点slow,注意如果待删除节点slow指向头节点,则head=head->next同时删除slow节点。另外,需要注意head是否为空或者n是否为负数或者大于链表节点数的问题

C++代码实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(n<=0 || head==NULL)
            return head;
        ListNode* slow = head;      //待删除的节点位置
        ListNode* fast = head;      //末尾节点

        int step = n-1;
        while(step-->0){
            if(fast->next!=NULL)    //防止n大于链表节点数
                fast = fast->next;
            else
                return NULL;
        }
        while(fast->next!=NULL){
            fast = fast->next;
            slow = slow->next;
        }
        if(slow==head){         //待删除的节点为头节点
            head = head->next;
            delete slow;
            slow = NULL;
            return head;
        }
        ListNode* pre = head;
        while(pre->next!=slow){     //pre指向slow的前一个节点
            pre = pre->next;
        }
        pre->next = slow->next;
        delete slow;
        slow = NULL;
        return head;
    }
};

287. Find the Duplicate Number(查找重复的数)

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Note:

You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n2).
There is only one duplicate number in the array, but it could be repeated more than once.

解析:

先对数组进行排序,然后进行二分搜索。取中间节点middle=(start-end)/2。如果数组大小为偶数,则判断nums[middle]是否等于middle[middle+1],如果等于则直接返回nums[middle]。如果是奇数,则判断nums[middle]是否等于middle[middle+1] 或者nums[middle]是否等于middle[middle-1],如果相等,则直接返回nums[middle]。否则,在nums[start : middle] 和 nums[middle-1 : end]中去搜索。

C++代码实现:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int count = nums.size();
        if(count<=1)
            return 0;
        sort(nums.begin(),nums.end());
        return binarySearch(nums,0,count-1);
    }
private:
    int binarySearch(const vector<int>& nums,int start,int end){
        int count = end-start+1;
        if(count<=1)
            return 0;
        int middle = (start+end)/2;
        if(count%2==0){
            if(nums[middle]==nums[middle+1])
                return nums[middle];
        }
        else{
            if(nums[middle]==nums[middle+1] || nums[middle]==nums[middle-1])
                return nums[middle];
        }
        int result = binarySearch(nums,start,middle);
        if(result==0)
            return binarySearch(nums,middle+1,end);
        return result;
    }
};

3. Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given “abcabcbb”, the answer is “abc”, which the length is 3.
Given “bbbbb”, the answer is “b”, with the length of 1.
Given “pwwkew”, the answer is “wke”, with the length of 3. Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

解析:

移动窗口+双指针,定义left和right指针,定义一个Hash表dict用于记录某个字符是否出现,count记录窗口[left,right]的长度。如果dict中没有s[right],则将s[right]加入dict,count加1。否则,移动left,使得窗口中的每个字符仅有一个。

C++代码实现:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.length();
        if(len<=1)
            return len;
        int maxLen = 0;
        int left = 0, right = 0;
        int count = 0;
        unordered_set<char> dict;
        while(right<len){
            if(!dict.count(s[right])){
                dict.insert(s[right]);
                count++;
            }
            else{
                if(s[left]==s[right]){
                    left++;
                }else{
                    while(left<right && s[left]!=s[right]){
                        if(dict.count(s[left]))
                            dict.erase(s[left]);
                        count--;
                        left++;
                    }
                    if(left<right)   //因为此时s[left]==s[right],需要跳过left
                        left++;     
                }

            }
            right++;
            maxLen = count>maxLen ? count : maxLen;
        }
        return maxLen;
    }
};

86. Partition List

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.

For example,

Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.

解析:

(1)方法一:设定两个指针left和right,right表示需要调整节点(val < x)的位置,left表示需要插入节点的位置。使用这个方法在调整指针指向时比较复杂、需要考虑多种情况。时间复杂度O(n)。
(2)方法二:根据X的大小将原来的链表分成两条链,一条节点val都比x小的链,一条节点val都不小于x的链,然后两条链合并即为调整顺序的链。具体来说,新建节点node1(0)、node2(0),分别对应上述两条链。设定两个指针p1=node1,p2=node2。从head开始遍历,如果head->val < x,则执行p1=p1->next=head;否则,执行p2=p2->next=head。以这种方式将两条链分开,再将p1->next = node2->next,这样就把两条链合并。最终返回node1->next即为调整顺序后的链。这个方法代码相当简短,推荐使用。时间复杂度O(n)。

C++代码实现:
方法二:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        if(head==NULL)
            return NULL;
        ListNode node1(0), node2(0);
        ListNode *p1 = &node1, *p2 = &node2;
        while (head) {
            if (head->val < x)
                p1 = p1->next = head;
            else
                p2 = p2->next = head;
            head = head->next;
        }
        p2->next = NULL;
        p1->next = node2.next;
        return node1.next;
    }
};

方法一:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        if(head==NULL)
            return NULL;
        ListNode* left = head;
        ListNode* right = head;

        ListNode* preLeft = left;
        ListNode* preRight = right;

        bool foundBigger = false;

        while(right!=NULL){
            if(right->val >= x)
                foundBigger = true;
            else if(right->val < x && foundBigger)
                break;
            preRight = right;
            right = right->next;
        }
        if(right==NULL)     //没有找到
            return head;
        //此时,right指向val<x的节点
        while(right!=NULL){
            if(right->val < x){
                int val = right->val;
                while(left!=NULL && left->val<x){
                    preLeft = left;
                    left = left->next;
                }
                preRight->next = right->next;
                right->next = left;
                if(left==head){     //如果left此时指向的是head,则head需要改变为right
                    head = right;
                    right = left;
                    preLeft = head;
                    left = head;
                    preRight = head;
                }
                else{
                    preLeft->next = right;
                    right = preRight;
                    left = preLeft->next;
                }
            }
            preRight = right;
            right = right->next;
        }
        return head;
    }
};

80. Remove Duplicates from Sorted Array II

Given a sorted array, remove the duplicates in place such that each element appear at most twice and return the new length.
Do not allocate extra space for another array, you must do this in place with constant memory.

For example,

Given sorted array nums = [1,1,1,2,2,3],
Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3. It doesn’t matter what you leave beyond the new length.

C++代码实现:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int count = nums.size();
        if(count<=2)
            return count;
         int i = 0;
        for (int n : nums)
            if (i < 2 || n > nums[i-2])
                nums[i++] = n;
        return i;
    }
};

75. Sort Colors

Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue.
Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.

Note:
You are not suppose to use the library’s sort function for this problem.

解析:

因为array中的数只为[0…..1…..2],因此用一个表dict[]分别记录0 1 2的出现次数,然后按顺序把他们放到array中即可。

C++代码实现:

class Solution {
public:
    void sortColors(vector<int>& nums) {
        if(nums.size()<=1)
            return;
        int dict[3]={0,0,0};
        for(int a : nums){
            if(a<0 || a>2)      //not invalid
                return;
            dict[a]++;
        }
        int j = 0;
        for(int i=0; i<=2; i++){
            while(dict[i]-->0){
                nums[j++] = i;
            }
        }
    }
};

567. Permutation in String

Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1. In other words, one of the first string’s permutations is the substring of the second string.

Example 1:

Input:s1 = “ab” s2 = “eidbaooo”
Output:True
Explanation: s2 contains one permutation of s1 (“ba”).

Example 2:

Input:s1= “ab” s2 = “eidboaoo”
Output: False

Note:

The input strings only contain lower case letters.
The length of both given strings is in range [1, 10,000].

解析:

类似substr问题,万能法。基本原理是:在s2中先找到包含s1所有字符的窗口[left,right],通过不断缩小窗口,最终找到结果。具体算法模板如下:

int findSubstring(string s){
        unordered_map<char,int> map(128,0);
        int counter; // check whether the substring is valid
        int begin=0, end=0; //two pointers, one point to tail and one  head
        int d; //the length of substring

        for() { /* initialize the hash map here */ }

        while(end<s.size()){

            if(map[s[end++]]-- ?){  /* modify counter here */ }

            while(/* counter condition */){ 

                 /* update d here if finding minimum*/

                //increase begin to make it invalid/valid again

                if(map[s[begin++]]++ ?){ /*modify counter here*/ }
            }  

            /* update d here if finding maximum*/
        }
        return d;
  }

C++代码实现:

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int len1 = s1.length();
        int len2 = s2.length();
        if(len1==0 || len2==0)
            return false;
        int dict[128]={};
        for(char c: s1){
            dict[c]++;
        }
        int left = 0, right = 0, counter = len1;
        while(right<len2){
            if(dict[s2[right++]]-->0)
                counter--;
            while(counter==0){
                if(s1.size()==(right-left)){
                    return true;
                }
                if(dict[s2[left++]]++==0)
                    counter++;
            }
        }
        return false;
    }
};

42. Trapping Rain Water

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.
这里写图片描述
The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped.

解析:

设定两个指针left和right,分别指向左右两边。然后移动left和right指针,找到能盛水的容器的两端。然后从两端不断查找可盛水的部分,直到left==right。详细见代码。

C++实现:

class Solution {
public:
    int trap(vector<int>& height) {
        int size = height.size();
        if(size<3)
            return 0;
        int result = 0;
        int left = 0, right = size-1;

        //find the left and right edge to hold the water
        while(left<right && height[left]<=height[left+1] )
            left++;
        while(left<right && height[right]<=height[right-1])
            right--;

        int left_height = 0;
        int right_height = 0;

        while(left<right){
            left_height = height[left];
            right_height = height[right];
            if(left_height<=right_height){
                //add water until larger edge than left
                while(left<right && left_height>height[++left]){
                    result += (left_height-height[left]);
                }
            }else{
                //add water until larger edge than right
                while(left<right && right_height>height[--right]){
                    result += (right_height-height[right]);
                }
            }
        }
        return result;
    }
};

求第K的元素

给定一个无序数组,一个K,查找数组中第K大的元素。

解析:

(1)方法一:对数组进行排序,输出第k个元素。算法时间复杂度为O(nlogn)。不建议。
(2)方法二:借鉴快排的思想,调用partition,将数组分成两个部分,将无序数组分成两部分,前一部分比基准大,后一部分比基准小。
如果基准的位置为k-1,则此时基准即为第K大的元素。
如果基准位置小于k-1,则意味着第K大的元素在基准的后面部分。
继续调用partition,直到基准的位置为k-1。此时基准即为第K大的数。
(3)方法三:堆。维护一个一个大小为K的小顶堆,堆顶元素是最大的K个数中最小的,即为第K大的元素。具体步骤:
维护一个大小为K的堆;
对于每个数,如果当前堆未满,则插入堆中;如果堆满了,则将该数字和堆顶元素比较。如果大于堆顶,则将堆顶元素替换为当前元素,调整小顶堆。如果该数字小于堆顶,则直接抛弃。
最后,输出堆顶元素,即为第K大的元素。

C++代码实现:
方法二:

#include <iostream>
#include <vector>
using namespace std;

int partition(vector<int>& nums,int left,int right)
{
    int key = nums[left];
    while(left < right){
        while(left<right && nums[right]<=key)
            right--;
        nums[left] = nums[right];
        while(left<right && nums[left]>=key)
            left++;
        nums[right] = nums[left];
    }
    nums[left] = key;
    return left;
}

//借鉴快排的思想,将无序数组分成两部分,前一部分比基准大,后一部分比基准小,
//如果基准的位置为k-1,则此时基准即为第K大的元素。
//如果基准位置大于k-1,则意味第K大的元素在基准的前面部分
//如果基准位置小于k-1,则意味着第K大的元素在基准的后面部分
int findKthElement(vector<int>& nums,int n,int k)
{
    int left=0, right=n-1;
    int realK = k-1;
    int mid = partition(nums,left,right);
    while(mid!=realK){
        if(mid > realK)
            right = mid-1;
        else
            left = mid+1;
        mid = partition(nums,left,right);
    }
    return nums[mid];
}


int main()
{
    int n;
    cin>>n;
    vector<int> nums(n);
    for(int i=0; i<n; i++)
        cin>>nums[i];
    cout<<findKthElement(nums,n,2);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值