zoukankan      html  css  js  c++  java
  • # 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;
    }
    
    干啥啥不行,吃饭第一名
  • 相关阅读:
    Integer to Roman leetcode java
    Reverse Integer leetcode java
    Binary Tree Maximum Path Sum leetcode java
    公司来了一个奇葩需求pppoe client+server+EOIP+vlan
    魔兽数据库-自然
    windows默认dns解析走ipv4而不走ipv6
    ROS支持BCP桥接(基于PPP隧道)
    几款比较好用的电动理发器推荐
    centos 拨号pptp在拨号成功和拨号失败的时候脚本处理!!!非常重要
    ros routeros 脚本命令script
  • 原文地址:https://www.cnblogs.com/jiangxinyu1/p/12407662.html
Copyright © 2011-2022 走看看