数组

数组中出现次数超过一半的数字

题目描述:

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

解题思路1:用一个哈希表记录每个元素的次数,比较是否超过一半。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int n = numbers.size();
        for(int i = 0; i < n; i++){
            if(mp.find(numbers[i]) == mp.end())
                mp[numbers[i]] = 1;
            else
                mp[numbers[i]] += 1;
        }
         
        for(int i = 0; i < n; i++){
            if(mp.find(numbers[i]) == mp.end()){
                continue;
            }else{
                if(mp[numbers[i]] > n / 2)
                    return numbers[i];
                else
                    mp.erase(numbers[i]);
            }
        }
         
        return 0;
    }
    unordered_map<int,int> mp;
};

解法2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int n = numbers.size();
        if(n == 0) return 0;
        
        int res = numbers[0], count = 1;
        
        for(int i = 1; i < n; i++){
            if(count == 0){
                res = numbers[i];
                count = 1;
            }else if(res == numbers[i]){
                count += 1;
            }else{
                count -= 1;
            }
        }
        
        count = 0;
        for(int i = 0; i < n; i++){
            if(res == numbers[i])
                count++;
        }
        if(count > n / 2)
            return res;
        return 0;
    }
};

最小的K个数

题目描述:

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

解法1,排序暴力解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> res;
        if(k < 0 || k > input.size())
            return res;
        
        sort(input.begin(),input.end());
        for(int i = 0; i < k; i++)
            res.push_back(input[i]);
        
        return res;
    }
};

解法2:

上述的解法1的算法时间复杂度是O(N^2),还有一种基于对排序的O(Nlogk)算法。

我们县创建一个大小为k的数据容器来存储k个最小的数字,接下来每次从输入中读取一个一个整数。如果容器中数字少于k个则直接插入;如果容器已满,则比较这个整数与容器中的最大值,确定替换或者丢弃。

因此在容器满了之后需要做三件事: 1 在k个整数中找最大输 2 有可能需要删除最大数 3 有可能需要新插入一个数

如果用一个二叉树来实现这个容器,则需要O(logk)时间来实现。总时间为O(nlogk).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> result;
        int length = input.size();
        bool change = true;
        if(length <= 0 || k <= 0 || k > length){
            return result;
        }
        
        for(int i = 0; i < input.size(); i++){
            if(result.size() < k){
                result.push_back(input[i]);
            }
            else{
                if(change == true){
                    for(int j = k / 2; j >= 0; j--){
                        HeadAdjust(result, j, k);
                    }
                    for(int j = k - 1; j > 0; j--){
                        swap(result[0], result[j]);
                        HeadAdjust(result, 0, j);
                    }
                    change = false;
                }
                if(result[k-1] > input[i]){
                    result[k-1] = input[i];
                    change = true;
                }
            }
        }
        return result;
    }
private:
    void HeadAdjust(vector<int> &input, int parent, int length){
        int temp = input[parent];
        int child = 2 * parent + 1;
        while(child < length){
            if(child + 1 < length && input[child] < input[child+1]){
                child++;
            }
            if(temp >= input[child]){
                break;
            }
            input[parent] = input[child];
            
            parent = child;
            child = 2 * parent + 1;
        }
        input[parent] = temp;
    }
};

连续字数组的和

题目描述:

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int n = array.size();
        
        //初始化
        int maxsum = array[0];
        int cursum = array[0];
        
        for(int  i = 1; i < n; i++){
            if(cursum <= 0)
                cursum = array[i];
            else
                cursum += array[i];
            
            if(cursum > maxsum)
                maxsum = cursum;
        }
        
        return maxsum;
    }
};

把数组拍成最小的数

题目描述:

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

解题思路:

定义一种比较方法cmp,满足: 1 2 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        int n = numbers.size();
        if(n == 0)
            return "";
        
        sort(numbers.begin(),numbers.end(),cmp);
        string res;
        for(int i = 0; i < n; i++){
            res += to_string(numbers[i]);
        }
        return res;
    }
    
    static bool cmp(int a, int b){
        string A = to_string(a)+to_string(b);
        string B = to_string(b)+to_string(a);
        return A < B;
    }
};

其中,cmp必须定义为静态成员函数或者是全局变量。因为,std::sort是全局函数,无法在其中调用非静态成员函数。静态成员函数或者全局函数是不依赖于具体对象的可以独立访问的。

当然,也可以用lambda表达式来代替cmp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        int n = numbers.size();
        if(n == 0)
            return "";
        
        sort(numbers.begin(),numbers.end(),[](int a,int b){return to_string(a)+to_string(b) < to_string(b)+to_string(a);});
        string res;
        for(int i = 0; i < n; i++){
            res += to_string(numbers[i]);
        }
        return res;
    }
};

数组中的逆序对

题目描述:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

	对于%50的数据,size<=10^4

	对于%75的数据,size<=10^5

	对于%100的数据,size<=2*10^5

主要运用归并排序的思想。

先把数组分隔成子数组,统计出子数组内部逆序对的数目,然后再统计出两个相邻子数组之间的逆序对数目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Solution {
public:
    int InversePairs(vector<int> data) {
        int n = data.size();
        if(n == 0) return 0;

        //排序的辅助数组
        vector<int> copy;
        for(int i = 0; i < n; i++){
            copy.push_back(data[i]);
        }
        return inverseHelper(data,copy,0,n-1)%1000000007;
    }

    long inverseHelper(vector<int> & data, vector<int> &copy,int begin,int end){
        //若指向同一位置,则没有逆序对
        if(begin == end){
            copy[begin] = data[end];
            return 0;
        }

        int mid = (begin + end) / 2;
        //是左半边有序,并返回左半段逆序对数目
        long left = inverseHelper(copy,data,begin,mid);
        long right = inverseHelper(copy,data,mid+1,end);

        int i = mid;//左半段最后一个数字的下标
        int j = end;
        int indexcopy = end;//辅助复制数组最后一个数字下标
        long count = 0;

        while(i >= begin && j >= mid+1){
            if(data[i] > data[j]){
                copy[indexcopy--] = data[i--];
                count += j-mid;
            }else{
                copy[indexcopy--] = data[j--];
            }
        }
        for(;i >= begin;--i){
            copy[indexcopy--] = data[i];
        }
        for(;j >= mid+1; --j){
            copy[indexcopy--] = data[j];
        }

        return left + right + count;
    }
};

数字在排序数组中出现的次数

题目描述:

统计一个数字在排序数组中出现的次数,比如排序数组为{1,2,3,3,3,4,5},那么数字3出现的次数就是3。

最简单的遍历数组计算个数,时间复杂度为O(n)

如果用二分法找到一个3,再向数组两端遍历。看似很好,但如果刚好有n个3,所以本质上的复杂度仍然是O(n)

其实还可以采用二分法分别找到第一个和最后一个3的位置,即可求出个数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        int n = data.size();
        if(n <= 0)
            return 0;
        
        int first = findFirst(data,k,0,n-1);
        int last = findLast(data,k,0,n-1);
        
        if(first != -1 && last != -1)
            return last-first+1;
        
        return 0;
    }
    
    int findFirst(vector<int> &data,int k, int start,int end){
        if(start > end)
            return -1;
        
        int mid = start + (end-start)/2;
        if(data[mid]==k){
            if((mid > 0 && data[mid-1] != k)||mid == 0)
                return mid;
            else
                end = mid-1;
        }else if(data[mid] > k)
            end = mid-1;
        else
            start = mid + 1;
        
        return findFirst(data,k,start,end);
    }
    
    int findLast(vector<int> &data,int k, int start,int end){
        int mid = start + (end-start)/2;
        while(start <= end){
        if(data[mid]==k){
            if((mid < data.size()-1 && data[mid+1] != k)||mid == data.size()-1)
                return mid;
            else
                start = mid+1;
        }else if(data[mid] > k)
            end = mid-1;
        else
            start = mid + 1;
        
        mid = mid = start + (end-start)/2;
        }
        return -1;
    }
};