牛客网_Leetcode经典编程题——数组

线性表——数组

1、remove-duplicates-from-sorted-array

Given a sorted array, remove the duplicates in place such that each element appear only once 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 input array A =[1,1,2],
Your function should return length =2, and A is now[1,2].

在有序数组中去重,可采用类似快慢指针的方式,具体见下图:
这里写图片描述

class Solution {
public:
    int removeDuplicates(int A[], int n) {
        if (n == 0) return 0; //空数组跳过
        int i = 0, j = 0; //维护快慢两个指针
        while (++j < n) { //快指针往后逐一遍历
            if (A[i] != A[j]) { //如若相等则跳过
                A[++i] = A[j]; //不相等则更新在i指针的后面
            }
        }
        return ++i; //i即为结果数组最大下标,加一即为实际数量
    }
};

2、remove-duplicates-from-sorted-array-ii

Follow up for “Remove Duplicates”:
What if duplicates are allowed at most twice?
For example,
Given sorted array A =[1,1,1,2,2,3],
Your function should return length =5, and A is now[1,1,2,2,3].

在上题的情况下,允许有两个重复的数存在。
只需维护一个cnt来记录这个数还可以出现几次。

class Solution {
public:
    int removeDuplicates(int A[], int n) {
        if (n <= 2) return n; //当数组元素个数小于两个时无需进行处理
        int i = 0, j = 0;
        int cnt = 1; 
        while (++j < n) {
            if (A[i] != A[j]) { //但不相等时为第一次放入,cnt置为1表示还可以放一个
                A[++i] = A[j];
                cnt = 1;
            } else if (cnt != 0) { //相等时如果还可以放置
                A[++i] = A[j];
                cnt--;
            }
        }
        return ++i;
    }
};

3、search-in-rotated-sorted-array

Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e.,0 1 2 4 5 6 7might become4 5 6 7 0 1 2).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.

在一个循环有序数组中查找一个数,可使用二分法来实现。
我们按照传统二分法确定左右和中界,可以发现,如果mid小于high,那么数组的右半部分就是有序的,否则左半部分为有序的。例如4 5 6 7 8 0 1 2,mid为7,high为2,7>2,因此左半部分为有序的,那么就可以把target跟左半部分的边界比较来判断target是不是在这一部分里面,如果在的话就下一次查找这一边,不在的话就查找另一边。

class Solution {
public:
    int search(int A[], int n, int target) {
        if (n == 0) return -1;
        int low = 0, high = n-1; //左右边界
        while (low <= high) {
            int mid = low + ((high - low) >> 1); //中界
            if (A[mid] == target) return mid; //命中
            if (A[mid] < A[high]) { //右半部分为有序
                if (A[mid] < target && A[high] >= target) low = mid + 1; //判断target是否在右半部分中
                else high = mid - 1; //否则就是在左半部分
            } else { //左半部分为有序
                if (A[mid] > target && A[low] <= target) high = mid - 1; //判断target是否在左半部分中
                else low = mid + 1;
            }
        }
        return -1;
    }
};

4、search-in-rotated-sorted-array-class

Follow up for “Search in Rotated Sorted Array”:
What if duplicates are allowed?
Would this affect the run-time complexity? How and why?
Write a function to determine if a given target is in the array.

在上题的基础上允许重复,这会导致我们之前说的确定某一半有序不成立,例如在1 2 1 1 1 1查找2,mid等于high,如果使用上一题的解法就会错误的判断左半部分有序,那么它用target 2去跟左半部分边界【1,1】比较的话就发现并不在这里面,于是它就去找另外一边了,这就导致了错误。我们可以多增加判断一种相等的情况,如果相等了,就直接将high前移一位,也就是将最后面舍弃掉,因为它肯定不会是跟target相等的。

class Solution {
public:
    bool search(int A[], int n, int target) {
        if (n == 0) return false;
        int low = 0, high = n - 1;
        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            if (A[mid] == target) return true;
            if (A[mid] < A[high]) { //右半部分有序
                if (A[mid] < target && A[high] >= target) low = mid + 1;
                else high = mid - 1;
            } else if (A[mid] > A[high]) { //左半部分有序
                if (A[mid] > target && A[low] <= target) high = mid - 1;
                else low = mid + 1;
            } else { //相等,舍弃最后一位
                --high;
            }
        }
        return false;
    }
};

5、median-of-two-sorted-arrays

There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

有两个有序数组,找出这两个数组中所有数字的中位数。
更普遍的形式是,找出第k大的数。这里要充分利用有序这个条件,采用二分法的思想,我们假设m是小于n的,那么把k分为两半,然后比较A[k/2 或 m 较小者]和B[k/2]的大小。如果两者相等,那么第k个数一定是这两个,例如A:1 2 3 4, B:1 2 3 4 5,找第6大的数时,3和3相等,那么结果必定为3;如果A[k/2] < B[k/2],例如A:1 2 3 4,B:2 3 4 5 6,找第6大的数时,3<4,那么A[k/2]之前的数必定不是结果,同时它们也必定是小于结果的,因此可以将它们删除,继续在A[> k/2]和B里面重复这个流程,此时的k就变成了k = k -( k/2 或 m 较小者)。如此递归之后当m等于0或k等于1时即可得出结果。

class Solution {
public:
    double findMedianSortedArrays(int A[], int m, int B[], int n) {
        int total = m + n;
        if (total & 0x1) return findKth(A, m, B, n, (total / 2) + 1); //如果总数为奇数,找最中间的数
        //总数为偶数,中位数为中间两个元素的平均
        else return (findKth(A, m, B, n, total / 2) + findKth(A, m, B, n, (total / 2) + 1)) / 2.0;
    }
private:
    static int findKth(int A[], int m, int B[], int n, int k) {
        if (m > n) return findKth(B, n, A, m, k); //我们假设是m<n的,这里交换一下位置
        if (m == 0) return B[k - 1]; //A没有元素了,直接返回B里的k大
        if (k == 1) return min(A[0], B[0]); //找第一大

        int i = min(m, k >> 1), j = k - i; //i为m或k/2较小者,j为k个数除去前者剩下的数
        if (A[i-1] == B[j-1]) return A[i - 1]; //两者相等,结果必为它们之一
        if (A[i-1] > B[j-1]) return findKth(A, m, B+j, n-j, k-j); //递归找下去
        else return findKth(A+i, m-i, B, n, k-i);
    }
};

6、longest-consecutive-sequence

Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
For example,
Given[100, 4, 200, 1, 3, 2],
The longest consecutive elements sequence is[1, 2, 3, 4]. Return its length:4.
Your algorithm should run in O(n) complexity.

用一个哈希表存储所有出现过的元素,对每个元素,以该元素为中心,往左右扩张,直到不连续为止,记录下最长的长度。

class Solution {
public:
    int longestConsecutive(vector<int> &num) {
        unordered_map<int, bool> used;
        for (auto i : num) used[i] = false; //初始化
        int longest = 0;
        for (auto i : num) {
            if (used[i]) continue;
            used[i] = true;
            int length = 1;
            for (int j = i + 1; used.find(j) != used.end(); ++j) { //往右扩张
                used[j] = true;
                ++length;
            }
            for (int j = i - 1; used.find(j) != used.end(); --j) { //往左扩张
                used[j] = true;
                ++length;
            }
            longest = max(length, longest);
        }
        return longest;
    }
};

7、two-sum

Givarray of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target,
where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

给出一组数和一个target,找出在这组数中两个数,使得它们的和等于target。
使用哈希表,存储数和它们的下标,根据target和一个数求出另一个数,并判断这个数是否在表中,复杂度O(n)

class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        unordered_map<int, int> map; // 数和下标对应关系
        vector<int> res; //保存结果
        for (int i = 0; i < numbers.size(); i++) { //初始化
            map[numbers[i]] = i;
        }
        for (int i = 0; i < numbers.size(); i++) { //对于表中每个数
            const int gap = target - numbers[i]; //求出另一半
            if (map.find(gap) != map.end() && map[gap] > i) { //判断是否存在该数,并判断下标大小关系
                res.push_back(i + 1);
                res.push_back(map[gap] + 1);
                break;
            }
        }
        return res;
    }
};

8、3Sum

Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.

For example, given array S = {-1 0 1 2 -1 -4},

A solution set is:
(-1, 0, 1)
(-1, -1, 2)

先排序,然后遍历数组,在其后用两个指针左右夹逼,复杂度 O(n^2)

class Solution {
public:
    vector<vector<int> > threeSum(vector<int> &num) {
        vector<vector<int> > res;
        if (num.size() < 3) return res;
        sort(num.begin(), num.end()); //排序
        const int target = 0;
        auto last = num.end(); //尾后指针
        for (auto i = num.begin(); i < last - 2; ++i) { //先找一个数
            if (i > num.begin() && *i == *(i-1)) continue; //跳过重复的数
            auto j = i + 1, k = last - 1; //后面左右夹逼的两个指针
            while (j < k) {
                if (*i + *j + *k < target) { //j小了
                    ++j;
                    while(*j == *(j - 1) && j < k) ++j; //跳过重复
                } else if (*i + *j + *k > target) { //k大了
                    --k;
                    while(*k == *(k + 1) && j < k) --k; //跳过重复
                } else { //得到一个有效解
                    res.push_back({ *i, *j, *k });
                    ++j;
                    --k;
                    while(*j == *(j - 1) && j < k) ++j; //跳过重复
                    while(*k == *(k + 1) && j < k) --k; //跳过重复
                }
            }
        }
        return res;
    }
};

9、3Sum Closest

Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
For example, given array S = {-1 2 1 -4}, and target = 1.
+
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

思路同上一题基本一致,先排序,然后左右夹逼。

class Solution {
public:
    int threeSumClosest(vector<int> &num, int target) {
        int result = 0;
        int min_gap = INT_MAX;
        sort(num.begin(), num.end()); //排序
        auto last = num.end() - 2;
        for (auto i = num.begin(); i < last; i++) {
            auto j = i + 1, k = num.end() - 1;
            while (j < k) { //左右夹逼
                const int sum = *i + *j + *k;
                const int gap = abs(sum - target);
                if (gap < min_gap) {
                    min_gap = gap;
                    result = sum;
                }
                if (sum > target) --k;
                else ++j;
            }
        }
        return result;
    }
};

10、4Sum

Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note:
Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
The solution set must not contain duplicate quadruplets.
example, given array S = {1 0 -1 0 -2 2}, and target = 0.
A solution set is:

(-1, 0, 0, 1)
(-2, -1, 1, 2)
(-2, 0, 0, 2)

分析
先排序,然后左右夹逼,复杂度 O(n^3)。
也可以用一个hashmap先缓存两个数的和,最终复杂度O(n^2)。这个策略也适用于 3Sum 。

// 4Sum
// 先排序,然后左右夹逼
// Time Complexity: O(n^3),Space Complexity: O(1)
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        if (nums.size() < 4) return result;
        sort(nums.begin(), nums.end());

        for (int i = 0; i < nums.size() - 3; ++i) {
            if (i > 0 && nums[i] == nums[i-1]) continue;
            for (int j = i + 1; j < nums.size() - 2; ++j) {
                if (j > i+1 && nums[j] == nums[j-1]) continue;
                int k = j + 1;
                int l = nums.size() - 1;
                while (k < l) {
                    const int sum = nums[i] + nums[j] + nums[k] + nums[l];
                    if (sum < target) {
                        ++k;
                        while(nums[k] == nums[k-1] && k < l) ++k;
                    } else if (sum > target) {
                        --l;
                        while(nums[l] == nums[l+1] && k < l) --l;
                    } else {
                        result.push_back({nums[i], nums[j], nums[k], nums[l]});
                        ++k;
                        --l;
                        while(nums[k] == nums[k-1] && k < l) ++k;
                        while(nums[l] == nums[l+1] && k < l) --l;
                    }
                }
            }
        }
        return result;
    }
};
// 4Sum
// 用一个hashmap先缓存两个数的和
// Time Complexity: 平均O(n^2),最坏O(n^4),Space Complexity: O(n^2)
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        if (nums.size() < 4) return result;
        sort(nums.begin(), nums.end());

        unordered_map<int, vector<pair<int, int> > > cache;
        for (auto a = 0; a < nums.size(); a++) { //用哈希表缓存两数之和及它们的下标
            for (auto b = a + 1; b < nums.size(); b++) {
                cache[nums[a] + nums[b]].push_back(pair<int, int>(a, b));
            }
        }
        for (auto c = 0; c < nums.size(); c++) { //遍历剩下的两数
            for (auto d = c + 1; d < nums.size(); d++) {
                const int gap = target - nums[c] - nums[d];
                if (cache.find(gap) == cache.end()) continue;

                const auto& vec = cache[gap];
                for (auto k = 0; k < vec.size(); k++) {
                    if (c <= vec[k].second) continue; //跳过重叠
                    result.push_back( { nums[vec[k].first], nums[vec[k].second], nums[c], nums[d] });
                }
            }
        }
        sort(result.begin(), result.end());
        result.erase(unique(result.begin(), result.end()), result.end());
        return result;
    }
};

11、remove-element

Given an array and a value, remove all instances of that value in place and return the new length.
The order of elements can be changed. It doesn’t matter what you leave beyond the new length.

// 输入:[1,2,3,4],1
// 输出:[4,2,3],3

class Solution {
public:    
    int removeElement(int A[], int n, int elem) {        
        for(int i=0; i<n; ++i) {            
            if(A[i] == elem) {                
                while(n>i && A[--n] == elem);                
                A[i] = A[n];            
            }        
        }        
        return n;    
    }
};

12、next-permutation

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
The replacement must be in-place, do not allocate extra memory.
Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,3→1,3,2
3,2,1→1,2,3
1,1,5→1,5,1

题意为求字典序的下一个排列,如果是最后一个了就返回第一个排列。可以利用从右到左的升序来做。

class Solution {
public:
    void nextPermutation(vector<int> &num) {
        if (num.size() <= 1) return;
        int i = num.size() - 1;
        while ((i > 0) && (num[i] <= num[i-1]) ) --i; //找出从右到左第一个非升序的数
        if (i == 0) {
            reverse(num.begin(), num.end());
        } else { //找出从右到左第一个比那个非升序大的数并交换
            int j = num.size() - 1;
            while (num[j] <= num[i-1]) --j;
            swap(num[j], num[i-1]);
            reverse(num.begin() + i, num.end());
        }
    }
};

猜你喜欢

转载自blog.csdn.net/silence1772/article/details/81272259