数据结构-线性表(数组)-双指针->数组追赶(快慢指针) 977&344&151&524&611无&80&986(没做)(总结各种排序算法 原地算法图解)

这类题为什么叫数组追赶?

  1. 有序数组的平方
  • 思路
    法一:先对nums每个元素进行平方,再sort排序
    法二:找到nums中负数和正数的分界线,进行类似与归并排序进行排序。[0,divid]为负数,平方后递减;[divid+1,len-1]为正数,平方后递增。相当于有了两个有序数组,用两个指针分别指向前一个数组最后面元素和后一个数组最前面的元素,均为两个数组中最小的元素,依次比较,选较小的放入新数组中,并且移动指针,当某个指针移出边界后,令一个指针还没遍历到的依次放入新数组就可。
    法三:左右指针,nums是非递减数组,所以下标为0和下标为len-1的元素平方最大,左右指针分别指向他们,依次往中间收缩。所以在res数组中,得从下标为len-1开始存入数据
  • 代码

法一:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len = nums.size();
        for(int i = 0;i<len;++i){
            nums[i] *= nums[i];
        }
        sort(nums.begin(),nums.end());
        return nums;
    }
};

法二

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len = nums.size();
        int divid = -1;
        int negative = -1;
        int positive = 0;
        vector<int> res;

        for(int i = 0;i<len;++i){
            if(nums[i]<0) divid = i;
            else break;
        }

        negative = divid;
        positive = divid+1;
        while(negative>=0 || positive<len){
            if(negative < 0) {
                res.push_back(nums[positive]*nums[positive]);
                ++positive;
            }
            else if(positive == len){
                res.push_back(nums[negative]*nums[negative]);
                --negative;
            }
            else if(nums[positive]*nums[positive] < nums[negative]*nums[negative]){
                res.push_back(nums[positive]*nums[positive]);
                ++positive;
            }else{
                res.push_back(nums[negative]*nums[negative]);
                --negative;
            }
        }
        return res;
    }
};

法三:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len = nums.size();
        int left = 0,right = len-1,pos = len-1;
        vector<int> res(len);
        while(left<=right){
            if(nums[left]*nums[left] > nums[right]*nums[right]){
                res[pos] = nums[left]*nums[left];
                ++left;
            }else{
                res[pos] = nums[right]*nums[right];
                --right;
            }
            --pos;
        }
        return res;
    }
};
  • 总结学习
    总结一下各种排序算法:
    使用双指针时可以把一个数组看成两个分开的数组,每个指针代表一个数组。
    1:归并排序 (未完。。)
  1. 反转字符串
  • 思路
    自己想出来了
  • 代码
class Solution {
public:
    void reverseString(vector<char>& s) {
        int len = s.size();
        int left = 0,right = len-1;
        char temp;
        while(left <= right){
            temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            ++left;
            --right;
        }
    }
};
  1. 颠倒字符串中的单词

代码:

  • 没有利用原地实现
class Solution {
public:
    string reverseWords(string s) {
        int len = s.size();
        string res;
        string temp;
        int i,j;
        for(i = 0;i<len;++i){
            if(s[i] != ' '){
                for(j = i+1;j<len;++j){
                    if(s[j] == ' ') break;
                }
                temp = s.substr(i,j-i);
                res = temp + " " + res;
                i = j;
            }
        }
        int a = res.size();
        return res.substr(0,a-1);
    }
};
  • 原地算法:没想出来
    在这里插入图片描述
  1. idx代表新候选字符串下标,start和end表示s字符串下标
  2. 当s[start]不是空格时,先判断idx是否为零,如果为零则表示候选数组中还没有字符,否者就是处理完一个单词,既有单词在候选字符串中,需要在单词后加一个空格
  3. start~end表示单词长度,为什么要s[idx++] = s[end++];,而不是单纯把下标++?因为下面对单词反转是按照候选字符串的下标进行反转
    循环完之后,非空字符都处理完了,需要把后面的空字符去掉(反转前字符串前的空字符)
  4. 函数都是前闭后开。
class Solution {
public:
    string reverseWords(string s) {
        // 反转整个字符串
        reverse(s.begin(), s.end());
        
        int n = s.size();
        int idx = 0;
        for (int start = 0; start < n; ++start) {
            if (s[start] != ' ') {
                // 填一个空白字符然后将idx移动到下一个单词的开头位置
                if (idx != 0) s[idx++] = ' ';

                // 循环遍历至单词的末尾
                int end = start;
                while (end < n && s[end] != ' ') s[idx++] = s[end++];

                // 反转整个单词
                reverse(s.begin() + idx - (end - start), s.begin() + idx);

                // 更新start,去找下一个单词
                start = end;
            }
        }
        s.erase(s.begin() + idx, s.end());
        return s;
    }
};
  1. 通过删除字母匹配到字典里最长单词
  • 思路
    法一双指针: 一个sp指针指字符串s,另一个dp指针值dictionary中的字符串d。当指针对应字符相等时,sp dp同时++,否者sp++;当dp移动到尾部时,说明d时s的子串。
    法二 先对dic进行长度降序和字母序升序排序,然后从前往后遍历找到第一个符合的直接返回。
    法三:动态规划??????????
  • 代码
class Solution {
public:
    string findLongestWord(string s, vector<string>& dictionary) {
        int len = dictionary.size();
        string res,temp;
        int sp = 0,dp = 0;
        for(int i = 0;i<len;++i){
            int len2 = dictionary[i].size();
            while(sp<s.size() && dp<len2){
                if(s[sp] == dictionary[i][dp]){
                    dictionary[i][dp++] = s[sp++];
                } else{
                    ++sp;
                }
            }
            if(dp == len2){
                if(dp > res.size() ||
                  (dp == res.size() && dictionary[i].compare(res)<0)){
                        res = dictionary[i];
                }
            }

            dp = 0;
            sp = 0;
        }
        return res;
    }
};

核心在while循环中,相当于把d当成上图中的下面的容器,dp为idx的类原地算法。
下面判断时,首先看是否是子串,然后判断长度大于 或者 长度相等但是字符序较小。

  • 总结学习
    1:双指针+原地算法做的时候一定要画图,重点在判断条件上,确定好判断条件,确定好候选容器。根据判断条件双指针不是指向同一个容器。
    2:string 的compare函数:
  1. 有效三角形的个数
  • 思路
    法一:排序后,遍历,对于两数之和,在后续的数组中查找小于两数之和的最大元素
    法二:排序后,遍历i,j k为双指针 nums[k]<nums[i]+nums[j]的k的最大
    细节:数组是非负的,可能有零,i,j对应元素都为0的话,[j+1~len-1]都是大于nums[i]+nums[j],二分法失败,解决办法是令k=j;
    同样,法二也会有不存在满足 nums[k]<nums[i]+nums[j] 的下标的情况,这样的话,不让k出现在j的右侧就可以,累加是把 k-j与0的较大值累加到res
  • 代码
class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                int left = j + 1, right = n - 1, k = j;
                while (left <= right) {
                    int mid = (left + right) / 2;
                    if (nums[mid] < nums[i] + nums[j]) {
                        k = mid;//仔细品 把这个小于的元素先保存下来
                        left = mid + 1;
                    }
                    else {
                        right = mid - 1;
                    }
                }
                ans += k - j;
            }
        }
        return ans;
    }
};

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int len = nums.size();
        int res = 0,k = 0;
        sort(nums.begin(),nums.end());
        for(int i = 0;i<len;++i){
            k = i;
            for(int j = i+1;j<len;++j){
                while(k+1<len && nums[i]+nums[j]>nums[k+1]){//对k+1判断
                    ++k;
                }
                res += max(k-j,0);
            }
            
        }
        return res;
    }
};
  • 学习
    查找->二分查找
    双指针变化:对于需要三个标志
  1. 删除有序数组中的重复项 II 自己做出来了
  • 思路
    因为给定数组是有序的,所以相同元素必然连续。可以使用双指针解决本题,遍历数组检查每一个元素是否应该被保留,如果应该被保留,就将其移动到指定位置。
    我的做法:在原数组中设置标志位,相同元素长度大于2就不往候选数组中添加
    更聪明的做法:直接比较nums[fast]与nums[slow-2]是否相等
  • 代码
    我的做法;
    int removeDuplicates(vector<int>& nums) {
        int len = nums.size();
        int idx = 1,pos = 1;
        int flag = 0;
        while(pos<len){
            if(nums[pos] == nums[pos-1]) ++flag;
            else flag = 0;

            if(flag<2){
                nums[idx++] = nums[pos++];
            }else{
                ++pos; 
            }
        }
        return idx;
    }

聪明的做法:

int removeDuplicates(vector<int>& nums) {
        int len = nums.size();
        if(len<2) return len;
        int idx = 2,pos = 2;
        while(pos<len){
            if(nums[idx-2] != nums[pos]){
                nums[idx] = nums[pos];
                ++idx;
            }
            ++pos; 
    
        }
        return idx;
    }
  • 学习

1:总结双指针使用的场景:

  • 有序数组
    2:原地算法,将当前元素添加到候选数组的条件判断:
    先考虑在原数组中对元素处理是否能得出是否添加的结论
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线性表是一种常见的数据结构,它表示具有相同数据类型的一组元素的有序序列。线性表中的元素之间存在一种顺序关系,每个元素都有一个前驱和一个后继(除了第一个元素有前驱,最后一个元素有后继)。线性表可以用顺序存储结构或链式存储结构实现。 在顺序存储结构中,线性表的元素按照顺序存储在连续的内存空间中,可以通过元素的下标来访问和操作元素。插入或删除元素时,需要移动其他元素,因此操作的时间复杂度较高。 链式存储结构中,线性表的每个元素都包含一个数据域和一个指针域,指针指向下一个元素。通过指针的链接,元素可以按照任意顺序存储在内存中,插入和删除操作只需要改变指针的指向,因此时间复杂度较低。 线性表常见的操作包括插入、删除、查找、获取长度等。其中插入和删除操作需要注意保持线性表的顺序关系。 常见的线性表数组、链表、栈和队列。数组是最简单的线性表,通过下标可以直接访问元素;链表是动态存储结构,插入和删除操作方便,但访问元素需要遍历链表;栈是一种特殊的线性表,只允许在表的一端进行插入和删除操作;队列也是一种特殊的线性表,只允许在表的一端进行插入操作,在另一端进行删除操作。这些数据结构在实际应用中都有各自的应用场景和优缺点。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值