0
点赞
收藏
分享

微信扫一扫

基础算法笔记——双指针

兵部尚输 2022-05-01 阅读 68
算法

目录

一、引入

二、算法思想

三、例题

第一题

第二题 

第三题

第四题

第五题


一、引入

先看一道例题  例题

对于这道题,我们最先想到的方式是另开一个数组,将该数组的各个数的平方一一存入该数组,之后重新排序。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> num(nums.size()); //开辟新数组

        for(int i = 0; i < nums.size(); ++i)//将原数组的平方处理后一一存入新数组
        {
            num[i] = nums[i] * nums[i];
        }

        sort(num.begin(), num.end(), less());//对新数组排序

        return num;//返回新数组
        
    }
};

对这个解法我们做点改动

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {

        // vector<int> num(nums.size());

        // for(int i = 0; i < nums.size(); ++i)
        // {
        //     num[i] = nums[i] * nums[i];
        // }

        // sort(num.begin(), num.end(), less());

        // return num;

        
        vector<int > num(nums.size());//新的空间
        int i = 0, j = nums.size() - 1;

        for(int k = nums.size() - 1; i <= j;)//遍历数组
        {            
            if(nums[i] * nums[i] < nums[j] * nums[j])
            {
                num[k] = nums[j] * nums[j];
                j--; 
            }
            else
            {
                num[k] = nums[i] * nums[i];
                i++;
            }
            k--;
        }
        return num;
    }
};

在这个解法中,因为包含负数,所以平方数最大值可能出现在头与尾;因此在遍历过程中,逐步比较头一个与最后一个。

二、算法思想

双指针其实是对原有算法的一种优化方式,常见的有快慢指针,对撞指针。

三、例题

第一题

第一题

先一般性的解法

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        vector<int > num;

        for(int i = 0, j = 0; i < nums.size(); ++i)//将nums中非0数排入num
        {
            if(nums[i] != 0)
            {
                num.push_back(nums[i]);
                j++;
            }
        }

        for(int i = 0, j = 0; i < nums.size(); ++i)//将nums中0排入num
        {
            if(nums[i] == 0)
            {
                num.push_back(nums[i]);
                j++;
            }
        }

        nums.assign(num.begin(), num.end());//将num复制进nums中
    }
};

双指针解法

我们设想如果两个指针,一个指针在左边,一个在右边查找非0数,如果右边的数字不是0,就将右边的数字换到左边。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        // vector<int > num;

        // for(int i = 0, j = 0; i < nums.size(); ++i)
        // {
        //     if(nums[i] != 0)
        //     {
        //         num.push_back(nums[i]);
        //         j++;
        //     }
        // }

        // for(int i = 0, j = 0; i < nums.size(); ++i)
        // {
        //     if(nums[i] == 0)
        //     {
        //         num.push_back(nums[i]);
        //         j++;
        //     }
        // }

        // nums.assign(num.begin(), num.end());

        int i = 0; //指向非0数
        int j = 0; //
        while(i < nums.size())//遍历整个数组
        {
            if (nums[i])//右边遇到非0数
            {
                swap(nums[i], nums[j]);
                j++;
            }
            i++;
        }
    }
};

做个动画演示一个整个过程

第二题 

第二题

这题就不用一般性解法演示了,比较复杂,难度不大

定义两个指针,一个指向字符串的首位,一个指向字符串的末尾,逐步将首位与末尾交换

class Solution {
public:
    void reverseString(vector<char>& s) {
        int i = 0, j = s.size() - 1;

        for(; i <= j; i++, j--)
        {
            swap(s[i], s[j]); //交换头与尾
        }
    }
};

第三题

第三题

对于这题我们很容易能想的方法就是直接找到中间的那个数,但是链表又无法直接定位,因此我们可以把该链表转换为数组,再定位到中间的数。

/**
 * 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* middleNode(ListNode* head) {
        vector <ListNode* > L = {head};
        
        while (L.back()->next != NULL)
        {
            L.push_back(L.back()->next);
        }

        return L[L.size() / 2];
    }
};

这个是把链表的数据转换到数组中,再直接输出中间节点。我们做点改进,在原有的链表中,找到中间的节点。

/**
 * 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* middleNode(ListNode* head) {
        
        /*
        * 第一种
        */
        // vector <ListNode* > L = {head};
        
        // while (L.back()->next != NULL)
        // {
        //     L.push_back(L.back()->next);
        // }

        // return L[L.size() / 2]


        /*
        * 第二种
        */
        int n = 0;//初始化
        ListNode* cur = head;

        while(cur != nullptr)//确定节点数量
        {
            ++n;
            cur = cur -> next;
        }

        int i = 0;
        cur = head;//初始化cur

        while (i < n / 2)//找到中间节点
        {
            i++;
            cur = cur->next;
        }

        return cur;
    }
};

再用双指针的方式优化一下,这里用到的是快慢指针。

/**
 * 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* middleNode(ListNode* head) {

        /*
        * 第一种
        */
        // vector <ListNode* > L = {head};
        
        // while (L.back()->next != NULL)
        // {
        //     L.push_back(L.back()->next);
        // }

        // return L[L.size() / 2]


        /*
        * 第二种
        */
        // int n = 0;
        // ListNode* cur = head;

        // while(cur != nullptr)//确定节点数量
        // {
        //     ++n;
        //     cur = cur->next;
        // }

        // int i = 0;
        // cur = head;//初始化cur

        // while (i < n / 2)//找到中间节点
        // {
        //     i++;
        //     cur = cur->next;
        // }

        // return cur;


        /*
        * 第三种
        */
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast != nullptr && fast->next != nullptr)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
};

快慢指针是利用两个指针的速度不一致,比如这题中,快指针的速度是慢指针的两倍,快指针到末尾的时候,恰好慢指针到达链表的中间。

第四题

第四题

class Solution {
public:
    void rotate(vector<int>& nums, int k) {

        //int i = 0; int j = nums.zise() - k;

        vector<int > num(nums.size());//开辟一个数组空间
        
        int temp;

        for(int i = 0; i < nums.size(); i++)
        {
            if(i < nums.size() - k)
            {
                num[(i + k) % nums.size()] = nums[i];//对nums.size()取模,防止溢出
            }
            else 
            {
                temp = nums.size() - i - k;
                temp = fabs(temp);
                temp = temp % nums.size();//同样取模,防止溢出
                num[temp] = nums[i];
            }
        }
        nums.assign(num.begin(), num.end());
    }
};

后面看官方解答,似乎并不需要分类,于是乎

class Solution {
public:
    void rotate(vector<int>& nums, int k) {

        //int i = 0; int j = nums.zise() - k;

        vector<int > num(nums.size());//开辟一个数组空间
        
        int temp;

        for(int i = 0; i < nums.size(); i++)
        {
            // if(i < nums.size() - k)
            // {
            //     num[(i + k) % nums.size()] = nums[i];//对nums.size()取模,防止溢出
            // }
            // else 
            // {
            //     temp = nums.size() - i - k;
            //     temp = fabs(temp);
            //     temp = temp % nums.size();//同样取模
            //     num[temp] = nums[i];
            // }
            num[(i + k) % nums.size()] = nums[i];
        }
        nums.assign(num.begin(), num.end());
    }
};

这个也是可以AC的。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
         //int i = 0; int j = nums.zise() - k;

        // vector<int > num(nums.size());//开辟一个数组空间
        
        // int temp;

        // for(int i = 0; i < nums.size(); i++)
        // {
        //     // if(i < nums.size() - k)
        //     // {
        //     //     num[(i + k) % nums.size()] = nums[i];//对nums.size()取模,防止溢出
        //     // }
        //     // else 
        //     // {
        //     //     temp = nums.size() - i - k;
        //     //     temp = fabs(temp);
        //     //     temp = temp % nums.size();//同样取模
        //     //     num[temp] = nums[i];
        //     // }
        //     num[(i + k) % nums.size()] = nums[i];
        // }
        // nums.assign(num.begin(), num.end());

        int i = 0, j = nums.size() - 1;

        while(i <= j)//将所有的数调转顺序。
        {
            swap(nums[i], nums[j]);
            i++;
            j--;
        }

        i = 0;
        j = k % nums.size() - 1;//取模,防止数据溢出

        while(i <= j)//调转前数列的顺序
        {
            swap(nums[i], nums[j]);
            i++;
            j--;
        }

        i = k % nums.size();//取模,防止数据溢出
        j = nums.size() - 1;

        while(i <= j)//调转后序列的顺序
        {
            swap(nums[i], nums[j]);
            i++;
            j--;
        }
    }
};

用双指针的方法,我们可以这样,将整个数列翻转,之后以k为界,分别将前后数列翻转。

官方解答

class Solution {
public:
    void reverse(vector<int>& nums, int start, int end) {
        while (start < end) {
            swap(nums[start], nums[end]);
            start += 1;
            end -= 1;
        }
    }

    void rotate(vector<int>& nums, int k) {
        k %= nums.size();
        reverse(nums, 0, nums.size() - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.size() - 1);
    }
};

 

第五题

第五题

在写这题的时候,我们想的就是,针对每一个单词分别调换。

class Solution {
public:
    string reverseWords(string s) {
        //string str;//新的空间,用于储存已经反转好的单词
        int i;
        int begin = 0, end = 0;//交换时的开始位置与结束位置
        int sLength = s.length();//字符串长度
        int count = 0;//定义遍历字母的数量
        while(count < sLength) //count的值不可以随意的变动,它表示的是遍历的字母位置
        {
            begin = count;//将i初始化至当前位置

            while(s[count] != ' ' && count < sLength)//确定j的位置,每个单词的结尾
            {
                count++;
            }
            
            end = count - 1;

            while(begin <= end)//颠倒单词的字母顺序
            {
                swap(s[begin], s[end]);
                begin++;
                end--;
            }

            while(s[count] == ' ' && count < sLength)//定位到下一个单词
            {
                count++;
            }
        }
        return s;
    }
};

举报

相关推荐

0 条评论