# B-排序(1)- O( N logN )排序算法

B-排序(1)- O( N logN )排序算法

题目
  1. 数组中的逆序对
  2. 最小的k个数
  3. 快排代码
  4. 归并排序代码

!!! 排序算法解题思路 !!!

看到排序问题,首先看题目的目的。从三种排序算法中做选择。(堆排序、归并排序、快速排序)

  1. 三种排序都是基于比较的排序方法,时间复杂度都是 O(N*logN) 但是各有各的特点
  2. 题目要求, 要求排序算法稳定。就选归并排序,merge过程中,先拷贝左侧区域的值就可以稳定
  3. 题目要求, 要求额外空间复杂度 O(1) 。就选 堆排序
  4. 题目要求, 不追求稳定性和额外空间复杂度 ,追求综合指标-就选快速排序
  5. 归并排序指标:O(N*logN) O(N) 稳定 (merge的过程需要辅助数组)
  6. 堆排序指标:O(N*logN) O(1) 不稳定
  7. 快速排序指标: O(N*logN) O(logN) 不稳定

1 归并排序

  1. 常规 归并排序问题
  2. 扩展 小和问题
  3. 扩展 逆序对问题
题目1 归并排序
#include <iostream>
#include <vector>
#include <string>
using namespace std;

struct Student
{
    string _name;
    int _grade;
    void print()
    {
        cout << _name << " " << _grade << endl;
    }
};

class MergeSort{
    
    public:  
    // 归并排序-函数
    void mergeSort( vector<Student>& stv, bool order )
    {
        if (stv.size() < 2 || stv.empty())
            return;
        process(stv, 0, stv.size() - 1, order);
    }

    // 归并排序递归辅助函数
    void process(vector<Student>& stv, int l, int r, bool order)
    {
        // 递归的出口
        if (r == l)
            return;
        // 求左右的中点
        int m = l + ((r - l) >> 1);
        // 左侧区域有序
        process(stv, l, m, order);
        // 右侧区域有序
        process(stv, m + 1, r, order);
        // 合并左右区域
        merge(stv, l, r, m, order);
    }

    // merge 函数
    void merge(vector<Student> &s1, int l, int r, int m, bool order)
    {
        // 如果需要合并的下标相等,return
        if (l == r)
            return;
        // 定义左右部分指针的起点
        int p1 = l;
        int p2 = m + 1;
        vector<Student> help;
        // 当左右区域的下标都没有越界的情况下,升序放置
        while (p1 <= m && p2 <= r && order == true)
        {
            help.push_back(s1[p1]._grade <= s1[p2]._grade ? s1[p1++] : s1[p2++]);
        }
        // 当左右区域的下标都没有越界的情况下,降序放置
        while (p1 <= m && p2 <= r && order == false)
        {
            help.push_back(s1[p1]._grade >= s1[p2]._grade ? s1[p1++] : s1[p2++]);
        }
        // 当左侧访问越界,把右侧容器的元素拷贝到help
        while (p1 <= m)
        {
            help.push_back(s1[p1++]);
        }
        // 当右侧访问越界,把左侧容器的元素拷贝到help
        while (p2 <= r)
        {
            help.push_back(s1[p2++]);
        }
        // 把辅助空间的元素拷贝到 原容器对应的位置上,merge完成
        for (int i = 0; i < help.size(); i++)
        {
            s1[l + i] = help[i];
        }
    }
};
题目2 数组中的逆序对
class Solution {
public:
    int reversePairs(vector<int>& nums) {
        if (nums.empty())
        {
            return 0;
        }
        return process(nums , 0 , nums.size()-1);
    }

    int  process( vector<int>& nums , int L ,int R  )
    {
        // base case
        if ( L == R )
        {
            return 0 ;
        }
        int mid = L + ( (R-L)>>1);

        return process( nums , L, mid ) + 
                      process( nums, mid+1, R ) + 
                      merge(nums , L , R , mid );  
    }
    int  merge( vector<int>&  nums,  int l , int r, int m)
    {
        int res = 0;
        vector<int> merged;
        int p1 = l ;
        int p2 = m+1;
        while ( p1 <= m && p2 <= r )
        {
            if (nums[p2]  < nums[p1])
            {
                merged.push_back(nums[p2++]);
                res  += m- p1+1;
            }else{
                merged.push_back(nums[p1++]);
            }
        }
        while ( p1<=m )
        {
            merged.push_back(nums[p1++]);
        }
        while (p2 <= r)
        {
            merged.push_back(nums[p2++]);
        }
        for ( int i = 0 ; i < merged.size();i++)
        {
            nums[ l + i ] = merged[i];
        }
        return res;
    }
};

2 堆排序

  1. 堆的概念和实现
  2. HeapInsert() 和 Heapify()
  3. 堆排序- 用已给的数据建立大根堆或小根堆,popMax(); heapify();

最小的k个数

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        if( arr.empty() || arr.size() <= k )
        {
            return arr;
        }
        vector<int> ans;
        priority_queue<int> heap;
        for ( auto i : arr)
        {
            heap.push(i);
            if (heap.size() > k)
            {
                heap.pop();
            }
        }
        for(int i = 0 ; i < k ; ++i)
        {
            int m = heap.top();
            heap.pop();
            ans.push_back(m);
        }
        return ans;
    }
};
例题:

215. 数组中的第K个最大元素
这道题,我用了快排,但是效果不理想

3 快速排序

  1. 荷兰国旗问题(Partition to 3 )
  2. 随机快排算法
#include <iostream>
#include <vector>
#include <ctime>
#define random(x)  rand()%(x)

using namespace std;

vector<int> partition(vector<int> &nums, int L, int R) ;
void swap(vector<int> &nums, int a, int b);
void process(vector<int> &nums, int L, int R);
void quickSort(vector<int> &nums);

void quickSort(vector<int> &nums)
{
    if (nums.size() == 0)
        return;
    if (nums.size() == 1)
        return;
    process(nums, 0, nums.size() - 1);
}

void process(vector<int> &nums, int L, int R)
{
    if (L < R)
    {
        srand((int) time(0));
        int target = ( random(R - L + 1) ) + L;
        swap(nums , target , R );
        vector<int> p = partition( nums, L, R );
        process(nums, L, p[0] - 1);
        process(nums, p[1] + 1, R);
    }
}

vector<int> partition(vector<int> &nums, int L, int R )
{
    int l = L - 1;
    int r = R + 1;
    int cur = L;
    while (cur < r)
    {
        if (nums[cur] < nums[R])
        {
            swap(nums, ++l, cur++);
        }
        else if (nums[cur] > nums[R])
        {
            swap(nums, --r, cur);
        }
        else if (nums[cur] == nums[R])
        {
            cur++;
        }
    }
    vector<int> index;
    index.push_back(l + 1);
    index.push_back(r - 1);
    return index;
}

void swap(vector<int> &nums, int a, int b)
{
    int tmp = nums[a];
    nums[a] = nums[b];
    nums[b] = tmp;
}

void print ( const vector<int>& nums )
{
    for( int i = 0; i < nums.size(); i++)
    {
        cout << nums[i] << " " ;
    }
    cout << "End" << endl;
}

int main()
{
    vector<int> test;
    test.clear();
    test.push_back(3);
    test.push_back(3);
    test.push_back(6);
    test.push_back(2);
    test.push_back(8);
    test.push_back(1);
    test.push_back(7);
    test.push_back(5);
    print(test);
    quickSort(test);
    print(test);
    return 0;
}

题目 1 成绩排序

  • 输入任意(用户,成绩)序列,可以获得成绩从高到低或从低到高的排列
  • 相同成绩都按先录入排列在前的规则处理。
  • 输入多行,先输入要排序的人的个数,然后输入排序方法0(降序)或者1(升序)再分别输入他们的名字和成绩,以一个空格隔开
  • 按照指定方式输出名字和成绩,名字和成绩之间以一个空格隔开
// 题目:输入任意(用户,成绩)序列,可以获得成绩从高到低或从低到高的排列,相同成绩都按先录入排列在前的规则处理。
// 输入多行,先输入要排序的人的个数,然后输入排序方法0(降序)或者1(升序)再分别输入他们的名字和成绩,以一个空格隔开
// 按照指定方式输出名字和成绩,名字和成绩之间以一个空格隔开

#include <iostream>
#include <vector>
#include <string>
using namespace std;

struct Student
{
    string _name;
    int _grade;
    void print()
    {
        cout << _name << " " << _grade << endl;
    }
};

void mergeSort(vector<Student> &stv, bool order);
void process(vector<Student> &stv, int l, int r, bool order);
void merge(vector<Student> &s1, int l, int r, int m, bool order);

// 归并排序-函数
void mergeSort(vector<Student> &stv, bool order)
{
    if (stv.size() < 2 || stv.empty())
        return;
    process(stv, 0, stv.size() - 1, order);
}
// 归并排序递归辅助函数
void process(vector<Student> &stv, int l, int r, bool order)
{
    // 递归的出口
    if (r == l)
        return;
    // 求左右的中点
    int m = l + ((r - l) >> 1);
    // 左侧区域有序
    process(stv, l, m, order);
    // 右侧区域有序
    process(stv, m + 1, r, order);
    // 合并左右区域
    merge(stv, l, r, m, order);
}

// 归并两个vector
void merge(vector<Student> &s1, int l, int r, int m, bool order)
{
    // 如果需要合并的下标相等,return
    if (l == r)
        return;
    // 定义左右部分指针的起点
    int p1 = l;
    int p2 = m + 1;
    vector<Student> help;
    // 当左右区域的下标都没有越界的情况下,升序放置
    while (p1 <= m && p2 <= r && order == true)
    {
        help.push_back(s1[p1]._grade <= s1[p2]._grade ? s1[p1++] : s1[p2++]);
    }
    // 当左右区域的下标都没有越界的情况下,降序放置
    while (p1 <= m && p2 <= r && order == false)
    {
        help.push_back(s1[p1]._grade >= s1[p2]._grade ? s1[p1++] : s1[p2++]);
    }
    // 当左侧访问越界,把右侧容器的元素拷贝到help
    while (p1 <= m)
    {
        help.push_back(s1[p1++]);
    }
    // 当右侧访问越界,把左侧容器的元素拷贝到help
    while (p2 <= r)
    {
        help.push_back(s1[p2++]);
    }
    // 把辅助空间的元素拷贝到 原容器对应的位置上,merge完成
    for (int i = 0; i < help.size(); i++)
    {
        s1[l + i] = help[i];
    }
}

int main()
{
    vector<Student> students;
    int num;
    bool isuporder;
    Student student;
    // NowCode 的 oj 需要输入多组测试用例,配合一下,循环输入...
    while (cin >> num )
    {
        cin >> isuporder;
        if (num <= 0)
            return -1;
        int i = 0;
        // 输入待排序的数组
        // cout << "Input the data : " << endl;
        while ( i++ < num && cin >> student._name )
        {
            cin >> student._grade ; 
            students.push_back(student);
        }
        // cout << "Input over " << endl;
        // 调用函数排序
        mergeSort(students, isuporder);

        // 打印排序好的数组 
        for (int j = 0; j < students.size(); j++)
        {
            students[j].print();
        }
        // 同样由于需要测试多组示例,每次结束后清空容器,否则出错...
        students.clear();
    }
    return 0;
}
posted @ 2020-03-04 09:28  longlongban  阅读(434)  评论(0编辑  收藏  举报