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;
}
發佈了31 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章