【2.排序算法——编程题】

题目一般从leedcode摘抄来,从简单到复杂

目录

1.简单题目

1.1有效的字母异位词

1.2两个数组的交集

1.3按奇偶排序数组Ⅱ

2.中等题目

2.1三数之和&四数之和


1.简单题目

1.1有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true
示例 2:

输入: s = "rat", t = "car"
输出: false

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-anagram
 

方法很多,很容易想到排序之后再比较,比如使用C++ 的STL,或者自己写排序算法。

方法1:排序之后比较

bool isAnagram(string s, string t) {
    //先进行排序,后用compare方法比较
    sort(s.begin(), s.end());
    sort(t.begin(), t.end());

    //0=相同,-1=不同
    int val = s.compare(t);

    return !val;
}

方法2:考虑每个字符对应特定的ASCII码的方式,新开一个26元素大小的数组,遍历两个字符串,每个字符与‘a’做差即为0-26的数字,采用计数的方式比较是否异位。提交结果显示明显比方法1执行用时少。

bool isAnagram(string s, string t){
    if(s.length() != t.length()){
        return false;
    }

    int num[26] = {0};
    for(int i = 0; i < s.length(); i++){
        num[s[i] - 'a']++;
        num[t[i] - 'a']--;
    }
    for (int i : num) {
        if(i)
            return false;
    }
    return true;
}

1.2两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2]
示例 2:

输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [9,4]

说明:

  • 输出结果中的每个元素一定是唯一的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/intersection-of-two-arrays
 

很容易想到先排序,再遍历的方式,要重点关心一下去重操作。(特别注意下标越界的情况,leedcode对这个很敏感)

方法1:

vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
    sort(nums1.begin(), nums1.end());
    sort(nums2.begin(), nums2.end());
    vector<int> res = {};

    int i = 0, j = 0;
    while(i < nums1.size() && j < nums2.size()){
        if(nums1[i] < nums2[j])
            i++;
        else if(nums1[i] > nums2[j])
            j++;
        else if(nums1[i] == nums2[j]){
            res.push_back(nums1[i]);
            i++;
            j++;
        }
        //去重
        while(i > 0 && i < nums1.size()){ //避免越界
            if(nums1[i] == nums1[i - 1])  //如果跟上一个元素相同,下标++
                i++;
            else
                break;
        }
    }
    return res;
}

方法2:使用C++中的set容器,能达到自动去重的效果。

vector<int> intersection(vector<int>& nums1, vector<int>& nums2){
    vector<int> res;
    set<int> s;
    for (int i : nums1) {
        //找到时返回下标,找不到返回迭代器结尾
        if (find(nums2.begin(), nums2.end(), i) != nums2.end())
        {
            s.insert(i);
        }
    }
    res.assign(s.begin(),s.end());
    return res;
}

1.3按奇偶排序数组Ⅱ

给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。

对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;当 A[i] 为偶数时, i 也是偶数。

你可以返回任何满足上述条件的数组作为答案。

示例:

输入:[4,2,5,7]
输出:[4,5,2,7]
解释:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-array-by-parity-ii
这道题的涉及排序、交换,解法肯定很多。我首先找到其中的规律,采用类似插入排序的一种算法

方法1

vector<int> sortArrayByParityII(vector<int>& A){
    //先使第0个数是偶数
    if(A[0] % 2 != 0){
        for(int i = 1; i < A.size(); i++){
            if(A[i] % 2 == 0)
                swap(A[0], A[i]);
        }
    }
    int count_0 = 1, count_1 = 0; //奇偶计数器
    //数组遍历
    for(int i = 1; i < A.size(); i++){
        int key = A[i];
        int j = i - 1;
        //当前为偶数
        if(key % 2 == 0){
            //前一个数是奇数,插入位置应该在偶数数量*2的前面
            if(A[i - 1] % 2 != 0){
                while(j >= count_0 * 2){
                    swap(A[j], A[j + 1]);
                    j--;
                }
            }
            count_0++;
        }
        //当前为奇数
        else{
            //前一个数是偶数,插入位置应该在奇数数量*2的后面
            if(A[i - 1] % 2 == 0){
                while(j > count_1 * 2){
                    swap(A[j], A[j + 1]);
                    j--;
                }
            }
            count_1++;
        }
    }
    return A;
}

方法2

两个指针遍历,将两个不符合规定的元素交换(有点快排的意思)

vector<int> sortArrayByParityII(vector<int>& A){
    int i = 0, j = 1;
    while(i < A.size() - 1 && j < A.size()){
        while(i < A.size() - 1){
            //遇到奇数就退出循环
            if(A[i] % 2 == 0)
                i += 2;
            else
                break;
        }
        while(j < A.size()){
            //遇到偶数就退出循环
            if(A[j] % 2 == 1)
                j += 2;
            else
                break;
        }
        //交换不符合顺序的
        if(i < A.size() - 1 && j < A.size())
            swap(A[i], A[j]);
        i += 2;
        j += 2;
    }
    return A;
}

2.中等题目

2.1三数之和&四数之和

三数之和:

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

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

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum

四数之和:

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:

答案中不可以包含重复的四元组。

示例:

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum
 

这一类题目刚开始想到的是回溯法,可解,但是会超时,所以采用先将数组排序,然后双指针法。

三数之和:

vector<vector<int>> threeSum(vector<int>& nums) {
    vector<vector<int>> res;
    if(nums.size() < 3)
        return res;
    sort(nums.begin(), nums.end());
    set<vector<int>> set1;
    for(int i = 0; i < nums.size() - 2; i++){
        if(i > 0){
            if(nums[i] == nums[i - 1])
                continue;
        }
        int left = i + 1, right = nums.size() - 1;
        while(left < right){
            int sum = nums[i] + nums[left] + nums[right];
            if(sum == 0){
                vector<int> temp = {nums[i], nums[left], nums[right]};
                set1.insert(temp);
                left++;
                right--;
            } else if(sum < 0){
                left++;
            } else if(sum > 0){
                right--;
            }
        }
    }
    res.assign(set1.begin(), set1.end());
    return res;
}

四数之和:(外层再加一层循环)

vector<vector<int>> fourSum(vector<int>& nums, int target) {
    vector<vector<int>> res;
    if(nums.size() < 4)
        return res;
    sort(nums.begin(), nums.end());
    set<vector<int>> set1;
    for(int i = 0; i < nums.size() - 3; i++){
        if(i > 0){
            if(nums[i] == nums[i - 1])
                continue;
        }
        for(int j = i + 1; j < nums.size() - 2; j++){
            int left = j + 1, right = nums.size() - 1;
            while(left < right){
                int sum = nums[i] + nums[j] + nums[left] + nums[right];
                if(sum == target){
                    vector<int> temp = {nums[i], nums[j], nums[left], nums[right]};
                    set1.insert(temp);
                    left++;
                    right--;
                } else if(sum < target){
                    left++;
                } else if(sum > target){
                    right--;
                }
            }
        }
    }
    res.assign(set1.begin(), set1.end());
    return res;
}

另外贴出超时的回溯解法:

void traceback(vector<int>& nums, int target, vector<vector<int>> &res, vector<int> &temp, int dep, int begin){
    if(dep == 4){
        if(target == 0){
            res.push_back(temp);
        }
        return;
    }

    for(int i = begin; i < nums.size() + (dep - 3); i++){
        if(i > begin && nums[i] == nums[i - 1])
            continue;
        temp.push_back(nums[i]);
        traceback(nums, target - nums[i], res, temp, dep + 1, i + 1);
        temp.pop_back();
    }
}

vector<vector<int>> fourSum(vector<int>& nums, int target) {
    vector<vector<int>> res;
    if(nums.size() < 4)
        return res;
    sort(nums.begin(), nums.end());
    vector<int> temp;
    traceback(nums, target, res, temp, 0, 0);
    return res;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值